From 215d01c1c14257c2071c2ad8cab8c5982f4b3c8c Mon Sep 17 00:00:00 2001 From: maxim Date: Wed, 13 Aug 2025 16:28:43 +0200 Subject: [PATCH] HostDMA/EMIFA basic driver ipc driver basic test --- .devcontainer/devcontainer.json | 1 - .dockerignore | 25 + .gitignore | 2 + cpu/Makefile | 5 +- cpu/cpu.lds | 5 + cpu/src/api/freetribe.c | 18 + cpu/src/api/freetribe.h | 1 + cpu/src/apps/system23/lookup_tables.h | 356 +++++++++++++ cpu/src/apps/system23/lut.c | 19 + cpu/src/apps/system23/lut.h | 10 + cpu/src/apps/system23/param_scale.h | 172 +++++++ cpu/src/apps/system23/sequencer.h | 8 + cpu/src/apps/system23/system23.c | 253 ++++++++++ cpu/src/apps/system23/utils/tinyprintf.c | 521 ++++++++++++++++++++ cpu/src/apps/system23/utils/tinyprintf.h | 186 +++++++ cpu/src/kernel/device/dev_board.c | 49 +- cpu/src/kernel/device/dev_dsp.c | 3 +- cpu/src/kernel/device/dev_ipc.c | 0 cpu/src/kernel/device/dev_ipc.h | 7 + cpu/src/kernel/peripheral/per_aintc.c | 30 ++ cpu/src/kernel/peripheral/per_aintc.h | 43 ++ cpu/src/kernel/peripheral/per_emifa.c | 366 ++++++++++++++ cpu/src/kernel/peripheral/per_emifa.c.old | 432 ++++++++++++++++ cpu/src/kernel/peripheral/per_emifa.h | 94 ++++ cpu/src/kernel/peripheral/per_emifa.h.old | 94 ++++ cpu/src/kernel/peripheral/per_gpio.h | 6 + cpu/src/kernel/peripheral/per_pinmux.c | 2 + cpu/src/kernel/service/svc_dsp.c | 24 + cpu/src/kernel/service/svc_dsp.h | 6 +- dsp/Makefile | 3 +- dsp/dsp.lds | 4 + dsp/src/kernel/device/dev_cpu_spi.c | 9 +- dsp/src/kernel/device/dev_cpu_spi.h | 2 +- dsp/src/kernel/device/dev_shared_mem.c | 8 + dsp/src/kernel/device/dev_shared_mem.h | 69 +++ dsp/src/kernel/peripheral/per_gpio.c | 21 + dsp/src/kernel/peripheral/per_gpio.h | 2 + dsp/src/kernel/peripheral/per_hostdma.c | 238 +++++++++ dsp/src/kernel/peripheral/per_hostdma.c.old | 241 +++++++++ dsp/src/kernel/peripheral/per_hostdma.h | 30 ++ dsp/src/kernel/peripheral/per_hostdma.h.old | 30 ++ dsp/src/kernel/service/dev_ipc.h | 6 + dsp/src/kernel/service/svc_cpu.c | 31 +- dsp/src/modules/system23/delayline.c | 34 ++ dsp/src/modules/system23/delayline.h | 20 + dsp/src/modules/system23/system23.c | 287 +++++++++++ 46 files changed, 3733 insertions(+), 40 deletions(-) delete mode 100644 .devcontainer/devcontainer.json create mode 100644 .dockerignore create mode 100644 cpu/src/apps/system23/lookup_tables.h create mode 100644 cpu/src/apps/system23/lut.c create mode 100644 cpu/src/apps/system23/lut.h create mode 100644 cpu/src/apps/system23/param_scale.h create mode 100644 cpu/src/apps/system23/sequencer.h create mode 100644 cpu/src/apps/system23/system23.c create mode 100644 cpu/src/apps/system23/utils/tinyprintf.c create mode 100644 cpu/src/apps/system23/utils/tinyprintf.h create mode 100644 cpu/src/kernel/device/dev_ipc.c create mode 100644 cpu/src/kernel/device/dev_ipc.h create mode 100644 cpu/src/kernel/peripheral/per_emifa.c create mode 100644 cpu/src/kernel/peripheral/per_emifa.c.old create mode 100644 cpu/src/kernel/peripheral/per_emifa.h create mode 100644 cpu/src/kernel/peripheral/per_emifa.h.old create mode 100644 dsp/src/kernel/device/dev_shared_mem.c create mode 100644 dsp/src/kernel/device/dev_shared_mem.h create mode 100644 dsp/src/kernel/peripheral/per_hostdma.c create mode 100644 dsp/src/kernel/peripheral/per_hostdma.c.old create mode 100644 dsp/src/kernel/peripheral/per_hostdma.h create mode 100644 dsp/src/kernel/peripheral/per_hostdma.h.old create mode 100644 dsp/src/kernel/service/dev_ipc.h create mode 100644 dsp/src/modules/system23/delayline.c create mode 100644 dsp/src/modules/system23/delayline.h create mode 100644 dsp/src/modules/system23/system23.c diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 62e9bf85..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1 +0,0 @@ -{ "dockerComposeFile": "../docker-compose.yaml", "service": "freetribe" } diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..7347a7fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/.gitignore b/.gitignore index eb894949..a0474005 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ compile_commands.json *.out cpu/src/resources/bfin_ldr.h run.sh +freetribe.code-workspace +.vscode \ No newline at end of file diff --git a/cpu/Makefile b/cpu/Makefile index d215a44e..03e2519a 100644 --- a/cpu/Makefile +++ b/cpu/Makefile @@ -31,10 +31,11 @@ BIN=$(PREFIX)objcopy # C compilation options. NEST_INT ?= 0 -OPTIMISE ?= -g3 -Og +# OPTIMISE ?= -g3 -Og # OPTIMISE ?= -O3 +OPTIMISE ?= -Os CPU ?= arm926ej-s -CFLAGS := -mcpu=$(CPU) $(OPTIMISE) -fdata-sections -ffunction-sections -fanalyzer -fstack-usage -Wstack-usage=128 -Wall +CFLAGS := -mcpu=$(CPU) $(OPTIMISE) -fdata-sections -ffunction-sections -fanalyzer -fstack-usage -Wstack-usage=128 -Wall -Wno-unused # Assembly compilation options. ASM_FLAGS := -x assembler-with-cpp diff --git a/cpu/cpu.lds b/cpu/cpu.lds index 73729b59..aa2aa93d 100755 --- a/cpu/cpu.lds +++ b/cpu/cpu.lds @@ -13,6 +13,7 @@ MEMORY { OC_RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x20000 ARM_RAM (rwx) : ORIGIN = 0xFFFF0000, LENGTH = 0x2000 + DDR2_RAM (rw) : ORIGIN = 0xc0000000, LENGTH = 64M /* sk hynix h5ps5162kfr-s5c chip maximum is 64MB */ } SECTIONS @@ -94,4 +95,8 @@ SECTIONS _stack = .; } > ARM_RAM + + _ddr2_start = ORIGIN(DDR2_RAM); + _ddr2_end = ORIGIN(DDR2_RAM) + LENGTH(DDR2_RAM); + } diff --git a/cpu/src/api/freetribe.c b/cpu/src/api/freetribe.c index baf823e7..c0ef808c 100644 --- a/cpu/src/api/freetribe.c +++ b/cpu/src/api/freetribe.c @@ -40,8 +40,12 @@ under the terms of the GNU Affero General Public License as published by /*----- Includes -----------------------------------------------------*/ +#define TINYPRINTF_DEFINE_TFP_PRINTF 0 +#define TINYPRINTF_OVERRIDE_LIBC 0 +#include "utils/tinyprintf.h" #include "freetribe.h" #include +#include /*----- Macros -------------------------------------------------------*/ @@ -133,6 +137,20 @@ void ft_register_print_callback(void (*callback)(char *)) { */ void ft_print(char *text) { svc_midi_send_string(text); } +/** + * @brief Literally printf girl + */ +void ft_printf(const char *format, ...) +{ + va_list ap; + static char str[256]; + + va_start(ap, format); + tfp_vsprintf(str, format, ap); + svc_midi_send_string(str); + va_end(ap); +} + // Panel API // /** diff --git a/cpu/src/api/freetribe.h b/cpu/src/api/freetribe.h index 8d212584..9465aac4 100644 --- a/cpu/src/api/freetribe.h +++ b/cpu/src/api/freetribe.h @@ -86,6 +86,7 @@ int8_t ft_fill_frame(uint16_t x_start, uint16_t y_start, uint16_t x_end, void ft_register_print_callback(void (*callback)(char *)); void ft_print(char *text); +void ft_printf(const char *format, ...); void ft_register_midi_callback(event_type event, t_midi_event_callback callback); diff --git a/cpu/src/apps/system23/lookup_tables.h b/cpu/src/apps/system23/lookup_tables.h new file mode 100644 index 00000000..1be7d4a9 --- /dev/null +++ b/cpu/src/apps/system23/lookup_tables.h @@ -0,0 +1,356 @@ +#ifndef LOOKUP_TABLES_H +#define LOOKUP_TABLES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +static int32_t freq_12tet_lut[] = { + 0x82d01, 0x86a55, 0x8a976, 0x8ea70, 0x92d51, 0x97228, + 0x9b904, 0xa01f3, 0xa4d05, 0xa9a4a, 0xae9d3, 0xb3bb0, + 0xb8ff4, 0xbe6b0, 0xc3ff6, 0xc9bda, 0xcfa70, 0xd5bca, + 0xdc000, 0xe2724, 0xe914f, 0xefe96, 0xf6f11, 0xfe2d7, + 0x105a02, 0x10d4ab, 0x1152ec, 0x11d4e0, 0x125aa2, 0x12e451, + 0x137208, 0x1403e6, 0x149a0a, 0x153495, 0x15d3a6, 0x167761, + 0x171fe9, 0x17cd60, 0x187fed, 0x1937b5, 0x19f4e0, 0x1ab795, + 0x1b8000, 0x1c4e49, 0x1d229e, 0x1dfd2c, 0x1ede22, 0x1fc5ae, + 0x20b404, 0x21a956, 0x22a5d8, 0x23a9c0, 0x24b545, 0x25c8a2, + 0x26e410, 0x2807cc, 0x293414, 0x2a692a, 0x2ba74d, 0x2ceec3, + 0x2e3fd2, 0x2f9ac1, 0x30ffda, 0x326f6a, 0x33e9c0, 0x356f2b, + 0x370000, 0x389c93, 0x3a453d, 0x3bfa59, 0x3dbc44, 0x3f8b5d, + 0x416809, 0x4352ac, 0x454bb0, 0x475380, 0x496a8b, 0x4b9144, + 0x4dc820, 0x500f98, 0x526829, 0x54d254, 0x574e9b, 0x59dd87, + 0x5c7fa4, 0x5f3582, 0x61ffb5, 0x64ded5, 0x67d380, 0x6ade56, + 0x6e0000, 0x713926, 0x748a7b, 0x77f4b2, 0x7b7888, 0x7f16bb, + 0x82d012, 0x86a559, 0x8a9760, 0x8ea700, 0x92d517, 0x972288, + 0x9b9041, 0xa01f31, 0xa4d053, 0xa9a4a8, 0xae9d36, 0xb3bb0f, + 0xb8ff49, 0xbe6b04, 0xc3ff6a, 0xc9bdaa, 0xcfa700, 0xd5bcad, + 0xdc0000, 0xe2724d, 0xe914f6, 0xefe965, 0xf6f110, 0xfe2d76, + 0x105a025, 0x10d4ab2, 0x1152ec0, 0x11d4e00, 0x125aa2e, 0x12e4511, + 0x1372082, 0x1403e63, 0x149a0a7, 0x1534950, 0x15d3a6d, 0x167761e, + 0x171fe92, 0x17cd609, 0x187fed4, 0x1937b55, 0x19f4e00, 0x1ab795b, + 0x1b80000, 0x1c4e49a, 0x1d229ec, 0x1dfd2ca, 0x1ede220, 0x1fc5aed, + 0x20b404a, 0x21a9564, 0x22a5d81, 0x23a9c01, 0x24b545c, 0x25c8a23, + 0x26e4104, 0x2807cc6, 0x293414f, 0x2a692a0, 0x2ba74da, 0x2ceec3c, + 0x2e3fd24, 0x2f9ac13, 0x30ffda9, 0x326f6ab, 0x33e9c01, 0x356f2b7, + 0x3700000, 0x389c935, 0x3a453d8, 0x3bfa594, 0x3dbc440, 0x3f8b5db, + 0x4168094, 0x4352ac8, 0x454bb03, 0x4753803, 0x496a8b8, 0x4b91447, + 0x4dc8208, 0x500f98c, 0x526829e, 0x54d2541, 0x574e9b5, 0x59dd879, + 0x5c7fa49, 0x5f35827, 0x61ffb53, 0x64ded57, 0x67d3802, 0x6ade56e, + 0x6e00000, 0x713926a, 0x748a7b1, 0x77f4b29, 0x7b78880, 0x7f16bb6, + 0x82d0128, 0x86a5590, 0x8a97607, 0x8ea7007, 0x92d5171, 0x972288e, + 0x9b90410, 0xa01f319, 0xa4d053c, 0xa9a4a82, 0xae9d36b, 0xb3bb0f2, + 0xb8ff493, 0xbe6b04e, 0xc3ff6a7, 0xc9bdaae, 0xcfa7005, 0xd5bcadc, + 0xdc00000, 0xe2724d4, 0xe914f62, 0xefe9653, 0xf6f1100, 0xfe2d76c, + 0x105a0250, 0x10d4ab21, 0x1152ec0e, 0x11d4e00f, 0x125aa2e3, 0x12e4511c, + 0x13720820, 0x1403e632, 0x149a0a79, 0x15349504, 0x15d3a6d6, 0x167761e4, + 0x171fe927, 0x17cd609c, 0x187fed4e, 0x1937b55d, 0x19f4e00a, 0x1ab795b9, + 0x1b800000, 0x1c4e49a9, 0x1d229ec4, 0x1dfd2ca7, 0x1ede2200, 0x1fc5aed8, + 0x20b404a1, 0x21a95642, 0x22a5d81c, 0x23a9c01e, 0x24b545c7, 0x25c8a238, + 0x26e41040, 0x2807cc64, 0x293414f2, 0x2a692a09, 0x2ba74dac, 0x2ceec3c9, + 0x2e3fd24f, 0x2f9ac139, 0x30ffda9c, 0x326f6abb}; + +static int32_t amp_lut[] = { + 0x00, 0x8f662, 0x11ecc5, 0x1ae328, 0x23d98b, 0x2ccfee, + 0x35c651, 0x3ebcb4, 0x47b316, 0x50a979, 0x599fdc, 0x62963f, + 0x6b8ca2, 0x748305, 0x7d7968, 0x866fca, 0x8f662d, 0x985c90, + 0xa152f3, 0xaa4956, 0xb33fb9, 0xbc361c, 0xc52c7e, 0xce22e1, + 0xd71944, 0xe00fa7, 0xe9060a, 0xf1fc6d, 0xfaf2d0, 0x103e932, + 0x10cdf95, 0x115d5f8, 0x11ecc5b, 0x127c2be, 0x130b921, 0x139af84, + 0x142a5e7, 0x14b9c49, 0x15492ac, 0x15d890f, 0x1667f72, 0x16f75d5, + 0x1786c38, 0x181629b, 0x18a58fd, 0x1934f60, 0x19c45c3, 0x1a53c26, + 0x1ae3289, 0x1b728ec, 0x1c01f4f, 0x1c915b1, 0x1d20c14, 0x1db0277, + 0x1e3f8da, 0x1ecef3d, 0x1f5e5a0, 0x1fedc03, 0x207d265, 0x210c8c8, + 0x219bf2b, 0x222b58e, 0x22babf1, 0x234a254, 0x23d98b7, 0x2468f1a, + 0x24f857c, 0x2587bdf, 0x2617242, 0x26a68a5, 0x2735f08, 0x27c556b, + 0x2854bce, 0x28e4230, 0x2973893, 0x2a02ef6, 0x2a92559, 0x2b21bbc, + 0x2bb121f, 0x2c40882, 0x2ccfee4, 0x2d5f547, 0x2deebaa, 0x2e7e20d, + 0x2f0d870, 0x2f9ced3, 0x302c536, 0x30bbb98, 0x314b1fb, 0x31da85e, + 0x3269ec1, 0x32f9524, 0x3388b87, 0x34181ea, 0x34a784d, 0x3536eaf, + 0x35c6512, 0x3655b75, 0x36e51d8, 0x377483b, 0x3803e9e, 0x3893501, + 0x3922b63, 0x39b21c6, 0x3a41829, 0x3ad0e8c, 0x3b604ef, 0x3befb52, + 0x3c7f1b5, 0x3d0e817, 0x3d9de7a, 0x3e2d4dd, 0x3ebcb40, 0x3f4c1a3, + 0x3fdb806, 0x406ae69, 0x40fa4cb, 0x4189b2e, 0x4219191, 0x42a87f4, + 0x4337e57, 0x43c74ba, 0x4456b1d, 0x44e6180, 0x45757e2, 0x4604e45, + 0x46944a8, 0x4723b0b, 0x47b316e, 0x48427d1, 0x48d1e34, 0x4961496, + 0x49f0af9, 0x4a8015c, 0x4c2b583, 0x4e41b97, 0x5066bfc, 0x529ad1d, + 0x54de594, 0x5731c28, 0x59957ce, 0x5c09fb0, 0x5e8fb27, 0x61271c1, + 0x63d0b3e, 0x668cf9a, 0x695c703, 0x6c3f9e6, 0x6f370e5, 0x72434e5, + 0x7564f04, 0x789c8a3, 0x7beab64, 0x7f5012d, 0x82cd428, 0x8662ec9, + 0x8a11bca, 0x8dda633, 0x91bd958, 0x95bc0dc, 0x99d68b6, 0x9e0dd2e, + 0xa262ae5, 0xa6d5ed1, 0xab68648, 0xb01aef8, 0xb4ee6f2, 0xb9e3cab, + 0xbefbefa, 0xc437d21, 0xc9986cb, 0xcf1ec12, 0xd4cbd81, 0xdaa0c17, + 0xe09e949, 0xe6c6709, 0xed197c4, 0xf398e6b, 0xfa45e72, 0x10121bd6, + 0x1082db20, 0x10f6b16a, 0x116db462, 0x11e7fa4c, 0x12659a0a, 0x12e6ab1f, + 0x136b45b1, 0x13f38292, 0x147f7b41, 0x150f49ee, 0x15a30983, 0x163ad5a7, + 0x16d6cac1, 0x17770601, 0x181ba563, 0x18c4c7b6, 0x19728ca0, 0x1a2514a7, + 0x1adc8132, 0x1b98f496, 0x1c5a9216, 0x1d217dee, 0x1deddd58, 0x1ebfd693, + 0x1f9790e9, 0x207534b9, 0x2158eb7e, 0x2242dfd5, 0x23333d86, 0x242a318c, + 0x2527ea1d, 0x262c96b6, 0x2738681d, 0x284b9071, 0x29664330, 0x2a88b542, + 0x2bb31d01, 0x2ce5b244, 0x2e20ae6a, 0x2f644c67, 0x30b0c8c9, 0x320661c9, + 0x33655753, 0x34cdeb15, 0x3640608a, 0x37bcfd04, 0x394407be, 0x3ad5c9e5, + 0x3c728ea8, 0x3e1aa346, 0x3fce571b, 0x418dfbb0, 0x4359e4cb, 0x4532687d, + 0x4717df31, 0x490aa3c2, 0x4b0b1385, 0x4d198e61, 0x4f3676da, 0x5162322b, + 0x539d2852, 0x55e7c429, 0x58427376, 0x5aada702, 0x5d29d2ae, 0x5fb76d85, + 0x6256f1d9, 0x6508dd54, 0x67cdb111, 0x6aa5f1b6, 0x6d92278d, 0x7092de98, + 0x73a8a6b3, 0x76d413ab, 0x7a15bd57, 0x7d6e3fba}; + +static int32_t amp_rep_lut[] = { + 0x01, 0xffb8bd52, 0xffbec298, 0xffc2482f, 0xffc4c7de, 0xffc6b80c, + 0xffc84d75, 0xffc9a439, 0xffcacd24, 0xffcbd30b, 0xffccbd52, 0xffcd9141, + 0xffce52bb, 0xffcf04b6, 0xffcfa97f, 0xffd042e9, 0xffd0d26b, 0xffd15938, + 0xffd1d851, 0xffd2508a, 0xffd2c299, 0xffd32f16, 0xffd39687, 0xffd3f95e, + 0xffd45801, 0xffd4b2c6, 0xffd509fc, 0xffd55de8, 0xffd5aec5, 0xffd5fccd, + 0xffd6482f, 0xffd69118, 0xffd6d7b1, 0xffd71c1d, 0xffd75e7f, 0xffd79ef3, + 0xffd7dd97, 0xffd81a84, 0xffd855d0, 0xffd88f93, 0xffd8c7df, 0xffd8fec7, + 0xffd9345c, 0xffd968ae, 0xffd99bcd, 0xffd9cdc5, 0xffd9fea4, 0xffda2e77, + 0xffda5d47, 0xffda8b20, 0xffdab80c, 0xffdae415, 0xffdb0f42, 0xffdb399d, + 0xffdb632e, 0xffdb8bfb, 0xffdbb40b, 0xffdbdb67, 0xffdc0213, 0xffdc2816, + 0xffdc4d75, 0xffdc7236, 0xffdc965e, 0xffdcb9f2, 0xffdcdcf7, 0xffdcff70, + 0xffdd2163, 0xffdd42d3, 0xffdd63c5, 0xffdd843b, 0xffdda439, 0xffddc3c4, + 0xffdde2dd, 0xffde0189, 0xffde1fca, 0xffde3da3, 0xffde5b16, 0xffde7828, + 0xffde94d9, 0xffdeb12c, 0xffdecd25, 0xffdee8c4, 0xffdf040d, 0xffdf1f01, + 0xffdf39a2, 0xffdf53f2, 0xffdf6df4, 0xffdf87a9, 0xffdfa113, 0xffdfba33, + 0xffdfd30b, 0xffdfeb9d, 0xffe003ea, 0xffe01bf4, 0xffe033bd, 0xffe04b44, + 0xffe0628d, 0xffe07998, 0xffe09066, 0xffe0a6f9, 0xffe0bd52, 0xffe0d373, + 0xffe0e95b, 0xffe0ff0c, 0xffe11488, 0xffe129d0, 0xffe13ee3, 0xffe153c4, + 0xffe16874, 0xffe17cf2, 0xffe19141, 0xffe1a560, 0xffe1b952, 0xffe1cd15, + 0xffe1e0ad, 0xffe1f418, 0xffe20759, 0xffe21a6f, 0xffe22d5c, 0xffe2401f, + 0xffe252bb, 0xffe2652f, 0xffe2777c, 0xffe289a3, 0xffe29ba4, 0xffe2ad80, + 0xffe2bf38, 0xffe2d0cc, 0xffe2e23d, 0xffe2f38b, 0xffe304b6, 0xffe315c0, + 0xffe326a9, 0xffe33771, 0xffe368b5, 0xffe3a4d3, 0xffe3e0f1, 0xffe41d0f, + 0xffe4592d, 0xffe4954b, 0xffe4d169, 0xffe50d87, 0xffe549a5, 0xffe585c3, + 0xffe5c1e1, 0xffe5fdff, 0xffe63a1e, 0xffe6763c, 0xffe6b25a, 0xffe6ee78, + 0xffe72a96, 0xffe766b4, 0xffe7a2d2, 0xffe7def0, 0xffe81b0e, 0xffe8572c, + 0xffe8934a, 0xffe8cf68, 0xffe90b86, 0xffe947a4, 0xffe983c2, 0xffe9bfe0, + 0xffe9fbfe, 0xffea381d, 0xffea743b, 0xffeab059, 0xffeaec77, 0xffeb2895, + 0xffeb64b3, 0xffeba0d1, 0xffebdcef, 0xffec190d, 0xffec552b, 0xffec9149, + 0xffeccd67, 0xffed0985, 0xffed45a3, 0xffed81c1, 0xffedbddf, 0xffedf9fd, + 0xffee361c, 0xffee723a, 0xffeeae58, 0xffeeea76, 0xffef2694, 0xffef62b2, + 0xffef9ed0, 0xffefdaee, 0xfff0170c, 0xfff0532a, 0xfff08f48, 0xfff0cb66, + 0xfff10784, 0xfff143a2, 0xfff17fc0, 0xfff1bbde, 0xfff1f7fc, 0xfff2341b, + 0xfff27039, 0xfff2ac57, 0xfff2e875, 0xfff32493, 0xfff360b1, 0xfff39ccf, + 0xfff3d8ed, 0xfff4150b, 0xfff45129, 0xfff48d47, 0xfff4c965, 0xfff50583, + 0xfff541a1, 0xfff57dbf, 0xfff5b9dd, 0xfff5f5fb, 0xfff6321a, 0xfff66e38, + 0xfff6aa56, 0xfff6e674, 0xfff72292, 0xfff75eb0, 0xfff79ace, 0xfff7d6ec, + 0xfff8130a, 0xfff84f28, 0xfff88b46, 0xfff8c764, 0xfff90382, 0xfff93fa0, + 0xfff97bbe, 0xfff9b7dc, 0xfff9f3fa, 0xfffa3019, 0xfffa6c37, 0xfffaa855, + 0xfffae473, 0xfffb2091, 0xfffb5caf, 0xfffb98cd, 0xfffbd4eb, 0xfffc1109, + 0xfffc4d27, 0xfffc8945, 0xfffcc563, 0xfffd0181, 0xfffd3d9f, 0xfffd79bd, + 0xfffdb5db, 0xfffdf1f9, 0xfffe2e18, 0xfffe6a36, 0xfffea654, 0xfffee272, + 0xffff1e90, 0xffff5aae, 0xffff96cc, 0xffffd2ea}; + +static int32_t svf_lut[] = { + 0x1188c8, 0x120c50, 0x1293b3, 0x131f0d, 0x13ae7d, 0x144221, + 0x14da18, 0x157683, 0x161783, 0x16bd3b, 0x1767ce, 0x181761, + 0x18cc19, 0x19861c, 0x1a4593, 0x1b0aa6, 0x1bd580, 0x1ca64b, + 0x1d7d34, 0x1e5a69, 0x1f3e1a, 0x202877, 0x2119b2, 0x2211fe, + 0x231191, 0x2418a1, 0x252766, 0x263e1b, 0x275cfa, 0x288441, + 0x29b430, 0x2aed06, 0x2c2f06, 0x2d7a77, 0x2ecf9d, 0x302ec3, + 0x319832, 0x330c39, 0x348b27, 0x36154d, 0x37ab00, 0x394c96, + 0x3afa68, 0x3cb4d3, 0x3e7c34, 0x4050ee, 0x423363, 0x4423fc, + 0x462322, 0x483142, 0x4a4ecd, 0x4c7c36, 0x4eb9f4, 0x510882, + 0x53685f, 0x55da0b, 0x585e0c, 0x5af4ec, 0x5d9f39, 0x605d84, + 0x633063, 0x661871, 0x69164c, 0x6c2a98, 0x6f55fd, 0x729929, + 0x75f4ce, 0x7969a3, 0x7cf865, 0x80a1d8, 0x8466c3, 0x8847f4, + 0x8c463f, 0x90627e, 0x949d93, 0x98f865, 0x9d73e1, 0xa210fd, + 0xa6d0b5, 0xabb40c, 0xb0bc0e, 0xb5e9cd, 0xbb3e66, 0xc0bafb, + 0xc660b8, 0xcc30d2, 0xd22c86, 0xd8551d, 0xdeabe5, 0xe5323b, + 0xebe982, 0xf2d32b, 0xf9f0ae, 0x1014390, 0x108cd62, 0x1108fc1, + 0x1188c54, 0x120c4cf, 0x1293af5, 0x131f094, 0x13ae787, 0x14421b9, + 0x14da123, 0x15767cb, 0x16177c8, 0x16bd340, 0x1767c68, 0x1817589, + 0x18cc0f9, 0x1986122, 0x1a4587f, 0x1b0a99f, 0x1bd5723, 0x1ca63bf, + 0x1d7d23d, 0x1e5a57b, 0x1f3e06d, 0x202861d, 0x21199aa, 0x2211e4e, + 0x2311757, 0x2418830, 0x252745a, 0x263df73, 0x275cd32, 0x288416c, + 0x29b4010, 0x2aecd2d, 0x2c2ecee, 0x2d7a3a1, 0x2ecf5b0, 0x302e7a8, + 0x3197e39, 0x330be35, 0x348ac92, 0x3614e6b, 0x37aa902, 0x394c1c1, + 0x3af9e38, 0x3cb4423, 0x3e7b969, 0x405041b, 0x4232a7a, 0x44232f4, + 0x4622428, 0x48304e5, 0x4a4dc30, 0x4c7b13f, 0x4eb8b81, 0x510729b, + 0x5366e6b, 0x55d870a, 0x585c4ce, 0x5af304b, 0x5d9d255, 0x605b402, + 0x632deab, 0x6615bf0, 0x69135b9, 0x6c27637, 0x6f527e7, 0x7295595, + 0x75f0a5b, 0x79651aa, 0x7cf3744, 0x809c744, 0x8460e21, 0x88418ac, + 0x8c3f415, 0x905adf1, 0x9495436, 0x98ef545, 0x9d69fe6, 0xa206352, + 0xa6c4f31, 0xaba739d, 0xb0ae12a, 0xb5da8e6, 0xbb2dc59, 0xc0a8d92, + 0xc64cf20, 0xcc1b41c, 0xd21502c, 0xd83b784, 0xde8feef, 0xe513bce, + 0xebc841d, 0xf2aee79, 0xf9c9225, 0x1011870a, 0x1089e5c1, 0x1105c793, + 0x1185467f, 0x12087d40, 0x128f8752, 0x131a80f1, 0x13a98728, 0x143cb7cd, + 0x14d4318b, 0x157013e3, 0x16107f38, 0x16b594cd, 0x175f76cc, 0x180e484e, + 0x18c22d5f, 0x197b4b02, 0x1a39c737, 0x1afdc8fe, 0x1bc77862, 0x1c96fe78, + 0x1d6c8566, 0x1e483869, 0x1f2a43d9, 0x2012d52e, 0x21021b01, 0x21f84516, + 0x22f5845a, 0x23fa0aec, 0x25060c1a, 0x2619bc6b, 0x2735519a, 0x2859029d, + 0x298507a1, 0x2ab99a12, 0x2bf6f493, 0x2d3d5300, 0x2e8cf26c, 0x2fe6111e, + 0x3148ee89, 0x32b5cb4d, 0x342ce928, 0x35ae8af3, 0x373af492, 0x38d26aee, + 0x3a7533de, 0x3c23961d, 0x3dddd932, 0x3fa44558, 0x41772367, 0x4356bcb5, + 0x45435af4, 0x473d480c, 0x4944cdf2, 0x4b5a3678, 0x4d7dcb15, 0x4fafd4b0, + 0x51f09b59, 0x54406603, 0x569f7a30, 0x590e1b9f, 0x5b8c8be2, 0x5e1b09f4, + 0x60b9d1c4, 0x63691bb0, 0x66291bf3, 0x68fa020d}; + +static int32_t integrator_lut[] = { + 0x00, 0x792fb81d, 0x7c97b63e, 0x7dbfb779, 0x7e54b174, 0x7eae5dba, + 0x7eea3fee, 0x7f150f68, 0x7f352d7d, 0x7f4e27d5, 0x7f62213e, 0x7f727631, + 0x7f800f43, 0x7f8b8da0, 0x7f956480, 0x7f9de86f, 0x7fa558e0, 0x7fabe65e, + 0x7fb1b6af, 0x7fb6e7a9, 0x7fbb912c, 0x7fbfc68d, 0x7fc3979f, 0x7fc71176, + 0x7fca3ef7, 0x7fcd294b, 0x7fcfd832, 0x7fd25246, 0x7fd49d31, 0x7fd6bdd4, + 0x7fd8b86b, 0x7fda90a5, 0x7fdc49bc, 0x7fdde686, 0x7fdf6983, 0x7fe0d4ec, + 0x7fe22ab8, 0x7fe36ca9, 0x7fe49c53, 0x7fe5bb1f, 0x7fe6ca52, 0x7fe7cb12, + 0x7fe8be69, 0x7fe9a548, 0x7fea808a, 0x7feb50f7, 0x7fec1746, 0x7fecd41d, + 0x7fed8815, 0x7fee33bc, 0x7feed792, 0x7fef7410, 0x7ff009a2, 0x7ff098ae, + 0x7ff12194, 0x7ff1a4ab, 0x7ff22243, 0x7ff29aa9, 0x7ff30e22, 0x7ff37cf1, + 0x7ff3e752, 0x7ff44d80, 0x7ff4afae, 0x7ff50e10, 0x7ff568d5, 0x7ff5c029, + 0x7ff61434, 0x7ff66520, 0x7ff6b30f, 0x7ff6fe24, 0x7ff74681, 0x7ff78c45, + 0x7ff7cf8b, 0x7ff81070, 0x7ff84f0e, 0x7ff88b7d, 0x7ff8c5d5, 0x7ff8fe2b, + 0x7ff93496, 0x7ff96928, 0x7ff99bf5, 0x7ff9cd0f, 0x7ff9fc87, 0x7ffa2a6c, + 0x7ffa56d0, 0x7ffa81c0, 0x7ffaab4b, 0x7ffad37e, 0x7ffafa65, 0x7ffb200e, + 0x7ffb4484, 0x7ffb67d2, 0x7ffb8a02, 0x7ffbab20, 0x7ffbcb35, 0x7ffbea4a, + 0x7ffc0868, 0x7ffc2598, 0x7ffc41e2, 0x7ffc5d4e, 0x7ffc77e3, 0x7ffc91aa, + 0x7ffcaaa9, 0x7ffcc2e6, 0x7ffcda69, 0x7ffcf136, 0x7ffd0756, 0x7ffd1ccc, + 0x7ffd319f, 0x7ffd45d4, 0x7ffd5970, 0x7ffd6c78, 0x7ffd7ef0, 0x7ffd90de, + 0x7ffda246, 0x7ffdb32c, 0x7ffdc394, 0x7ffdd381, 0x7ffde2f9, 0x7ffdf1fe, + 0x7ffe0095, 0x7ffe0ec0, 0x7ffe1c82, 0x7ffe29e0, 0x7ffe36dc, 0x7ffe4378, + 0x7ffe4fb9, 0x7ffe5ba1, 0x7ffe6732, 0x7ffe726f, 0x7ffe7d5a, 0x7ffe87f7, + 0x7ffe9247, 0x7ffe9c4c, 0x7ffea60a, 0x7ffeaf81, 0x7ffeb8b4, 0x7ffec1a5, + 0x7ffeca57, 0x7ffed2ca, 0x7ffedb00, 0x7ffee2fd, 0x7ffeeac0, 0x7ffef24c, + 0x7ffef9a2, 0x7fff00c4, 0x7fff07b4, 0x7fff0e72, 0x7fff1501, 0x7fff1b61, + 0x7fff2194, 0x7fff279c, 0x7fff2d78, 0x7fff332c, 0x7fff38b7, 0x7fff3e1c, + 0x7fff435a, 0x7fff4874, 0x7fff4d6a, 0x7fff523d, 0x7fff56ee, 0x7fff5b7f, + 0x7fff5fef, 0x7fff6441, 0x7fff6874, 0x7fff6c8a, 0x7fff7083, 0x7fff7461, + 0x7fff7824, 0x7fff7bcc, 0x7fff7f5b, 0x7fff82d2, 0x7fff8630, 0x7fff8977, + 0x7fff8ca7, 0x7fff8fc1, 0x7fff92c5, 0x7fff95b4, 0x7fff988f, 0x7fff9b57, + 0x7fff9e0b, 0x7fffa0ac, 0x7fffa33b, 0x7fffa5b9, 0x7fffa825, 0x7fffaa80, + 0x7fffaccb, 0x7fffaf06, 0x7fffb132, 0x7fffb34f, 0x7fffb55d, 0x7fffb75d, + 0x7fffb950, 0x7fffbb34, 0x7fffbd0c, 0x7fffbed7, 0x7fffc096, 0x7fffc249, + 0x7fffc3f0, 0x7fffc58b, 0x7fffc71c, 0x7fffc8a2, 0x7fffca1d, 0x7fffcb8e, + 0x7fffccf5, 0x7fffce53, 0x7fffcfa7, 0x7fffd0f2, 0x7fffd234, 0x7fffd36e, + 0x7fffd49f, 0x7fffd5c8, 0x7fffd6e8, 0x7fffd802, 0x7fffd913, 0x7fffda1e, + 0x7fffdb21, 0x7fffdc1d, 0x7fffdd12, 0x7fffde01, 0x7fffdeea, 0x7fffdfcc, + 0x7fffe0a8, 0x7fffe17f, 0x7fffe24f, 0x7fffe31a, 0x7fffe3e0, 0x7fffe4a0, + 0x7fffe55b, 0x7fffe611, 0x7fffe6c2, 0x7fffe76f, 0x7fffe817, 0x7fffe8ba, + 0x7fffe959, 0x7fffe9f4, 0x7fffea8a, 0x7fffeb1d, 0x7fffebac, 0x7fffec37, + 0x7fffecbe, 0x7fffed41, 0x7fffedc1, 0x7fffee3e, 0x7fffeeb7, 0x7fffef2d, + 0x7fffefa0, 0x7ffff010, 0x7ffff07d, 0x7ffff0e7, 0x7ffff14e, 0x7ffff1b2, + 0x7ffff214, 0x7ffff273, 0x7ffff2cf, 0x7ffff329}; + +static int32_t integrator_short_lut[] = { + 0x00, 0x2a085d6e, 0x49e73feb, 0x5931b3fc, 0x61fc0d1a, 0x67aaa25d, + 0x6ba24a99, 0x6e8eeb6f, 0x70cd3715, 0x72937d17, 0x7403a463, 0x7533ebe2, + 0x76338643, 0x770d2f82, 0x77c8b376, 0x786bde8d, 0x78fb17a3, 0x7979c588, + 0x79ea93cc, 0x7a4fa27a, 0x7aaaa7f7, 0x7afd0977, 0x7b47ecee, 0x7b8c467d, + 0x7bcae290, 0x7c046da0, 0x7c397a33, 0x7c6a8594, 0x7c97fb85, 0x7cc23939, + 0x7ce98fb3, 0x7d0e45b4, 0x7d309950, 0x7d50c135, 0x7d6eedc1, 0x7d8b49e1, + 0x7da5fbd2, 0x7dbf25c0, 0x7dd6e64c, 0x7ded5902, 0x7e0296b4, 0x7e16b5d3, + 0x7e29cab5, 0x7e3be7d3, 0x7e4d1dfe, 0x7e5d7c8d, 0x7e6d1187, 0x7e7be9c6, + 0x7e8a1115, 0x7e97924b, 0x7ea47765, 0x7eb0c99b, 0x7ebc9170, 0x7ec7d6c6, + 0x7ed2a0ea, 0x7edcf6a4, 0x7ee6de42, 0x7ef05da0, 0x7ef97a37, 0x7f02391e, + 0x7f0a9f1b, 0x7f12b0a1, 0x7f1a71dd, 0x7f21e6b6, 0x7f2912d9, 0x7f2ff9b6, + 0x7f369e8c, 0x7f3d0465, 0x7f432e21, 0x7f491e74, 0x7f4ed7ec, 0x7f545cf1, + 0x7f59afcb, 0x7f5ed2a2, 0x7f63c77e, 0x7f68904e, 0x7f6d2ee8, 0x7f71a507, + 0x7f75f450, 0x7f7a1e56, 0x7f7e2494, 0x7f820874, 0x7f85cb50, 0x7f896e6e, + 0x7f8cf308, 0x7f905a48, 0x7f93a548, 0x7f96d51a, 0x7f99eabe, 0x7f9ce72d, + 0x7f9fcb52, 0x7fa2980f, 0x7fa54e3d, 0x7fa7eea9, 0x7faa7a1a, 0x7facf14e, + 0x7faf54f9, 0x7fb1a5ca, 0x7fb3e468, 0x7fb61173, 0x7fb82d84, 0x7fba392f, + 0x7fbc3502, 0x7fbe2184, 0x7fbfff39, 0x7fc1ce9d, 0x7fc3902b, 0x7fc54456, + 0x7fc6eb8e, 0x7fc8863e, 0x7fca14cf, 0x7fcb97a5, 0x7fcd0f1e, 0x7fce7b99, + 0x7fcfdd6e, 0x7fd134f4, 0x7fd2827d, 0x7fd3c659, 0x7fd500d7, 0x7fd6323f, + 0x7fd75adb, 0x7fd87af0, 0x7fd992c0, 0x7fdaa28d, 0x7fdbaa95, 0x7fdcab15, + 0x7fdda447, 0x7fde9663, 0x7fdf81a1, 0x7fe06634, 0x7fe14450, 0x7fe21c27, + 0x7fe2ede7, 0x7fe3b9c0, 0x7fe47fdd, 0x7fe5406b, 0x7fe5fb93, 0x7fe6b17e, + 0x7fe76252, 0x7fe80e37, 0x7fe8b550, 0x7fe957c1, 0x7fe9f5ae, 0x7fea8f37, + 0x7feb247d, 0x7febb5a0, 0x7fec42bd, 0x7feccbf3, 0x7fed515d, 0x7fedd319, + 0x7fee5140, 0x7feecbed, 0x7fef4339, 0x7fefb73d, 0x7ff02810, 0x7ff095ca, + 0x7ff10081, 0x7ff1684a, 0x7ff1cd3c, 0x7ff22f6a, 0x7ff28ee8, 0x7ff2ebca, + 0x7ff34621, 0x7ff39e02, 0x7ff3f37c, 0x7ff446a2, 0x7ff49783, 0x7ff4e631, + 0x7ff532ba, 0x7ff57d2e, 0x7ff5c59b, 0x7ff60c11, 0x7ff6509d, 0x7ff6934c, + 0x7ff6d42c, 0x7ff7134a, 0x7ff750b2, 0x7ff78c6f, 0x7ff7c68f, 0x7ff7ff1b, + 0x7ff83620, 0x7ff86ba8, 0x7ff89fbd, 0x7ff8d26a, 0x7ff903b8, 0x7ff933b1, + 0x7ff9625f, 0x7ff98fca, 0x7ff9bbfb, 0x7ff9e6fc, 0x7ffa10d3, 0x7ffa398a, + 0x7ffa6128, 0x7ffa87b4, 0x7ffaad37, 0x7ffad1b8, 0x7ffaf53c, 0x7ffb17cc, + 0x7ffb396f, 0x7ffb5a29, 0x7ffb7a02, 0x7ffb9900, 0x7ffbb729, 0x7ffbd482, + 0x7ffbf112, 0x7ffc0cdd, 0x7ffc27e9, 0x7ffc423b, 0x7ffc5bd8, 0x7ffc74c6, + 0x7ffc8d08, 0x7ffca4a3, 0x7ffcbb9d, 0x7ffcd1f8, 0x7ffce7ba, 0x7ffcfce7, + 0x7ffd1183, 0x7ffd2591, 0x7ffd3916, 0x7ffd4c14, 0x7ffd5e91, 0x7ffd708e, + 0x7ffd8211, 0x7ffd931b, 0x7ffda3b1, 0x7ffdb3d5, 0x7ffdc38a, 0x7ffdd2d3, + 0x7ffde1b4, 0x7ffdf02f, 0x7ffdfe47, 0x7ffe0bff, 0x7ffe1958, 0x7ffe2656, + 0x7ffe32fb, 0x7ffe3f49, 0x7ffe4b44, 0x7ffe56ec, 0x7ffe6244, 0x7ffe6d4f, + 0x7ffe780e, 0x7ffe8284, 0x7ffe8cb2, 0x7ffe969b, 0x7ffea03f, 0x7ffea9a2, + 0x7ffeb2c5, 0x7ffebba9, 0x7ffec450, 0x7ffeccbc, 0x7ffed4ef, 0x7ffedce9, + 0x7ffee4ad, 0x7ffeec3c, 0x7ffef397, 0x7ffefac0}; + +static int32_t integrator_rep_lut[] = { + 0x00, 0x72, 0xe9, 0x162, 0x1df, 0x25f, 0x2e2, + 0x369, 0x3f4, 0x483, 0x515, 0x5ac, 0x647, 0x6e6, + 0x789, 0x831, 0x8dd, 0x98e, 0xa44, 0xaff, 0xbbf, + 0xc84, 0xd4f, 0xe1f, 0xef5, 0xfd1, 0x10b3, 0x119b, + 0x1289, 0x137e, 0x147a, 0x157c, 0x1686, 0x1796, 0x18af, + 0x19cf, 0x1af7, 0x1c26, 0x1d5f, 0x1e9f, 0x1fe9, 0x213c, + 0x2298, 0x23fd, 0x256c, 0x26e5, 0x2869, 0x29f7, 0x2b90, + 0x2d35, 0x2ee4, 0x30a0, 0x3268, 0x343c, 0x361d, 0x380b, + 0x3a07, 0x3c11, 0x3e29, 0x404f, 0x4285, 0x44ca, 0x471f, + 0x4985, 0x4bfb, 0x4e82, 0x511b, 0x53c7, 0x5685, 0x5956, + 0x5c3b, 0x5f35, 0x6243, 0x6567, 0x68a0, 0x6bf1, 0x6f58, + 0x72d8, 0x766f, 0x7a20, 0x7deb, 0x81d1, 0x85d2, 0x89ee, + 0x8e28, 0x927f, 0x96f5, 0x9b8a, 0xa03f, 0xa515, 0xaa0d, + 0xaf28, 0xb467, 0xb9ca, 0xbf53, 0xc503, 0xcadb, 0xd0dc, + 0xd706, 0xdd5c, 0xe3df, 0xea8f, 0xf16e, 0xf87d, 0xffbd, + 0x10731, 0x10ed8, 0x116b6, 0x11eca, 0x12717, 0x12f9e, 0x13861, + 0x14161, 0x14aa1, 0x15421, 0x15de4, 0x167eb, 0x17239, 0x17cce, + 0x187ae, 0x192da, 0x19e55, 0x1aa20, 0x1b63d, 0x1c2b0, 0x1cf7a, + 0x1dc9d, 0x1ea1c, 0x1f7fa, 0x2063a, 0x214dd, 0x223e6, 0x23359, + 0x24339, 0x25387, 0x26448, 0x2757e, 0x2872d, 0x29958, 0x2ac02, + 0x2bf2f, 0x2d2e3, 0x2e720, 0x2fbeb, 0x31148, 0x3273b, 0x33dc8, + 0x354f3, 0x36cc0, 0x38533, 0x39e53, 0x3b822, 0x3d2a6, 0x3ede4, + 0x409e1, 0x426a2, 0x4442d, 0x46287, 0x481b5, 0x4a1be, 0x4c2a8, + 0x4e478, 0x50735, 0x52ae6, 0x54f91, 0x5753c, 0x59bf0, 0x5c3b3, + 0x5ec8d, 0x61685, 0x641a3, 0x66df0, 0x69b73, 0x6ca34, 0x6fa3e, + 0x72b98, 0x75e4c, 0x79264, 0x7c7e9, 0x7fee4, 0x83761, 0x8716a, + 0x8ad0a, 0x8ea4c, 0x9293b, 0x969e3, 0x9ac50, 0x9f08f, 0xa36ac, + 0xa7eb4, 0xac8b6, 0xb14be, 0xb62db, 0xbb31b, 0xc058e, 0xc5a43, + 0xcb149, 0xd0ab1, 0xd668c, 0xdc4eb, 0xe25df, 0xe897b, 0xeefd1, + 0xf58f4, 0xfc4f8, 0x1033f1, 0x10a5f4, 0x111b16, 0x11936c, 0x120f0f, + 0x128e13, 0x131092, 0x1396a4, 0x142061, 0x14ade4, 0x153f46, 0x15d4a3, + 0x166e18, 0x170bbf, 0x17adb8, 0x185420, 0x18ff16, 0x19aebb, 0x1a632e, + 0x1b1c93, 0x1bdb0a, 0x1c9eb9, 0x1d67c3, 0x1e364e, 0x1f0a80, 0x1fe482, + 0x20c47c, 0x21aa98, 0x229700, 0x2389e1, 0x248369, 0x2583c5, 0x268b27, + 0x2799be, 0x28afbd, 0x29cd5a, 0x2af2c7, 0x2c203e, 0x2d55f5, 0x2e9427, + 0x2fdb0f, 0x312aeb, 0x3283f8, 0x33e678, 0x3552ac, 0x36c8d9, 0x384944, + 0x39d436, 0x3b69f8, 0x3d0ad5, 0x3eb71d}; + +static int32_t integrator_short_rep_lut[] = { + 0x00, 0x05, 0x0b, 0x11, 0x17, 0x1d, 0x24, 0x2a, + 0x31, 0x38, 0x3f, 0x47, 0x4e, 0x56, 0x5e, 0x67, + 0x6f, 0x78, 0x81, 0x8a, 0x93, 0x9d, 0xa7, 0xb1, + 0xbc, 0xc6, 0xd2, 0xdd, 0xe9, 0xf5, 0x101, 0x10e, + 0x11b, 0x128, 0x136, 0x144, 0x153, 0x162, 0x171, 0x181, + 0x191, 0x1a1, 0x1b3, 0x1c4, 0x1d6, 0x1e9, 0x1fc, 0x20f, + 0x223, 0x238, 0x24d, 0x263, 0x279, 0x290, 0x2a8, 0x2c0, + 0x2d9, 0x2f3, 0x30d, 0x328, 0x344, 0x361, 0x37e, 0x39c, + 0x3bb, 0x3db, 0x3fb, 0x41d, 0x440, 0x463, 0x487, 0x4ad, + 0x4d3, 0x4fb, 0x523, 0x54d, 0x578, 0x5a4, 0x5d1, 0x5ff, + 0x62f, 0x660, 0x692, 0x6c6, 0x6fb, 0x732, 0x76a, 0x7a3, + 0x7df, 0x81b, 0x85a, 0x89a, 0x8dc, 0x920, 0x965, 0x9ad, + 0x9f6, 0xa42, 0xa8f, 0xadf, 0xb31, 0xb85, 0xbdb, 0xc34, + 0xc8f, 0xced, 0xd4d, 0xdb0, 0xe16, 0xe7e, 0xeea, 0xf58, + 0xfc9, 0x103d, 0x10b5, 0x112f, 0x11ad, 0x122f, 0x12b4, 0x133d, + 0x13c9, 0x145a, 0x14ee, 0x1586, 0x1623, 0x16c4, 0x1769, 0x1813, + 0x18c1, 0x1974, 0x1a2c, 0x1ae9, 0x1bac, 0x1c73, 0x1d40, 0x1e13, + 0x1eeb, 0x1fca, 0x20ae, 0x2199, 0x228a, 0x2382, 0x2480, 0x2586, + 0x2692, 0x27a6, 0x28c2, 0x29e5, 0x2b11, 0x2c44, 0x2d80, 0x2ec5, + 0x3012, 0x3169, 0x32c9, 0x3432, 0x35a6, 0x3723, 0x38ab, 0x3a3e, + 0x3bdc, 0x3d85, 0x3f3a, 0x40fb, 0x42c8, 0x44a2, 0x4688, 0x487c, + 0x4a7e, 0x4c8e, 0x4eac, 0x50d9, 0x5315, 0x5561, 0x57be, 0x5a2a, + 0x5ca8, 0x5f37, 0x61d8, 0x648b, 0x6752, 0x6a2b, 0x6d19, 0x701b, + 0x7333, 0x7660, 0x79a3, 0x7cfd, 0x806f, 0x83f9, 0x879b, 0x8b58, + 0x8f2e, 0x931f, 0x972c, 0x9b55, 0x9f9b, 0xa400, 0xa883, 0xad25, + 0xb1e9, 0xb6cd, 0xbbd4, 0xc0fe, 0xc64c, 0xcbc0, 0xd15a, 0xd71a, + 0xdd04, 0xe316, 0xe954, 0xefbd, 0xf653, 0xfd17, 0x1040a, 0x10b2e, + 0x11285, 0x11a0e, 0x121cd, 0x129c2, 0x131ee, 0x13a54, 0x142f5, 0x14bd2, + 0x154ed, 0x15e48, 0x167e5, 0x171c5, 0x17bea, 0x18657, 0x1910c, 0x19c0d, + 0x1a75a, 0x1b2f7, 0x1bee5, 0x1cb27, 0x1d7bf, 0x1e4af, 0x1f1fa, 0x1ffa1, + 0x20da9, 0x21c13, 0x22ae2, 0x23a18, 0x249ba, 0x259c9, 0x26a48, 0x27b3b, + 0x28ca5, 0x29e89, 0x2b0ea, 0x2c3cc, 0x2d732, 0x2eb21, 0x2ff9b, 0x314a4}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/cpu/src/apps/system23/lut.c b/cpu/src/apps/system23/lut.c new file mode 100644 index 00000000..aba2f227 --- /dev/null +++ b/cpu/src/apps/system23/lut.c @@ -0,0 +1,19 @@ + +#include "lut.h" +#include "param_scale.h" + +int32_t g_cutoff_lut[128]; +int32_t g_resonance_lut[256]; + +void lut_init() { + // float_to_fract32(1.0 - (value / 255.0f)) + + for (int i = 0; i < 128; i++) { + g_cutoff_lut[i] = float_to_fix16(cv_to_filter_freq_oversample(note_to_cv(i))); + } + + for (int i = 0; i <= 255; i++) { + g_resonance_lut[i] = 0x7fffffff - (i * (1 << 23)); + } + +} \ No newline at end of file diff --git a/cpu/src/apps/system23/lut.h b/cpu/src/apps/system23/lut.h new file mode 100644 index 00000000..196d730c --- /dev/null +++ b/cpu/src/apps/system23/lut.h @@ -0,0 +1,10 @@ +#ifndef LUT_H +#define LUT_H +#include + +extern int32_t g_cutoff_lut[128]; +extern int32_t g_resonance_lut[256]; + +void lut_init(); + +#endif \ No newline at end of file diff --git a/cpu/src/apps/system23/param_scale.h b/cpu/src/apps/system23/param_scale.h new file mode 100644 index 00000000..57d9056a --- /dev/null +++ b/cpu/src/apps/system23/param_scale.h @@ -0,0 +1,172 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file param_scale.h + * + * @brief Public API for parameter scaling. + */ + +#ifndef PARAM_SCALE_H +#define PARAM_SCALE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +#include "lookup_tables.h" + +/*----- Macros -------------------------------------------------------*/ + +// Maximum and minimum float values representable as fract32. +#define FRACT32_MAX_FLOAT 0x0.FFFFFFp0F +#define FRACT32_MIN_FLOAT -1.0F + +#define FIX16_MAX 0x7FFFFFFF +#define FIX16_MIN 0x80000000 +#define FIX16_ONE 0x00010000 + +/// TODO: These could probably be optimised. +/// Aleph_Phasor takes normalised frequency in Hz, 16.16 format. +/// Aleph_FilterSVF appears to want that converted to radians. +/// Our CPU side module_set_param function expects all values +/// as floating point between -1.0 and 1.0. + +// Normalise frequency, 0x7fffffff / (48000 << 16) +#define OSC_FREQ_CONST 0.6826667 + +// Convert to radians. +#define FILTER_FREQ_CONST (OSC_FREQ_CONST * 2 * M_PI) + +// Half frequency as 2 times oversampled. +#define FILTER_FREQ_OVERSAMPLE_CONST (OSC_FREQ_CONST * M_PI) + +#define CONCERT_PITCH_HZ 440.0 +#define CONCERT_PITCH_MIDI 69.0 + +#define CV_CENTRE_FREQ 27.5 + +/*----- Typedefs -----------------------------------------------------*/ + +/*----- Extern variable declarations ---------------------------------*/ + +/*----- Static variable definitions ----------------------------------*/ + +/*----- Extern function prototypes -----------------------------------*/ + +/*----- Extern function implementations ------------------------------*/ + +static inline float clamp_value(float value) { + + return fmaxf(fminf(value, FRACT32_MAX_FLOAT), FRACT32_MIN_FLOAT); +} + +static inline int32_t float_to_fract32(float value) { + + int32_t result; + + if (value == 0) { + result = 0; + + } else { + result = (int32_t)roundf(scalbnf(clamp_value(value), 31)); + } + + return result; +} + +static inline int32_t float_to_fix16(float value) { + + int32_t result; + + if (value == 0) { + result = 0; + + } else { + result = (int32_t)(value * FIX16_ONE); + } + + return result; +} + +static inline float note_to_freq(float note) { + + return CONCERT_PITCH_HZ * powf(2.0, ((note - CONCERT_PITCH_MIDI) / 12.0)); +} + +static inline float freq_to_cv(float freq) { + + // 0.1 per octave, 0 == 27.5 Hz (A0). + return (logf(freq / CV_CENTRE_FREQ) / logf(2.0)) / 10; +} + +static inline float note_to_cv(float note) { + + return freq_to_cv(note_to_freq(note)); +} + +static inline float cv_to_freq(float cv) { + + return powf(2.0, cv * 10) * CV_CENTRE_FREQ; +} + +static float cv_to_osc_freq(float cv) { + + return cv_to_freq(cv) * OSC_FREQ_CONST; +} + +static float cv_to_filter_freq(float cv) { + + return cv_to_freq(cv) * FILTER_FREQ_CONST; +} + +static float cv_to_filter_freq_oversample(float cv) { + + return cv_to_freq(cv) * FILTER_FREQ_OVERSAMPLE_CONST; +} + + + +static inline int32_t note_to_fract32(uint8_t note) { + return freq_12tet_lut[note]; +} + + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/apps/system23/sequencer.h b/cpu/src/apps/system23/sequencer.h new file mode 100644 index 00000000..fb17bcd7 --- /dev/null +++ b/cpu/src/apps/system23/sequencer.h @@ -0,0 +1,8 @@ +#ifndef SEQUENCER_H +#define SEQUENCER_H + +#define GATE_4TH ((SAMPLERATE * 60) / (BPM * 4)) +#define GATE_8TH ((SAMPLERATE * 60) / (BPM * 8)) +#define GATE_16TH ((SAMPLERATE * 60) / (BPM * 16)) + +#endif \ No newline at end of file diff --git a/cpu/src/apps/system23/system23.c b/cpu/src/apps/system23/system23.c new file mode 100644 index 00000000..0f08a107 --- /dev/null +++ b/cpu/src/apps/system23/system23.c @@ -0,0 +1,253 @@ +#include "freetribe.h" + +#include "sequencer.h" +#include "param_scale.h" +#include "lut.h" + + +#define TRIGGER_MODE_CONTINUOUS 1 +#define SAMPLERATE 48000 +#define BPM 150 +#define BEAT_1_4 (60000 / (BPM * 4)) +#define BUTTON_EXIT 0x0D +#define BUTTON_BAR_0 0x1C +#define BUTTON_BAR_1 0x1D +#define BUTTON_BAR_2 0x1E +#define BUTTON_BAR_3 0x1F + +#define KNOB_LEVEL 0x00 +#define KNOB_PITCH 0x02 +#define KNOB_RESONANCE 0x03 +#define KNOB_EG 0x04 +#define KNOB_ATTACK 0x06 +#define KNOB_DECAY 0x08 +#define KNOB_MOD_DEPTH 0x05 +#define KNOB_MOD_SPEED 0x0a + +#define ENCODER_OSC 0x01 +#define ENCODER_CUTOFF 0x02 +#define ENCODER_MOD 0x03 + +#define PARAM_NOTE_FREQ 2 +#define PARAM_GATE_TICKS 3 +#define PARAM_NEXT_NOTE_FREQ 4 +#define PARAM_TICKS_TIL_NEXT 5 // the number of DSP frames it takes to get to the next note after the current one's gate ends +#define PARAM_CUTOFF 10 +#define PARAM_RESONANCE 11 + +const int32_t NOTE_FREQS[128] = { + 0x00077340, 0x0007E4AA, 0x00085CD1, 0x0008DC1E, 0x000962FD, 0x0009F1E0, 0x000A8943, 0x000B29A6, 0x000BD393, 0x000C879A, + 0x000D4656, 0x000E1069, 0x000EE681, 0x000FC953, 0x0010B9A3, 0x0011B83C, 0x0012C5F9, 0x0013E3C0, 0x00151286, 0x0016534C, + 0x0017A726, 0x00190F34, 0x001A8CAC, 0x001C20D3, 0x001DCD02, 0x001F92A7, 0x00217345, 0x00237078, 0x00258BF2, 0x0027C781, + 0x002A250C, 0x002CA698, 0x002F4E4B, 0x00321E69, 0x00351958, 0x003841A6, 0x003B9A04, 0x003F254E, 0x0042E68B, 0x0046E0F0, + 0x004B17E5, 0x004F8F01, 0x00544A17, 0x00594D31, 0x005E9C96, 0x00643CD2, 0x006A32B1, 0x0070834C, 0x00773407, 0x007E4A9B, + 0x0085CD15, 0x008DC1E1, 0x00962FC9, 0x009F1E03, 0x00A8942E, 0x00B29A62, 0x00BD392D, 0x00C879A3, 0x00D46562, 0x00E10697, + 0x00EE680F, 0x00FC9536, 0x010B9A2B, 0x011B83C2, 0x012C5F93, 0x013E3C06, 0x0151285D, 0x016534C3, 0x017A725A, 0x0190F347, + 0x01A8CAC3, 0x01C20D2F, 0x01DCD01D, 0x01F92A6D, 0x02173456, 0x02370783, 0x0258BF26, 0x027C780B, 0x02A250BA, 0x02CA6987, + 0x02F4E4B4, 0x0321E68D, 0x03519586, 0x03841A5D, 0x03B9A03A, 0x03F254D9, 0x042E68AC, 0x046E0F07, 0x04B17E4B, 0x04F8F017, + 0x0544A173, 0x0594D30D, 0x05E9C968, 0x0643CD1B, 0x06A32B0D, 0x070834BA, 0x07734075, 0x07E4A9B2, 0x085CD157, 0x08DC1E0D, + 0x0962FC96, 0x09F1E02D, 0x0A8942E7, 0x0B29A61A, 0x0BD392D0, 0x0C879A35, 0x0D46561A, 0x0E106974, 0x0EE680E9, 0x0FC95364, + 0x10B9A2AF, 0x11B83C1A, 0x12C5F92C, 0x13E3C05A, 0x151285CE, 0x16534C35, 0x17A725A0, 0x190F346A, 0x1A8CAC34, 0x1C20D2E8, + 0x1DCD01D3, 0x1F92A6C8, 0x2173455E, 0x23707835, 0x258BF259, 0x27C780B5, 0x2A250B9C, 0x2CA6986A +}; + +typedef struct { + uint32_t gate; + uint8_t note; + bool slide; + bool accent; +} t_step; + +static uint32_t g_beat_ticks; +static uint32_t g_old_systicks; +static uint8_t g_step = 0; // position within current bar +static uint8_t g_total_steps = 0; +static uint8_t g_bar = 0; +static uint8_t g_sequencer_num_steps = 8; +static uint8_t g_selected_bar = 0; +static t_step g_sequencer_steps[64] = { + { GATE_4TH, 57+0, false, false }, + { GATE_16TH, 57+1, false, false }, + { GATE_4TH, 57+1 , false, true }, + { GATE_4TH, 57+0, false, false }, + { GATE_16TH, 57+12, false, false }, + { GATE_4TH, 57+24 , false, false }, + { GATE_8TH, 57+13, false, false }, + { GATE_4TH, 57+25 , false, false }, +}; + +static void _trigger_callback(uint8_t pad, uint8_t vel, bool state); +static void _button_callback(uint8_t index, bool state); +static void _knob_callback(uint8_t index, uint8_t value); +static void _encoder_callback(uint8_t index, uint8_t value); +static void _tick_callback(void); +static void _update_sequencer_display(); + + +/** + * @brief Initialise application. + * + * @return status Status code indicating success: + * - SUCCESS + * - WARNING + * - ERROR + */ +t_status app_init(void) { + + t_status status = ERROR; + + g_old_systicks = systick_get(); // reset beat timer + ft_register_tick_callback(0, _tick_callback); + ft_register_panel_callback(TRIGGER_EVENT, _trigger_callback); + ft_register_panel_callback(BUTTON_EVENT, _button_callback); + ft_register_panel_callback(ENCODER_EVENT, _encoder_callback); + ft_register_panel_callback(KNOB_EVENT, _knob_callback); + // ft_set_trigger_mode(TRIGGER_MODE_CONTINUOUS); // wtf is this + + lut_init(); + ft_print("System23 initialised.\n"); + + + status = SUCCESS; + return status; +} + +/** + * @brief Run application. + */ +void app_run(void) { + + _update_sequencer_display(); + +} + +/*----- Static function implementations ------------------------------*/ + +static void _trigger_callback(uint8_t pad, uint8_t vel, bool state) { + + // ft_printf("trigger callback: %02X %u %u", pad, vel, state); + + uint8_t step_index = pad + (g_selected_bar * 16); + if (g_sequencer_steps[step_index].gate > 0) { + g_sequencer_steps[step_index].gate = 0; // note is off + } else { + g_sequencer_steps[step_index].gate = GATE_4TH; // note is on + } + +} + +static void _knob_callback(uint8_t index, uint8_t value) { + + // char text[64]; + // sprintf(text, "knob callback: %02X %02X", index, value); + // ft_print(text); + + switch (index) { + case KNOB_RESONANCE: + ft_set_module_param(0, PARAM_RESONANCE, g_resonance_lut[value]); + break; + } +} + +static void _encoder_callback(uint8_t index, uint8_t value) { + static uint8_t cutoff = 0x7f; + + switch (index) { + + case ENCODER_CUTOFF: + if (value == 0x01) { + if (cutoff < 0x7f) + cutoff++; + } else { + if (cutoff > 0) + cutoff--; + } + // char text[64]; + // sprintf(text, "encoder callback: %02X %f", cutoff, note_to_cv(cutoff)); + // ft_print(text); + ft_set_module_param(0, PARAM_CUTOFF, g_cutoff_lut[cutoff]); + break; + + } + +} + +static void _button_callback(uint8_t index, bool state) { + ft_printf("button callback: %u=%u", index, state); + + switch (index) { + case BUTTON_EXIT: ft_shutdown(); break; + case BUTTON_BAR_0: g_selected_bar = 0; break; + case BUTTON_BAR_1: g_selected_bar = 1; break; + case BUTTON_BAR_2: g_selected_bar = 2; break; + case BUTTON_BAR_3: g_selected_bar = 3; break; + } +} + +static void _tick_callback(void) { + + g_beat_ticks += systick_get() - g_old_systicks; + g_old_systicks = systick_get(); + if (g_beat_ticks > BEAT_1_4) { + g_beat_ticks = 0; + + // Play note + uint8_t step = 16 * g_bar + g_step; + if (g_sequencer_steps[step].gate > 0) { + ft_set_module_param(0, PARAM_NOTE_FREQ, NOTE_FREQS[g_sequencer_steps[step].note]); + ft_set_module_param(0, PARAM_GATE_TICKS, g_sequencer_steps[step].gate); + uint8_t next_step = (step+1)%(sizeof(g_sequencer_steps)/sizeof(t_step)); + int32_t ticks_til_next_gate = GATE_4TH - g_sequencer_steps[step].gate; + ft_set_module_param(0, PARAM_TICKS_TIL_NEXT, ticks_til_next_gate); + ft_set_module_param(0, PARAM_NEXT_NOTE_FREQ, NOTE_FREQS[g_sequencer_steps[next_step].note]); + } + + g_step++; + if (g_step >= 16) { + g_step = 0; + g_bar++; + if (g_bar > 3) { + g_bar = 0; + } + } + + // if the pattern is smaller than 64, we gotta reset the sequencer position + g_total_steps++; + if (g_total_steps >= g_sequencer_num_steps) { + g_total_steps = 0; + g_bar = 0; + g_step = 0; + } + + } +} + + +static void _update_sequencer_display() { + + // step in pad + if (g_bar == g_selected_bar) { + for (uint8_t i = 0; i <= 15; i++) { + if (i != g_step) + ft_set_led(LED_PAD_0_BLUE + (i<<1), false); + } + ft_set_led(LED_PAD_0_BLUE + (g_step<<1), true); + } else { + for (uint8_t i = 0; i <= 15; i++) + ft_set_led(LED_PAD_0_BLUE + (i<<1), false); + } + + // draw steps + for (uint8_t i = 0; i <= 15; i++) { + bool has_note = g_sequencer_steps[16 * g_selected_bar + i].gate > 0; + ft_set_led(LED_PAD_0_RED + (i<<1), has_note); + } + + // bars + for (uint8_t i = 0; i <= 3; i++) { + ft_set_led(LED_BAR_0_BLUE + i, (i == g_bar)); + } + for (uint8_t i = 0; i <= 3; i++) { + ft_set_led(LED_BAR_0_RED + i, (i == g_selected_bar)); + } +} diff --git a/cpu/src/apps/system23/utils/tinyprintf.c b/cpu/src/apps/system23/utils/tinyprintf.c new file mode 100644 index 00000000..bb22700a --- /dev/null +++ b/cpu/src/apps/system23/utils/tinyprintf.c @@ -0,0 +1,521 @@ +/* +File: tinyprintf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "tinyprintf.h" + + +/* + * Configuration + */ + +/* Enable long int support */ +#define PRINTF_LONG_SUPPORT + +/* Enable long long int support (implies long int support) */ +#define PRINTF_LONG_LONG_SUPPORT + +/* Enable %z (size_t) support */ +#define PRINTF_SIZE_T_SUPPORT + +/* + * Configuration adjustments + */ +#ifdef PRINTF_SIZE_T_SUPPORT +#include +#endif + +#ifdef PRINTF_LONG_LONG_SUPPORT +# define PRINTF_LONG_SUPPORT +#endif + +/* __SIZEOF___ defined at least by gcc */ +#ifdef __SIZEOF_POINTER__ +# define SIZEOF_POINTER __SIZEOF_POINTER__ +#endif +#ifdef __SIZEOF_LONG_LONG__ +# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__ +#endif +#ifdef __SIZEOF_LONG__ +# define SIZEOF_LONG __SIZEOF_LONG__ +#endif +#ifdef __SIZEOF_INT__ +# define SIZEOF_INT __SIZEOF_INT__ +#endif + +#ifdef __GNUC__ +# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline)) +#else +# define _TFP_GCC_NO_INLINE_ +#endif + +/* + * Implementation + */ +struct param { + char lz:1; /**< Leading zeros */ + char alt:1; /**< alternate form */ + char uc:1; /**< Upper case (for base16 only) */ + char align_left:1; /**< 0 == align right (default), 1 == align left */ + unsigned int width; /**< field width */ + char sign; /**< The sign to display (if any) */ + unsigned int base; /**< number base (e.g.: 8, 10, 16) */ + char *bf; /**< Buffer to output */ +}; + + +#ifdef PRINTF_LONG_LONG_SUPPORT +static void _TFP_GCC_NO_INLINE_ ulli2a( + unsigned long long int num, struct param *p) +{ + int n = 0; + unsigned long long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void lli2a(long long int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ulli2a(num, p); +} +#endif + +#ifdef PRINTF_LONG_SUPPORT +static void uli2a(unsigned long int num, struct param *p) +{ + int n = 0; + unsigned long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + uli2a(num, p); +} +#endif + +static void ui2a(unsigned int num, struct param *p) +{ + int n = 0; + unsigned int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ui2a(num, p); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2u(char ch, const char **src, int base, unsigned int *nump) +{ + const char *p = *src; + unsigned int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, struct param *p) +{ + char ch; + int n = p->width; + char *bf = p->bf; + + /* Number of filling characters */ + while (*bf++ && n > 0) + n--; + if (p->sign) + n--; + if (p->alt && p->base == 16) + n -= 2; + else if (p->alt && p->base == 8) + n--; + + /* Fill with space to align to the right, before alternate or sign */ + if (!p->lz && !p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } + + /* print sign */ + if (p->sign) + putf(putp, p->sign); + + /* Alternate */ + if (p->alt && p->base == 16) { + putf(putp, '0'); + putf(putp, (p->uc ? 'X' : 'x')); + } else if (p->alt && p->base == 8) { + putf(putp, '0'); + } + + /* Fill with zeros, after alternate or sign */ + if (p->lz) { + while (n-- > 0) + putf(putp, '0'); + } + + /* Put actual buffer */ + bf = p->bf; + while ((ch = *bf++)) + putf(putp, ch); + + /* Fill with space to align to the left, after string */ + if (!p->lz && p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } +} + +void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) +{ + struct param p; +#ifdef PRINTF_LONG_SUPPORT + char bf[23]; /* long = 64b on some architectures */ +#else + char bf[12]; /* int = 32b on some architectures */ +#endif + char ch; + p.bf = bf; + + while ((ch = *(fmt++))) { + if (ch != '%') { + putf(putp, ch); + } else { +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; /* 1 for long, 2 for long long */ +#endif + /* Init parameter struct */ + p.lz = 0; + p.alt = 0; + p.width = 0; + p.align_left = 0; + p.sign = 0; + + /* Flags */ + while ((ch = *(fmt++))) { + switch (ch) { + case '-': + p.align_left = 1; + continue; + case '0': + p.lz = 1; + continue; + case '#': + p.alt = 1; + continue; + default: + break; + } + break; + } + + /* Width */ + if (ch >= '0' && ch <= '9') { + ch = a2u(ch, &fmt, 10, &(p.width)); + } + + /* We accept 'x.y' format but don't support it completely: + * we ignore the 'y' digit => this ignores 0-fill + * size and makes it == width (ie. 'x') */ + if (ch == '.') { + p.lz = 1; /* zero-padding */ + /* ignore actual 0-fill size: */ + do { + ch = *(fmt++); + } while ((ch >= '0') && (ch <= '9')); + } + +#ifdef PRINTF_SIZE_T_SUPPORT +# ifdef PRINTF_LONG_SUPPORT + if (ch == 'z') { + ch = *(fmt++); + if (sizeof(size_t) == sizeof(unsigned long int)) + lng = 1; +# ifdef PRINTF_LONG_LONG_SUPPORT + else if (sizeof(size_t) == sizeof(unsigned long long int)) + lng = 2; +# endif + } else +# endif +#endif + +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 1; +#ifdef PRINTF_LONG_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 2; + } +#endif + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u': + p.base = 10; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'd': + case 'i': + p.base = 10; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + lli2a(va_arg(va, long long int), &p); + else +#endif + if (1 == lng) + li2a(va_arg(va, long int), &p); + else +#endif + i2a(va_arg(va, int), &p); + putchw(putp, putf, &p); + break; +#ifdef SIZEOF_POINTER + case 'p': + p.alt = 1; +# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT + lng = 0; +# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG + lng = 1; +# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG + lng = 2; +# endif +#endif + case 'x': + case 'X': + p.base = 16; + p.uc = (ch == 'X')?1:0; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'o': + p.base = 8; + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + p.bf = va_arg(va, char *); + putchw(putp, putf, &p); + p.bf = bf; + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } + abort:; +} + +#if TINYPRINTF_DEFINE_TFP_PRINTF +static putcf stdout_putf; +static void *stdout_putp; + +void init_printf(void *putp, putcf putf) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} +#endif + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +struct _vsnprintf_putcf_data +{ + size_t dest_capacity; + char *dest; + size_t num_chars; +}; + +static void _vsnprintf_putcf(void *p, char c) +{ + struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p; + if (data->num_chars < data->dest_capacity) + data->dest[data->num_chars] = c; + data->num_chars ++; +} + +int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + struct _vsnprintf_putcf_data data; + + if (size < 1) + return 0; + + data.dest = str; + data.dest_capacity = size-1; + data.num_chars = 0; + tfp_format(&data, _vsnprintf_putcf, format, ap); + + if (data.num_chars < data.dest_capacity) + data.dest[data.num_chars] = '\0'; + else + data.dest[data.dest_capacity] = '\0'; + + return data.num_chars; +} + +int tfp_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsnprintf(str, size, format, ap); + va_end(ap); + return retval; +} + +struct _vsprintf_putcf_data +{ + char *dest; + size_t num_chars; +}; + +static void _vsprintf_putcf(void *p, char c) +{ + struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p; + data->dest[data->num_chars++] = c; +} + +int tfp_vsprintf(char *str, const char *format, va_list ap) +{ + struct _vsprintf_putcf_data data; + data.dest = str; + data.num_chars = 0; + tfp_format(&data, _vsprintf_putcf, format, ap); + data.dest[data.num_chars] = '\0'; + return data.num_chars; +} + +int tfp_sprintf(char *str, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsprintf(str, format, ap); + va_end(ap); + return retval; +} +#endif diff --git a/cpu/src/apps/system23/utils/tinyprintf.h b/cpu/src/apps/system23/utils/tinyprintf.h new file mode 100644 index 00000000..a769f4a8 --- /dev/null +++ b/cpu/src/apps/system23/utils/tinyprintf.h @@ -0,0 +1,186 @@ +/* +File: tinyprintf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'. + +They provide a simple and small (+400 loc) printf functionality to +be used in embedded systems. + +I've found them so useful in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and the 'sprintf' family of +functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf'). + +The formats supported by this implementation are: +'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then +the long specifier is also supported. Note that this will pull in some +long math routines (pun intended!) and thus make your executable +noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the +long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t +specifier. + +The memory footprint of course depends on the target CPU, compiler and +compiler options, but a rough guesstimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your mileage may vary. By hacking the source code you can +get rid of some hundred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf, you need to supply your own character output function, +something like : + +void putc ( void* p, char c) +{ + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; +} + +Before you can call printf, you need to initialize it to use your +character output function with something like: + +init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it is safe +to call it from interrupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set +(default). Setting it to 0 makes it possible to use them along with +'stdio.h' printf's in a single source file. When +'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are +not function-like macros, so if you have variables or struct members +with these names, things will explode in your face. Without variadic +macros this is the best we can do to wrap these function. If it is a +problem, just give up the macros and use the functions directly, or +rename them. + +It is also possible to avoid defining tfp_printf and/or tfp_sprintf by +clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or +'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to +export only tfp_format, which is at the core of all the other +functions. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +/* Global configuration */ + +/* Set this to 0 if you do not want to provide tfp_printf */ +#ifndef TINYPRINTF_DEFINE_TFP_PRINTF +# define TINYPRINTF_DEFINE_TFP_PRINTF 1 +#endif + +/* Set this to 0 if you do not want to provide + tfp_sprintf/snprintf/vsprintf/vsnprintf */ +#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF +# define TINYPRINTF_DEFINE_TFP_SPRINTF 1 +#endif + +/* Set this to 0 if you do not want tfp_printf and + tfp_{vsn,sn,vs,s}printf to be also available as + printf/{vsn,sn,vs,s}printf */ +#ifndef TINYPRINTF_OVERRIDE_LIBC +# define TINYPRINTF_OVERRIDE_LIBC 1 +#endif + +/* Optional external types dependencies */ + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +# include /* size_t */ +#endif + +/* Declarations */ + +#ifdef __GNUC__ +# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \ + __attribute__((format (printf, fmt_idx, arg1_idx))) +#else +# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*putcf) (void *, char); + +/* + 'tfp_format' really is the central function for all tinyprintf. For + each output character after formatting, the 'putf' callback is + called with 2 args: + - an arbitrary void* 'putp' param defined by the user and + passed unmodified from 'tfp_format', + - the character. + The 'tfp_printf' and 'tfp_sprintf' functions simply define their own + callback and pass to it the right 'putp' it is expecting. +*/ +void tfp_format(void *putp, putcf putf, const char *fmt, va_list va); + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap); +int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \ + _TFP_SPECIFY_PRINTF_FMT(3, 4); +int tfp_vsprintf(char *str, const char *fmt, va_list ap); +int tfp_sprintf(char *str, const char *fmt, ...) \ + _TFP_SPECIFY_PRINTF_FMT(2, 3); +# if TINYPRINTF_OVERRIDE_LIBC +# define vsnprintf tfp_vsnprintf +# define snprintf tfp_snprintf +# define vsprintf tfp_vsprintf +# define sprintf tfp_sprintf +# endif +#endif + +#if TINYPRINTF_DEFINE_TFP_PRINTF +void init_printf(void *putp, putcf putf); +void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2); +# if TINYPRINTF_OVERRIDE_LIBC +# define printf tfp_printf +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpu/src/kernel/device/dev_board.c b/cpu/src/kernel/device/dev_board.c index eb58e796..f163b24a 100644 --- a/cpu/src/kernel/device/dev_board.c +++ b/cpu/src/kernel/device/dev_board.c @@ -42,6 +42,7 @@ under the terms of the GNU Affero General Public License as published by #include "per_pinmux.h" #include "per_spi.h" #include "per_uart.h" +#include "per_emifa.h" #include "dev_board.h" @@ -93,49 +94,51 @@ void dev_board_init(void) { /// TODO: Move up to svc_system. dev_flash_init(); -} -/*----- Static function implementations ------------------------------*/ + per_emifa_init(); +} /* - * @brief Shut down periperals. - * - * Returns main board to unitialised state, - * allowing factory SBL and APP to boot. - * - * @param none - * - * @return none - * - */ +* @brief Shut down periperals. +* +* Returns main board to unitialised state, +* allowing factory SBL and APP to boot. +* +* @param none +* +* @return none +* +*/ void dev_board_terminate(void) { // Allows handing off to factory bootloader. // Factory SBL will hang if DDR already initialised. per_ddr_terminate(); - + // Flush TRS MIDI before terminating MCU. per_uart_terminate(1); per_uart_terminate(0); - + _hardware_terminate(); } void dev_board_power_off(void) { per_gpio_set(7, 14, 0); } +/*----- Static function implementations ------------------------------*/ + /* - * @brief Set GPIO to initialise board hardware? - * - * @param none - * - * @return none - */ +* @brief Set GPIO to initialise board hardware? +* +* @param none +* +* @return none +*/ static void _hardware_init(void) { - + /// TODO: What does each pin represent? - + // MCU out of reset. per_gpio_set_indexed(105, 1); // Set GP6P8 - + per_gpio_set_indexed(103, 1); // Set GP6P6 // Only red lights if not set. diff --git a/cpu/src/kernel/device/dev_dsp.c b/cpu/src/kernel/device/dev_dsp.c index d81834ea..1a307299 100644 --- a/cpu/src/kernel/device/dev_dsp.c +++ b/cpu/src/kernel/device/dev_dsp.c @@ -43,6 +43,7 @@ under the terms of the GNU Affero General Public License as published by #include "per_gpio.h" #include "per_spi.h" +#include "per_emifa.h" #include "dev_dsp.h" @@ -127,8 +128,6 @@ void dev_dsp_init(void) { // _dsp_spi_init(); - /// TODO: EMIFA driver. - // // _dsp_emifa_init(); } diff --git a/cpu/src/kernel/device/dev_ipc.c b/cpu/src/kernel/device/dev_ipc.c new file mode 100644 index 00000000..e69de29b diff --git a/cpu/src/kernel/device/dev_ipc.h b/cpu/src/kernel/device/dev_ipc.h new file mode 100644 index 00000000..a6b299cb --- /dev/null +++ b/cpu/src/kernel/device/dev_ipc.h @@ -0,0 +1,7 @@ +#ifndef DEV_IPC_H +#define DEV_IPC_H + +void dev_ipc_init(); +void dev_ipc_send(); + +#endif diff --git a/cpu/src/kernel/peripheral/per_aintc.c b/cpu/src/kernel/peripheral/per_aintc.c index 4b95fd0e..456b62be 100644 --- a/cpu/src/kernel/peripheral/per_aintc.c +++ b/cpu/src/kernel/peripheral/per_aintc.c @@ -36,9 +36,12 @@ under the terms of the GNU Affero General Public License as published by /*----- Includes -----------------------------------------------------*/ +#include "soc_AM1808.h" #include "csl_interrupt.h" +#include "csl_gpio.h" #include "per_aintc.h" +#include "per_gpio.h" /*----- Macros -------------------------------------------------------*/ @@ -72,6 +75,33 @@ void per_aintc_init(void) { IntIRQEnable(); } +void per_aintc_register_gpio_interrupt(uint8_t channel, + uint8_t pin, + uint8_t int_type, + void (*isr)(void)) { + + uint8_t bank = per_gpio_bank_from_pin(pin); + uint8_t system_int = SYS_INT_GPIOB0 + bank; + + GPIODirModeSet(SOC_GPIO_0_REGS, pin, GPIO_DIR_INPUT); + GPIOIntTypeSet(SOC_GPIO_0_REGS, pin, int_type); + GPIOBankIntEnable(SOC_GPIO_0_REGS, bank); + + IntChannelSet(system_int, channel); + IntRegister(system_int, isr); + IntSystemEnable(system_int); + +} + +void per_aintc_clear_status_gpio(uint8_t pin) { + IntSystemStatusClear(SYS_INT_GPIOB0 + per_gpio_bank_from_pin(pin)); + GPIOPinIntClear(SOC_GPIO_0_REGS, pin); +} + +void per_aintc_change_gpio_int_type(uint8_t pin, uint8_t int_type) { + GPIOIntTypeSet(SOC_GPIO_0_REGS, pin, int_type); +} + /*----- Static function implementations ------------------------------*/ /*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_aintc.h b/cpu/src/kernel/peripheral/per_aintc.h index 4984115f..f357138a 100644 --- a/cpu/src/kernel/peripheral/per_aintc.h +++ b/cpu/src/kernel/peripheral/per_aintc.h @@ -37,6 +37,8 @@ under the terms of the GNU Affero General Public License as published by #ifndef PER_AINTC_H #define PER_AINTC_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -53,6 +55,47 @@ extern "C" { void per_aintc_init(void); +/** + * @brief Register interrupt triggered by GPIO pin. + * + * @param channel Channel number for a system interrupt. 0-1 are mapped to FIQ + * and 2-31 are mapped to IRQ of ARM. One or more system interrupts + * can be mapped to one channel. However, one system interrupt + * can not be mapped to multiple channels. + * + * @param pin Indexed GPIO pin number, starting from 1 through 144. + * + * @param int_type How interrupt generation triggers based on electrical signal: + * - GPIO_INT_TYPE_NOEDGE + * - GPIO_INT_TYPE_FALLEDGE + * - GPIO_INT_TYPE_RISEDGE + * - GPIO_INT_TYPE_BOTHEDGE + * + * @param isr Callback function. + */ +void per_aintc_register_gpio_interrupt(uint8_t channel, uint8_t pin, uint8_t int_type, void (*isr)(void)); + +/** + * @brief Clear interrupt status for GPIO interrupts. + * + * @param pin Indexed GPIO pin number, starting from 1 through 144. + */ +void per_aintc_clear_status_gpio(uint8_t pin); + +/** + * @brief Change trigger type of GPIO pin interrupt. + * + * @param pin Indexed GPIO pin number, starting from 1 through 144. + * + * @param int_type How interrupt generation triggers based on electrical signal: + * - GPIO_INT_TYPE_NOEDGE + * - GPIO_INT_TYPE_FALLEDGE + * - GPIO_INT_TYPE_RISEDGE + * - GPIO_INT_TYPE_BOTHEDGE + * + */ +void per_aintc_change_gpio_int_type(uint8_t pin, uint8_t int_type); + #ifdef __cplusplus } #endif diff --git a/cpu/src/kernel/peripheral/per_emifa.c b/cpu/src/kernel/peripheral/per_emifa.c new file mode 100644 index 00000000..55d5dfcf --- /dev/null +++ b/cpu/src/kernel/peripheral/per_emifa.c @@ -0,0 +1,366 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_emifa.c. + * + * @brief Configuration and handling of EMIFA peripheral. + * EMIFA communicates with the DSP's HostDMA engine and has the + * highest bandwidth for transmitting data between CPU and DSP. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +#include "soc_AM1808.h" +#include "csl_interrupt.h" +#include "csl_gpio.h" +#include "csl_emifa.h" +#include "hw_emifa2.h" +#include "hw_types.h" +#include "hw_syscfg0_AM1808.h" +#include "per_emifa.h" +#include "per_aintc.h" +#include "per_gpio.h" +#include "freetribe.h" // ft_printf + +/*----- Macros -------------------------------------------------------*/ + +#ifndef MIN +#define MIN(A,B) ((A) < (B) ? (A) : (B)) +#endif + +#define EMIFA_ASYNC_CFG2 HWREG(SOC_EMIFA_0_REGS + EMIFA_CE2CFG) + +#define HDMA_DATA_PORT ((volatile uint16_t*)(SOC_EMIFA_CS2_ADDR + 0x00)) +#define HDMA_CONFIG_PORT ((volatile uint16_t*)(SOC_EMIFA_CS2_ADDR + 0x02)) + +#define HOST_STATUS_DMA_RDY (1 << 0) +#define HOST_STATUS_FIFOFULL (1 << 1) +#define HOST_STATUS_FIFOEMPTY (1 << 2) +#define HOST_STATUS_DMA_CMPLT (1 << 3) +#define HOST_STATUS_HSHK (1 << 4) +#define HOST_STATUS_HOSTDP_TOUT (1 << 5) +#define HOST_STATUS_HIRQ (1 << 6) +#define HOST_STATUS_ALLOW_CNFG (1 << 7) +#define HOST_STATUS_DMA_DIR (1 << 8) + +#define HOST_CONFIG_WNR (1 << 1) // 0=Host read, 1=Host write +#define HOST_CONFIG_DMA2D (1 << 4) // 0=Linear , 1=Two-dimensional +#define HOST_CONFIG_FLOW (1 << 12) // 0=Stop mode, 1=Autobuffer mode + +/*----- Typedefs -----------------------------------------------------*/ + +/*----- Static function prototypes -----------------------------------*/ + +static t_emifa_status _emifa_config(uint32_t start_address, int word_count, uint16_t extra_flags); +static inline bool _is_dma_ready(); +static inline bool _is_fifo_full(); +static inline bool _is_fifo_empty(); +static inline bool _is_dma_complete(); +static inline bool _is_handshake_bit_set(); +static inline bool _is_allow_config_bit_set(); +static inline bool _was_hostdma_initialized(); +static void _request_host_status_interrupt(); + +/*----- Static variable definitions ----------------------------------*/ + +static bool g_hostdma_started = false; + +/*----- Extern variable definitions ----------------------------------*/ + +/*----- Extern function implementations ------------------------------*/ + +/** + * @brief Sets up the EMIFA engine. + */ +void per_emifa_init() { + + // EMA_CLK is 10~ ns per cycle? + // @TODO: Calculate actual values and maybe adjust + EMIFAWaitTimingConfig(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_ASYNC_WAITTIME_CONFIG( + 11, // wset Write setup time or width in EMA_CLK cycles + 6, // wstb Write strobe time or width in EMA_CLK cycles + 6, // whld Write hold time or width in EMA_CLK cycles + 11, // rset Read setup time or width in EMA_CLK cycles + 6, // rstb Read strobe time or width in EMA_CLK cycles + 6, // rhld Read hold time or width in EMA_CLK cycles + 11 // ta Minimum Turn-Around time + )); + EMIFAAsyncDevOpModeSelect(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_ASYNC_INTERFACE_NORMAL_MODE); + EMIFAAsyncDevDataBusWidthSelect(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_DATA_BUSWITTH_16BIT); + EMIFAExtendedWaitConfig(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_EXTENDED_WAIT_DISABLE); + + // Configure EMIFA interrupts + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_ASYNC_TIMOUT_INT); + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_LINE_TRAP_INT); + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_WAIT_RISE_INT); + + // Dummy data for debugging + uint16_t *ptr = (uint16_t*)0xC0000000; + for (int i = 0; i < 64; i++) { + *ptr++ = i; + } + +} + +/** + * @brief Poll for incoming HostDMA requests. When the bus is occupied + * nothing is able to come through. When the HostDMA connection + * has not been initialised we will wait for it to do so. + */ +void per_emifa_poll() { + + if (!g_hostdma_started) { + if (!_was_hostdma_initialized()) { + return; + } + ft_print("HostDMA started!"); + g_hostdma_started = true; + } + + + // Poll for incoming transmissions; The DSP requests a host read by + // setting the handshake bit. + if (_is_handshake_bit_set()) { + + // Read header: + // 1. Send the 7 configuration words + // 2. Wait for config descriptor to initialize DMA engine + // 3. Send host status IRQ to DSP to clear handshake bit (acknowledges request) + // 4. Wait for DMA transaction to complete. + _emifa_config(0x00000000, 6, 0x0000); + uint16_t word_count = *HDMA_DATA_PORT; + uint16_t block_count = *HDMA_DATA_PORT; + uint16_t host_address_lo = *HDMA_DATA_PORT; + uint16_t host_address_hi = *HDMA_DATA_PORT; + uint16_t dsp_address_lo = *HDMA_DATA_PORT; + uint16_t dsp_address_hi = *HDMA_DATA_PORT; + + // ... The handshake bit has been automatically cleared now that we + // read the last header word ... + + while (!_is_dma_complete()) { + // @TODO: Raise error if handshake bit is set again by ISR (indicating error)! + + // < IF TIMING IS WRONG EVENTUALLY IT WILL COMPLETELY HANG HERE > + ft_print("per_emifa_poll() waiting for completion of DMA transfer"); + } + + uint16_t *host_address = (uint16_t*)(host_address_lo | (host_address_hi << 16)); + uint32_t dsp_address = (uint32_t)(dsp_address_lo | (dsp_address_hi << 16)); + + uint16_t words_remaining = word_count; + + while (words_remaining != 0) { + + uint16_t num_block_words = (words_remaining < 16) ? words_remaining : 16; + // ft_printf("per_emifa_poll() reading %i words of remaining %i", (int)num_block_words, (int)words_remaining); + _emifa_config(dsp_address, num_block_words, 0x0000); + + for (int i = 0; i < num_block_words; i++) { + // ft_printf("per_emifa_poll() reading word:%i num_block_words:%i remaining:%i", i, num_block_words, words_remaining); + *host_address++ = *HDMA_DATA_PORT; + } + + words_remaining -= num_block_words; + dsp_address += 16 * sizeof(uint16_t); // increment up by an entire FIFO, actually + // should be num_block_words, but once we + // reach this after last block is received + // we exit the loop anyway and dsp_address + // variable won't be needed anymore. + + while (!_is_dma_complete()) { + // @TODO: Raise error if handshake bit is set again by ISR (indicating error)! + ft_print("per_emifa_poll() waiting for block transaction to complete..."); + } + + } + + // ft_print("per_emifa_poll() COMPLETE:"); + // host_address = (uint16_t*)(host_address_lo | (host_address_hi << 16)); + // for (int i = 0; i < word_count; i++) { + // ft_printf("%04X", *host_address++); + // } + // ft_print("per_emifa_poll() END HOST READ"); + host_address = (uint16_t*)(host_address_lo | (host_address_hi << 16)); + if (*host_address != 0x0000) { + ft_print("HOSTDMA ROUNDTRIP FAILED"); + } + + } + +} + +/** + * @brief Perform a blocking transfer. + * @TODO: MUST BE 32-BIT INTS! + * + * @param dsp_address Address in the DSP's virtual memory map to write to. + * @param words Pointer to the buffer of 16-bit words to write + * @param word_count Number of 16-bit words to write. Cannot be uneven amount! + */ +t_emifa_status per_emifa_transfer(uint32_t dsp_address, uint16_t *words, uint16_t word_count) { + + if (!g_hostdma_started) { + // ft_print("per_emifa_transfer() HostDMA uninitialized: cancelling transfer."); + return EMIFA_UNINITIALISED; + } + + // Check if host bit is set indicating it is waiting for us to acknowledge + // a transfer. Since this is a blocking operation on the DSP side, we want + // acknowledge it as fast as possible, this means the DSP's request needs + // to be prioritised over the host's. + if (_is_handshake_bit_set()) { + // ft_print("per_emifa_transfer() DSP wants request: cancelling transfer!"); + return EMIFA_BUS_OCCUPIED; // prioritises the DSP over host + } + + // ft_print("per_emifa_transfer() start ..."); + + int32_t words_remaining = word_count; + while (words_remaining > 0) { + + // ft_printf("per_emifa_transfer() words remaining: %i", (int)words_remaining); + int32_t num_words = MIN(words_remaining, 16); + + _emifa_config(dsp_address, num_words, HOST_CONFIG_WNR); + if (_is_handshake_bit_set()) { + _request_host_status_interrupt(); + } + + for (int i = 0; i < num_words; i++) { + *HDMA_DATA_PORT = *words++; + } + + words_remaining -= num_words; + dsp_address += num_words * sizeof(uint16_t); + } + + // ft_print("per_emifa_transfer() end ..."); + return EMIFA_SUCCESS; + +} + +/** + * @brief Whenever the EMIFA bus is occupied it's impossible for any new + * transmissions to happen. + * + * @return True if EMIFA bus currently occupied transferring or receiving. + */ +bool per_emifa_is_bus_available() { + uint16_t status = *HDMA_CONFIG_PORT; + return !(status & HOST_STATUS_HSHK) && !(status & HOST_STATUS_DMA_RDY) && (status & HOST_STATUS_ALLOW_CNFG); +} + +/*----- Static function implementations ------------------------------*/ + +/** + * @brief Configure HostDMA for reading/writing. + * + * @param start_address Address in the DSP's virtual memory map to read/write to. + * @param word_count Max 16, since the FIFO size is 16 16-bit words. + * MUST be atleast 2, otherwise burst mode breaks by causing + * infinite wait for DMA_RDY. + * @param extra_flags HOST_CONFIG flags. use HOST_CONFIG_WNR for host write mode. + */ +static t_emifa_status _emifa_config(uint32_t start_address, int word_count, uint16_t extra_flags) { + + while (!_is_allow_config_bit_set()) { + ft_print("_emifa_config _is_allow_config_bit_set() FALSE!!!"); + // EMIFA timing probably too fast, not sure ! + // return EMIFA_BUS_OCCUPIED; // this should never happen + } + + *HDMA_CONFIG_PORT = 0x00A9 | extra_flags; + *HDMA_CONFIG_PORT = start_address & 0xFFFF; + *HDMA_CONFIG_PORT = (start_address >> 16) & 0xFFFF; + *HDMA_CONFIG_PORT = word_count; // XCOUNT + *HDMA_CONFIG_PORT = 2; // XMODIFY + *HDMA_CONFIG_PORT = 1; // YCOUNT + *HDMA_CONFIG_PORT = 1; // YMODIFY + + while (!_is_dma_ready()) {} + + return EMIFA_SUCCESS; + +} + +static inline bool _is_dma_ready() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_DMA_RDY); +} + +static inline bool _is_fifo_full() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_FIFOFULL); +} + +static inline bool _is_fifo_empty() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_FIFOEMPTY); +} + +static inline bool _is_dma_complete() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_DMA_CMPLT); +} + +static inline bool _is_handshake_bit_set() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_HSHK); +} + +static inline bool _is_allow_config_bit_set() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_ALLOW_CNFG); +} + +/** + * @brief This is a trick; Before the HostDMA peripheral is initialized on the + * DSP side, the FIFOFULL and FIFOEMPTY in HOST_STATUS are both set. + * Ofcourse this is an invalid state and from this we can deduce that + * the HostDMA peripheral has yet to be initialized by the DSP. + * + * @returns True if HostDMA peripheral has been initialized by the DSP. + */ +static inline bool _was_hostdma_initialized() { + uint16_t status = *HDMA_CONFIG_PORT; + return !((status & HOST_STATUS_FIFOFULL) && (status & HOST_STATUS_FIFOEMPTY)); +} + +/** + * @brief Signals IRQ to DSP to clear handshake bit. This is to acknowledge + * the DSP's request for a host read operation. + * Note that this can only be called after DMA_RDY is set. + */ +static void _request_host_status_interrupt() { + // *(volatile uint8_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ + // *(volatile uint8_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ + *(volatile uint16_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ +} + + diff --git a/cpu/src/kernel/peripheral/per_emifa.c.old b/cpu/src/kernel/peripheral/per_emifa.c.old new file mode 100644 index 00000000..70f564e3 --- /dev/null +++ b/cpu/src/kernel/peripheral/per_emifa.c.old @@ -0,0 +1,432 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_emifa.c. + * + * @brief Configuration and handling of EMIFA peripheral. + * EMIFA communicates with the DSP's HostDMA engine and has the + * highest bandwidth for transmitting data between CPU and DSP. + */ + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +#include "soc_AM1808.h" +#include "csl_interrupt.h" +#include "csl_gpio.h" +#include "csl_emifa.h" +#include "hw_emifa2.h" +#include "hw_types.h" +#include "hw_syscfg0_AM1808.h" +#include "per_emifa.h" +#include "per_aintc.h" +#include "per_gpio.h" +#include "freetribe.h" // ft_printf + +/*----- Macros -------------------------------------------------------*/ + +#ifndef MIN +#define MIN(A,B) ((A) < (B) ? (A) : (B)) +#endif + +#define EMIFA_ASYNC_CFG2 HWREG(SOC_EMIFA_0_REGS + EMIFA_CE2CFG) + +#define HDMA_DATA_PORT ((volatile uint16_t*)(SOC_EMIFA_CS2_ADDR + 0x00)) +#define HDMA_CONFIG_PORT ((volatile uint16_t*)(SOC_EMIFA_CS2_ADDR + 0x02)) + +#define HOST_STATUS_DMA_RDY (1 << 0) +#define HOST_STATUS_FIFOFULL (1 << 1) +#define HOST_STATUS_FIFOEMPTY (1 << 2) +#define HOST_STATUS_DMA_CMPLT (1 << 3) +#define HOST_STATUS_HSHK (1 << 4) +#define HOST_STATUS_HOSTDP_TOUT (1 << 5) +#define HOST_STATUS_HIRQ (1 << 6) +#define HOST_STATUS_ALLOW_CNFG (1 << 7) +#define HOST_STATUS_DMA_DIR (1 << 8) + +#define HOST_CONFIG_WNR (1 << 1) // 0=Host read, 1=Host write +#define HOST_CONFIG_DMA2D (1 << 4) // 0=Linear , 1=Two-dimensional +#define HOST_CONFIG_FLOW (1 << 12) // 0=Stop mode, 1=Autobuffer mode + +#define HDMA_MODE_HOST_READ false +#define HDMA_MODE_HOST_WRITE true + +// #define PIN_EMA_D_15 56 // GP3[7] <-> PH15 +// #define PIN_EMA_D_14 55 // GP3[6] <-> PH14 +// #define PIN_EMA_D_13 54 // GP3[5] <-> PH13 +// #define PIN_EMA_D_12 53 // GP3[4] <-> PH12 +// #define PIN_EMA_D_11 52 // GP3[3] <-> PH11 +// #define PIN_EMA_D_10 51 // GP3[2] <-> PH10 +// #define PIN_EMA_D_9 50 // GP3[1] <-> PH9 +// #define PIN_EMA_D_8 49 // GP3[0] <-> PH8 +// #define PIN_EMA_D_7 80 // GP4[15] <-> PH7 +// #define PIN_EMA_D_6 79 // GP4[14] <-> PH6 +// #define PIN_EMA_D_5 78 // GP4[13] <-> PH5 +// #define PIN_EMA_D_4 77 // GP4[12] <-> PH4 +// #define PIN_EMA_D_3 76 // GP4[11] <-> PH3 +// #define PIN_EMA_D_2 75 // GP4[10] <-> PH2 +// #define PIN_EMA_D_1 74 // GP4[9] <-> PH1 +// #define PIN_EMA_D_0 73 // GP4[8] <-> PH0 +// #define PIN_EMA_A_22 71 // GP4[6] +// #define PIN_EMA_A_21 70 // GP4[5] +// #define PIN_EMA_A_20 69 // GP4[4] +// #define PIN_EMA_A_19 68 // GP4[3] +// #define PIN_EMA_A_18 67 // GP4[2] +// #define PIN_EMA_A_17 66 // GP4[1] +// #define PIN_EMA_A_16 65 // GP4[0] +// #define PIN_EMA_A_15 96 // GP5[15] +// #define PIN_EMA_A_14 95 // GP5[14] +// #define PIN_EMA_A_13 94 // GP5[13] +// #define PIN_EMA_A_12 93 // GP5[12] +// #define PIN_EMA_A_11 92 // GP5[11] +// #define PIN_EMA_A_10 91 // GP5[10] +// #define PIN_EMA_A_9 90 // GP5[9] +// #define PIN_EMA_A_8 89 // GP5[8] +// #define PIN_EMA_A_7 88 // GP5[7] +// #define PIN_EMA_A_6 87 // GP5[6] +// #define PIN_EMA_A_5 86 // GP5[5] +// #define PIN_EMA_A_4 85 // GP5[4] +// #define PIN_EMA_A_3 84 // GP5[3] +// #define PIN_EMA_A_2 83 // GP5[2] +// #define PIN_EMA_A_1 82 // GP5[1] +// #define PIN_EMA_A_0 81 // GP5[0] +// #define PIN_EMA_BA_0 41 // GP2[8] +// #define PIN_EMA_BA_1 42 // GP2[9] +// #define PIN_EMA_CLK 40 // GP2[7] +// #define PIN_EMA_SDCKE 39 // GP2[6] +// #define PIN_EMA_RAS 38 // GP2[5] +// #define PIN_EMA_CAS 37 // GP2[4] +// #define PIN_EMA_CS_0 33 // GP2[0] +// #define PIN_EMA_CS_2 64 // GP3[15] +// #define PIN_EMA_CS_3 63 // GP3[14] +// #define PIN_EMA_CS_4 62 // GP3[13] +// #define PIN_EMA_CS_5 61 // GP3[12] +// #define PIN_EMA_A_RW 58 // GP3[9] +// #define PIN_EMA_WE 60 // GP3[11] (can be used for interrupt!) +// #define PIN_EMA_WEN_DQM_1 35 // GP2[2] +// #define PIN_EMA_WEN_DQM_0 36 // GP2[3] +// #define PIN_EMA_OE 59 // GP3[10] (can be used for interrupt!) +// #define PIN_EMA_WAIT_0 57 // GP3[8] +// #define PIN_EMA_WAIT_1 34 // GP2[1] + +// #define HRDY_PIN PIN_EMA_WAIT_0 +// #define EMIFA_GPIO_INT_CHANNEL 9 + +/*----- Typedefs -----------------------------------------------------*/ + +/*----- Static function prototypes -----------------------------------*/ + +static t_emifa_status _emifa_config(uint32_t start_address, int word_count, uint16_t extra_flags); +static inline bool _is_dma_ready(); +static inline bool _is_fifo_full(); +static inline bool _is_fifo_empty(); +static inline bool _is_dma_complete(); +static inline bool _is_handshake_bit_set(); +static inline bool _is_allow_config_bit_set(); +static inline bool _was_hostdma_initialized(); +static void _request_host_status_interrupt(); + +/*----- Static variable definitions ----------------------------------*/ + +static bool g_hostdma_started = false; + +/*----- Extern variable definitions ----------------------------------*/ + +/*----- Extern function implementations ------------------------------*/ + +/** + * @brief Sets up the EMIFA engine. + */ +void per_emifa_init() { + + // EMA_CLK is 10~ ns per cycle? + // @TODO: Calculate and adjust faster values + EMIFAWaitTimingConfig(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_ASYNC_WAITTIME_CONFIG( + 40, // 2, // wset Write setup time or width in EMA_CLK cycles + 40, // , // wstb Write strobe time or width in EMA_CLK cycles + 40, // 2, // whld Write hold time or width in EMA_CLK cycles + 40, // , // rset Read setup time or width in EMA_CLK cycles + 40, // , // rstb Read strobe time or width in EMA_CLK cycles + 40, // 2, // rhld Read hold time or width in EMA_CLK cycles + 40 // // ta Minimum Turn-Around time + )); + EMIFAAsyncDevOpModeSelect(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_ASYNC_INTERFACE_NORMAL_MODE); + EMIFAAsyncDevDataBusWidthSelect(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_DATA_BUSWITTH_16BIT); + EMIFAExtendedWaitConfig(SOC_EMIFA_0_REGS, EMIFA_CHIP_SELECT_2, EMIFA_EXTENDED_WAIT_DISABLE); + + // Configure EMIFA interrupts + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_ASYNC_TIMOUT_INT); + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_LINE_TRAP_INT); + EMIFAMskedIntDisable(SOC_EMIFA_0_REGS, EMIFA_WAIT_RISE_INT); + + // Dummy data for debugging + uint16_t *ptr = (uint16_t*)0xC0000000; + for (int i = 0; i < 64; i++) { + *ptr++ = i; + } + +} + +/** + * @brief Poll for incoming HostDMA requests. When the bus is occupied + * nothing is able to come through. When the HostDMA connection + * has not been initialised we will wait for it to do so. + */ +void per_emifa_poll() { + + if (!g_hostdma_started) { + if (!_was_hostdma_initialized()) { + return; + } + ft_print("HostDMA started!"); + g_hostdma_started = true; + } + + + // Poll for incoming transmissions; The DSP requests a host read by + // setting the handshake bit. + if (_is_handshake_bit_set()) { + + ft_print("per_emifa_poll() START HOST READ"); + + // Read header: + // 1. Send the 7 configuration words + // 2. Wait for config descriptor to initialize DMA engine + // 3. Send host status IRQ to DSP to clear handshake bit (acknowledges request) + // 4. Wait for DMA transaction to complete. + _emifa_config(0x00000000, 6, 0x0000); + uint16_t word_count = *HDMA_DATA_PORT; + uint16_t block_count = *HDMA_DATA_PORT; + uint16_t host_address_lo = *HDMA_DATA_PORT; + uint16_t host_address_hi = *HDMA_DATA_PORT; + uint16_t dsp_address_lo = *HDMA_DATA_PORT; + uint16_t dsp_address_hi = *HDMA_DATA_PORT; + + // ... The handshake bit has been automatically cleared now that we + // read the last header word ... + + while (!_is_dma_complete()) { + // @TODO: Raise error if handshake bit is set again by ISR (indicating error)! + ft_print("per_emifa_poll() waiting for completion of DMA transfer"); + } + + uint16_t *host_address = (uint16_t*)(host_address_lo | (host_address_hi << 16)); + uint32_t dsp_address = (uint32_t)(dsp_address_lo | (dsp_address_hi << 16)); + + ft_printf("per_emifa_poll() header ready: %i %i %p %p", + (int)word_count, (int)block_count, (void*)host_address, (void*)dsp_address); + + uint16_t words_remaining = word_count; + + while (words_remaining != 0) { + + uint16_t num_block_words = (words_remaining < 16) ? words_remaining : 16; + ft_printf("per_emifa_poll() reading %i words of remaining %i", (int)num_block_words, (int)words_remaining); + _emifa_config(dsp_address, num_block_words, 0x0000); + + for (int i = 0; i < num_block_words; i++) { + // ft_printf("per_emifa_poll() reading word:%i num_block_words:%i remaining:%i", i, num_block_words, words_remaining); + *host_address++ = *HDMA_DATA_PORT; + } + + words_remaining -= num_block_words; + dsp_address += 16 * sizeof(uint16_t); // increment up by an entire FIFO, actually + // should be num_block_words, but once we + // reach this after last block is received + // we exit the loop anyway and dsp_address + // variable won't be needed anymore. + + while (!_is_dma_complete()) { + // @TODO: Raise error if handshake bit is set again by ISR (indicating error)! + ft_print("per_emifa_poll() waiting for block transaction to complete..."); + } + + } + + // ft_print("== HOST RECEIVE COMPLETE =="); + // host_address = (uint16_t*)(host_address_lo | (host_address_hi << 16)); + // for (int i = 0; i < word_count; i++) { + // ft_printf("%04X", *host_address++); + // } + ft_print("per_emifa_poll() END HOST READ"); + + } + +} + +/** + * @brief Perform a blocking transfer. + * @TODO: MUST BE 32-BIT INTS! + * + * @param dsp_address Address in the DSP's virtual memory map to write to. + * @param words Pointer to the buffer of 16-bit words to write + * @param word_count Number of 16-bit words to write. Cannot be uneven amount! + */ +t_emifa_status per_emifa_transfer(uint32_t dsp_address, uint16_t *words, uint16_t word_count) { + + if (!g_hostdma_started) { + // ft_print("per_emifa_transfer() HostDMA uninitialized: cancelling transfer."); + return EMIFA_UNINITIALISED; + } + + // Check if host bit is set indicating it is waiting for us to acknowledge + // a transfer. Since this is a blocking operation on the DSP side, we want + // acknowledge it as fast as possible, this means the DSP's request needs + // to be prioritised over the host's. + if (_is_handshake_bit_set()) { + // ft_print("per_emifa_transfer() DSP wants request: cancelling transfer!"); + return EMIFA_BUS_OCCUPIED; // prioritises the DSP over host + } + + int32_t words_remaining = word_count; + while (words_remaining > 0) { + + // ft_printf("per_emifa_transfer() words remaining: %i", (int)words_remaining); + int32_t num_words = MIN(words_remaining, 16); + + _emifa_config(dsp_address, num_words, HOST_CONFIG_WNR); + if (_is_handshake_bit_set()) { + _request_host_status_interrupt(); + } + + for (int i = 0; i < num_words; i++) { + *HDMA_DATA_PORT = *words++; + } + + words_remaining -= num_words; + dsp_address += num_words * sizeof(uint16_t); + } + + // ft_print("per_emifa_transfer() end ..."); + return EMIFA_SUCCESS; + +} + +/** + * @brief Whenever the EMIFA bus is occupied it's impossible for any new + * transmissions to happen. + * + * @return True if EMIFA bus currently occupied transferring or receiving. + */ +bool per_emifa_is_bus_available() { + uint16_t status = *HDMA_CONFIG_PORT; + return !(status & HOST_STATUS_HSHK) && !(status & HOST_STATUS_DMA_RDY) && (status & HOST_STATUS_ALLOW_CNFG); +} + +/*----- Static function implementations ------------------------------*/ + +/** + * @brief Configure HostDMA for reading/writing. + * + * @param start_address Address in the DSP's virtual memory map to read/write to. + * @param word_count Max 16, since the FIFO size is 16 16-bit words. + * MUST be atleast 2, otherwise burst mode breaks by causing + * infinite wait for DMA_RDY. + * @param extra_flags HOST_CONFIG flags. use HOST_CONFIG_WNR for host write mode. + */ +static t_emifa_status _emifa_config(uint32_t start_address, int word_count, uint16_t extra_flags) { + + if (!_is_allow_config_bit_set()) { + ft_print("_emifa_config _is_allow_config_bit_set() FALSE!!!"); + return EMIFA_BUS_OCCUPIED; // this should never happen + } + + *HDMA_CONFIG_PORT = 0x00A9 | extra_flags; + *HDMA_CONFIG_PORT = start_address & 0xFFFF; + *HDMA_CONFIG_PORT = (start_address >> 16) & 0xFFFF; + *HDMA_CONFIG_PORT = word_count; // XCOUNT + *HDMA_CONFIG_PORT = 2; // XMODIFY + *HDMA_CONFIG_PORT = 1; // YCOUNT + *HDMA_CONFIG_PORT = 1; // YMODIFY + + while (!_is_dma_ready()) { + ft_printf("_emifa_config waiting for configuration: allow_cfg:%i dma_cmplt:%i hshk:%i", + (int)_is_allow_config_bit_set(), + (int)_is_dma_complete(), + (int)_is_handshake_bit_set() + ); + } + + return EMIFA_SUCCESS; + +} + +static inline bool _is_dma_ready() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_DMA_RDY); +} + +static inline bool _is_fifo_full() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_FIFOFULL); +} + +static inline bool _is_fifo_empty() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_FIFOEMPTY); +} + +static inline bool _is_dma_complete() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_DMA_CMPLT); +} + +static inline bool _is_handshake_bit_set() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_HSHK); +} + +static inline bool _is_allow_config_bit_set() { + return (*HDMA_CONFIG_PORT & HOST_STATUS_ALLOW_CNFG); +} + +/** + * @brief This is a trick; Before the HostDMA peripheral is initialized on the + * DSP side, the FIFOFULL and FIFOEMPTY in HOST_STATUS are both set. + * Ofcourse this is an invalid state and from this we can deduce that + * the HostDMA peripheral has yet to be initialized by the DSP. + * + * @returns True if HostDMA peripheral has been initialized by the DSP. + */ +static inline bool _was_hostdma_initialized() { + uint16_t status = *HDMA_CONFIG_PORT; + return !((status & HOST_STATUS_FIFOFULL) && (status & HOST_STATUS_FIFOEMPTY)); +} + +/** + * @brief Signals IRQ to DSP to clear handshake bit. This is to acknowledge + * the DSP's request for a host read operation. + * Note that this can only be called after DMA_RDY is set. + */ +static void _request_host_status_interrupt() { + // *(volatile uint8_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ + // *(volatile uint8_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ + *(volatile uint16_t*)HDMA_CONFIG_PORT = 0x1C; // HOST IRQ +} + + diff --git a/cpu/src/kernel/peripheral/per_emifa.h b/cpu/src/kernel/peripheral/per_emifa.h new file mode 100644 index 00000000..c7858410 --- /dev/null +++ b/cpu/src/kernel/peripheral/per_emifa.h @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_emifa.h + * + * @brief Public API for EMIFA peripheral driver. + * EMIFA communicates with the DSP's HostDMA engine and has the + * highest bandwidth for transmitting data between CPU and DSP. + */ + +#ifndef PER_EMIFA_H +#define PER_EMIFA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +/*----- Typedefs -----------------------------------------------------*/ + +typedef enum { + EMIFA_SUCCESS = 0, + EMIFA_UNINITIALISED, + EMIFA_BUS_OCCUPIED, +} t_emifa_status; + +/*----- Extern function prototypes -----------------------------------*/ + +/** + * @brief Sets up the EMIFA engine. + */ +void per_emifa_init(); + +/** + * @brief Poll for incoming HostDMA requests. When the bus is occupied + * nothing is able to come through. When the HostDMA connection + * has not been initialised we will wait for it to do so. + */ +void per_emifa_poll(); + +/** + * @brief Perform a blocking transfer. + * + * @param dsp_address Address in the DSP's virtual memory map to write to. + * @param words Pointer to the buffer of 16-bit words to write + * @param word_count Number of 16-bit words to write. Cannot be 1. + */ +t_emifa_status per_emifa_transfer(uint32_t dsp_address, uint16_t *words, uint16_t word_count); + +/** + * @brief Whenever the EMIFA bus is occupied it's impossible for any new + * transmissions to happen. + * + * @return True if EMIFA bus currently occupied transferring or receiving. + */ +bool per_emifa_is_bus_available(); + +#ifdef __cplusplus +} +#endif +#endif +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_emifa.h.old b/cpu/src/kernel/peripheral/per_emifa.h.old new file mode 100644 index 00000000..c7858410 --- /dev/null +++ b/cpu/src/kernel/peripheral/per_emifa.h.old @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file per_emifa.h + * + * @brief Public API for EMIFA peripheral driver. + * EMIFA communicates with the DSP's HostDMA engine and has the + * highest bandwidth for transmitting data between CPU and DSP. + */ + +#ifndef PER_EMIFA_H +#define PER_EMIFA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +/*----- Typedefs -----------------------------------------------------*/ + +typedef enum { + EMIFA_SUCCESS = 0, + EMIFA_UNINITIALISED, + EMIFA_BUS_OCCUPIED, +} t_emifa_status; + +/*----- Extern function prototypes -----------------------------------*/ + +/** + * @brief Sets up the EMIFA engine. + */ +void per_emifa_init(); + +/** + * @brief Poll for incoming HostDMA requests. When the bus is occupied + * nothing is able to come through. When the HostDMA connection + * has not been initialised we will wait for it to do so. + */ +void per_emifa_poll(); + +/** + * @brief Perform a blocking transfer. + * + * @param dsp_address Address in the DSP's virtual memory map to write to. + * @param words Pointer to the buffer of 16-bit words to write + * @param word_count Number of 16-bit words to write. Cannot be 1. + */ +t_emifa_status per_emifa_transfer(uint32_t dsp_address, uint16_t *words, uint16_t word_count); + +/** + * @brief Whenever the EMIFA bus is occupied it's impossible for any new + * transmissions to happen. + * + * @return True if EMIFA bus currently occupied transferring or receiving. + */ +bool per_emifa_is_bus_available(); + +#ifdef __cplusplus +} +#endif +#endif +/*----- End of file --------------------------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_gpio.h b/cpu/src/kernel/peripheral/per_gpio.h index fb571327..034b1421 100644 --- a/cpu/src/kernel/peripheral/per_gpio.h +++ b/cpu/src/kernel/peripheral/per_gpio.h @@ -48,6 +48,12 @@ extern "C" { /*----- Macros -------------------------------------------------------*/ +/** @brief Get the bank number corresponding to pin number. */ +static inline uint8_t per_gpio_bank_from_pin(uint8_t pin_index) { + uint8_t bank_index = ((pin_index - 1) >> 4) * !!pin_index; + return bank_index; +} + /*----- Typedefs -----------------------------------------------------*/ /*----- Extern variable declarations ---------------------------------*/ diff --git a/cpu/src/kernel/peripheral/per_pinmux.c b/cpu/src/kernel/peripheral/per_pinmux.c index 1d14dbb5..798d5084 100644 --- a/cpu/src/kernel/peripheral/per_pinmux.c +++ b/cpu/src/kernel/peripheral/per_pinmux.c @@ -78,9 +78,11 @@ void per_pinmux_init(void) { HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(5)) = 0x81111111; // GPIO2 0-7. + // HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(6)) = 0x81888888; // EMA_WAIT 1 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(6)) = 0x88888888; // EMIFA Control, GPIO3 8-9, 12-14. + // HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(7)) = 0x18118881; // EMA_WAIT 0 HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(7)) = 0x88118881; // EMIFA Data 8-15. diff --git a/cpu/src/kernel/service/svc_dsp.c b/cpu/src/kernel/service/svc_dsp.c index 43c65141..0b433a02 100644 --- a/cpu/src/kernel/service/svc_dsp.c +++ b/cpu/src/kernel/service/svc_dsp.c @@ -39,9 +39,12 @@ under the terms of the GNU Affero General Public License as published by #include #include +#include "per_emifa.h" + #include "dev_dsp.h" #include "ft_error.h" +#include "freetribe.h" // ft_printf #include "svc_delay.h" #include "svc_dsp.h" @@ -124,9 +127,12 @@ static t_status _handle_system_ready(void); static t_status _handle_system_profile(uint8_t *payload, uint8_t length); +// static t_status _handle_request_hostdma_config(void); + void _register_module_callback(uint8_t msg_id, void *callback); void _register_system_callback(uint8_t msg_id, void *callback); +static int g_test = 1; /*----- Extern function implementations ------------------------------*/ void svc_dsp_task(void) { @@ -179,6 +185,14 @@ void svc_dsp_task(void) { break; case STATE_RUN: + if (g_test <= 0) { + per_emifa_transfer(0x00000010, (uint16_t*)0xC0000000, 32); + g_test = 4; + } else { + g_test--; + } + per_emifa_poll(); + // Handle received bytes. if (dev_dsp_spi_rx_dequeue(&dsp_byte) == SUCCESS) { _dsp_receive(dsp_byte); @@ -481,6 +495,10 @@ static t_status _handle_system_message(uint8_t msg_id, uint8_t *payload, case SYSTEM_PROFILE: result = _handle_system_profile(payload, length); break; + + // case SYSTEM_REQUEST_HOSTDMA_CONFIG: + // result = _handle_request_hostdma_config(); + // break; default: break; @@ -555,6 +573,12 @@ static t_status _handle_system_profile(uint8_t *payload, uint8_t length) { return SUCCESS; } +// static t_status _handle_request_hostdma_config(void) { +// // per_emifa_config_host_read(0x00000000, 1); // @TODO: Must be handled by device layer IPC (default state) +// // _transmit_message(MSG_TYPE_SYSTEM, SYSTEM_AWAIT_HOSTDMA_HANDSHAKE, NULL, 0); +// return SUCCESS; +// } + static void _dsp_response_required(void) { g_pending_response++; } static void _dsp_response_received(void) { diff --git a/cpu/src/kernel/service/svc_dsp.h b/cpu/src/kernel/service/svc_dsp.h index 05df37ae..6926ea90 100644 --- a/cpu/src/kernel/service/svc_dsp.h +++ b/cpu/src/kernel/service/svc_dsp.h @@ -59,7 +59,8 @@ enum e_module_msg_id { MODULE_SET_PARAM_VALUE, MODULE_PARAM_VALUE, MODULE_GET_PARAM_NAME, - MODULE_PARAM_NAME + MODULE_PARAM_NAME, + MODULE_TRANSMIT_BUFFER, }; enum e_system_msg_id { @@ -70,6 +71,9 @@ enum e_system_msg_id { SYSTEM_PORT_STATE, SYSTEM_GET_PROFILE, SYSTEM_PROFILE, + SYSTEM_REQUEST_HOSTDMA_CONFIG, + SYSTEM_AWAIT_HOSTDMA_HANDSHAKE, + SYSTEM_CONFIRM_HOSTDMA_HANDSHAKE, }; /*----- Typedefs -----------------------------------------------------*/ diff --git a/dsp/Makefile b/dsp/Makefile index 6a4b4f06..8cc9d1fe 100644 --- a/dsp/Makefile +++ b/dsp/Makefile @@ -28,7 +28,8 @@ BIN := $(BFIN_TOOLCHAIN)$(PREFIX)objcopy CPU := bf527 # C compilation options -OPTIMISE = -g -O3 +# OPTIMISE = -g -O3 +OPTIMISE = -Os CFLAGS := -mcpu=$(CPU) $(OPTIMISE) -mfast-fp -fdata-sections -ffunction-sections -Wall # Assembly compilation options ASM_FLAGS := -x assembler-with-cpp diff --git a/dsp/dsp.lds b/dsp/dsp.lds index 85b9d45d..6e465ada 100755 --- a/dsp/dsp.lds +++ b/dsp/dsp.lds @@ -22,6 +22,7 @@ MEMORY MEM_L1_DATA_B : ORIGIN = 0xFF900000, LENGTH = 0x8000 MEM_L1_DATA_A : ORIGIN = 0xFF800000, LENGTH = 0x8000 MEM_L2 : ORIGIN = 0xFEB00000, LENGTH = 0x0 + MEM_SDRAM : ORIGIN = 0x00000000, LENGTH = 32M } OUTPUT_FORMAT("elf32-bfin", "elf32-bfin", @@ -258,5 +259,8 @@ SECTIONS __stack_end = ORIGIN(MEM_L1_SCRATCH) + LENGTH(MEM_L1_SCRATCH); + _sdram_start = ORIGIN(MEM_SDRAM); + _sdram_end = ORIGIN(MEM_SDRAM) + LENGTH(MEM_SDRAM); + /DISCARD/ : { *(.note.GNU-stack) } } diff --git a/dsp/src/kernel/device/dev_cpu_spi.c b/dsp/src/kernel/device/dev_cpu_spi.c index 8685c469..aaff55d7 100644 --- a/dsp/src/kernel/device/dev_cpu_spi.c +++ b/dsp/src/kernel/device/dev_cpu_spi.c @@ -65,14 +65,14 @@ static uint8_t g_spi_tx_rbmem[SPI_TX_BUF_LEN]; static uint8_t g_cpu_spi_rx_byte; static uint8_t g_cpu_spi_tx_byte; -static bool g_spi_tx_complete = true; +// static bool g_spi_tx_complete = true; // NOTE: was never used /*----- Extern variable definitions ----------------------------------*/ /*----- Static function prototypes -----------------------------------*/ static int _cpu_spi_tx_dequeue(uint8_t *spi_byte); -static void _cpu_spi_rx_enqueue(uint8_t *spi_byte); +static int _cpu_spi_rx_enqueue(uint8_t *spi_byte); static void _cpu_spi_trx_byte(uint8_t *tx_byte, uint8_t *rx_byte); @@ -122,11 +122,10 @@ static int _cpu_spi_tx_dequeue(uint8_t *spi_byte) { return ring_buffer_get(g_spi_tx_rbd, spi_byte); } -/// TODO: Check/return status. -static void _cpu_spi_rx_enqueue(uint8_t *spi_byte) { +static int _cpu_spi_rx_enqueue(uint8_t *spi_byte) { // Overwrite on overflow? - ring_buffer_put_force(g_spi_rx_rbd, spi_byte); + return ring_buffer_put_force(g_spi_rx_rbd, spi_byte); } static void _cpu_spi_trx_byte(uint8_t *tx_byte, uint8_t *rx_byte) { diff --git a/dsp/src/kernel/device/dev_cpu_spi.h b/dsp/src/kernel/device/dev_cpu_spi.h index 863f6ee7..6472dbd7 100644 --- a/dsp/src/kernel/device/dev_cpu_spi.h +++ b/dsp/src/kernel/device/dev_cpu_spi.h @@ -56,7 +56,7 @@ extern "C" { void dev_cpu_spi_init(void); int dev_cpu_spi_rx_dequeue(uint8_t *spi_byte); -void dev_cpu_spi_tx_enqueue(uint8_t *spi_byte); +int dev_cpu_spi_tx_enqueue(uint8_t *spi_byte); #ifdef __cplusplus } diff --git a/dsp/src/kernel/device/dev_shared_mem.c b/dsp/src/kernel/device/dev_shared_mem.c new file mode 100644 index 00000000..59e9465a --- /dev/null +++ b/dsp/src/kernel/device/dev_shared_mem.c @@ -0,0 +1,8 @@ + +#include "dev_shared_mem.h" +#include "dev_cpu_spi.h" +#include "per_hostdma.h" + +void dev_shared_mem_init() { + +} diff --git a/dsp/src/kernel/device/dev_shared_mem.h b/dsp/src/kernel/device/dev_shared_mem.h new file mode 100644 index 00000000..c34d8953 --- /dev/null +++ b/dsp/src/kernel/device/dev_shared_mem.h @@ -0,0 +1,69 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file dev_shared_mem.h + * + * @brief Handles communication between EMIFA and HostDMA + * + */ + +#ifndef DEV_SHARED_MEM_H +#define DEV_SHARED_MEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include + +/*----- Macros -------------------------------------------------------*/ + +/*----- Typedefs -----------------------------------------------------*/ + +enum e_shared_mem_state { + SHARED_MEM_STATE_AWAIT_CONFIG, + SHARED_MEM_STATE_WAIT_DMA_RDY, +}; + +/*----- Extern variable declarations ---------------------------------*/ + +/*----- Extern function prototypes -----------------------------------*/ + +void dev_shared_mem_init(); + +#ifdef __cplusplus +} +#endif +#endif + +/*----- End of file --------------------------------------------------*/ diff --git a/dsp/src/kernel/peripheral/per_gpio.c b/dsp/src/kernel/peripheral/per_gpio.c index 83fcac84..e4fb6227 100644 --- a/dsp/src/kernel/peripheral/per_gpio.c +++ b/dsp/src/kernel/peripheral/per_gpio.c @@ -68,6 +68,8 @@ under the terms of the GNU Affero General Public License as published by /*----- Static function prototypes -----------------------------------*/ +void _test_output(void); // for probing which pins are connected + /*----- Extern function implementations ------------------------------*/ void per_gpio_init(void) { @@ -143,6 +145,25 @@ void per_gpio_set_port(uint8_t port, uint16_t value) { } } +void per_gpio_set_dir(uint8_t port, uint8_t pin, bool output) { + + switch (port) { + + case PORT_F: + *pPORTFIO_DIR |= pin & output; + break; + case PORT_G: + *pPORTGIO_DIR |= pin & output; + break; + case PORT_H: + *pPORTGIO_DIR |= pin & output; + break; + + default: + break; + } +} + /*----- Static function implementations ------------------------------*/ /*----- End of file --------------------------------------------------*/ diff --git a/dsp/src/kernel/peripheral/per_gpio.h b/dsp/src/kernel/peripheral/per_gpio.h index bb2305cb..2cd39d6c 100644 --- a/dsp/src/kernel/peripheral/per_gpio.h +++ b/dsp/src/kernel/peripheral/per_gpio.h @@ -44,6 +44,7 @@ extern "C" { /*----- Includes -----------------------------------------------------*/ #include +#include /*----- Macros -------------------------------------------------------*/ @@ -61,6 +62,7 @@ typedef enum { PORT_F, PORT_G, PORT_H } e_port; void per_gpio_init(void); uint16_t per_gpio_get_port(uint8_t port); void per_gpio_set_port(uint8_t port, uint16_t value); +void per_gpio_set_dir(uint8_t port, uint8_t pin, bool output); #ifdef __cplusplus } diff --git a/dsp/src/kernel/peripheral/per_hostdma.c b/dsp/src/kernel/peripheral/per_hostdma.c new file mode 100644 index 00000000..642e9fd0 --- /dev/null +++ b/dsp/src/kernel/peripheral/per_hostdma.c @@ -0,0 +1,238 @@ +#include +#include +#include + +#include +#include + +#include "per_hostdma.h" +#include "per_gpio.h" + +/*----- Macros -------------------------------------------------------*/ + +// #define PGMUX_HOSTDP 0x2800 +// #define PGFER_SPI 0x001e +// #define PGFER_UART 0x0180 +// #define PGFER_HOSTDP 0xf800 +// #define PHFER_HOSTDP 0xffff + +/*----- Static function prototypes -----------------------------------*/ + +/*----- Static variable definitions ----------------------------------*/ + +static bool g_initialised = false; +static bool g_reject_host_read = false; + +/*----- Extern variable definitions ----------------------------------*/ + +__attribute__((interrupt_handler)) static void _hostdp_status_isr(void); +__attribute__((interrupt_handler)) static void _hostdp_dma1_isr(void); +__attribute__((interrupt_handler)) static void _host_read_done_isr(void); +static inline bool _is_bus_available_hardware(); +static void _test_buffer(); + +/*----- Extern function implementations ------------------------------*/ + +void per_hostdma_init() { + + // Register HOSTDP status interrupt + *pSIC_IAR6 |= P49_IVG(10); + ssync(); + *pEVT10 = &_hostdp_status_isr; + ssync(); + *pSIC_IMASK1 |= IRQ_HOSTDP_STATUS; + ssync(); + int i; + asm volatile("cli %0; bitset(%0, 10); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + // Register HOST READ DONE interrupt + *pSIC_IAR6 |= P50_IVG(13); + ssync(); + *pEVT13 = &_host_read_done_isr; + ssync(); + *pSIC_IMASK1 |= IRQ_HOSTRD_DONE; + ssync(); + asm volatile("cli %0; bitset(%0, 13); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + // Register DMA1 HOSTDP interrupt + *pSIC_IAR3 |= P28_IVG(12); + ssync(); + *pEVT12 = &_hostdp_dma1_isr; + ssync(); + *pSIC_IMASK0 |= IRQ_DMA1; + ssync(); + asm volatile("cli %0; bitset(%0, 12); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + _test_buffer(); + + per_hostdma_reset(); + g_initialised = true; +} + +void per_hostdma_reset() { + // Configure HostDMA + // *pHOST_STATUS = 0x000C; // reset: DMA_CMPLT | FIFOEMPTY + // ssync(); + *pHOST_CONTROL = BDR | EHW | EHR | HOSTDP_DATA_SIZE | HOSTDP_EN; // @TODO: this necessary every time during DSP transfer? + ssync(); +} + +/** + * @brief Transfers a block of data to the host. + * + * @param host_address Address in the host's virtual memory map to write to. + * @param words Buffer of data + * @param word_count Number of words, must be atleast 2 + * + * @return 0 on success, otherwise an error + */ +t_hostdma_status per_hostdma_transfer(uint32_t host_address, uint16_t *words, uint16_t word_count) { + + if (!g_initialised) + return HOSTDMA_UNINITIALISED; + + // First check to quickly see if the host is occupied right now. + // This does not mean we are free to go yet since we need a handshake + // to occur between us and the host in order to safely send data. + if (!_is_bus_available_hardware()) + return HOSTDMA_BUS_OCCUPIED; + + // Calculate block count; Each block is a full FIFO unless there + // are less words remaining. Only exception is when the block has + // only one word left, then we add an additional word for padding. + // This is to make burst mode happy. + uint16_t block_count = ((uint32_t)word_count + 15) / 16; + + // Prepare a header for transfer first + *(volatile uint16_t*)0x00000000 = word_count; + *(volatile uint16_t*)0x00000002 = block_count; + *(volatile uint16_t*)0x00000004 = host_address & 0xFFFF; + *(volatile uint16_t*)0x00000006 = (host_address >> 16) & 0xFFFF; + *(volatile uint16_t*)0x00000008 = (uint32_t)words & 0xFFFF; + *(volatile uint16_t*)0x0000000A = ((uint32_t)words >> 16) & 0xFFFF; + + // @TODO: Make it unable to handshake if handshake bit is already set by host! + + // Signal to host using handshake bit that we want to initiate a host_read. + *pHOST_STATUS |= HSHK; + ssync(); + while (*pHOST_STATUS & HSHK) { + + // ... Wait for host to acknowledge our request by clearing HSHK bit, + // this happens when the last data word is read by the host. + + // If the host issues a HOST STATUS IRQ it means the host already claimed + // the bus. + if (g_reject_host_read) { + *pHOST_STATUS &= ~HSHK; + ssync(); + g_reject_host_read = false; + return HOSTDMA_BUS_OCCUPIED; + } + } + + while (!(*pHOST_STATUS & DMA_CMPLT)) { + // @TODO: Handle host-side errors that cause DSP to infinitely stall here! + // (maybe using bus timeout?!) + // (or detect a HOSTDP reset command??? (dma finish)) + } + per_hostdma_reset(); + + // Block transfer loop; All we have to do is wait for the DMA + // to happen, and reset the HostDMA peripheral afterwards. + uint16_t i; + for (i = 0; i < block_count; i++) { + while (!(*pHOST_STATUS & DMA_CMPLT)) { + // @TODO: Handle host-side errors that cause DSP to infinitely stall here! + // (maybe using bus timeout?!) + // (or detect a HOSTDP reset command??? (dma finish)) + } + per_hostdma_reset(); + } + + return HOSTDMA_SUCCESS; +} + +/** + * @brief + */ +bool per_hostdma_is_bus_available() { + uint16_t status = *pHOST_STATUS; + return !(status & DMA_RDY) && (status & ALLOW_CNFG); +} + +/*----- Static function implementations ------------------------------*/ + +/** + * @brief Software interrupt requested by the host. + * In our implementation the host uses to tell us that it's + * busy doing a host write operation. Hence, we cancel our pending + * operation. + */ +__attribute__((interrupt_handler)) static void _hostdp_status_isr(void) { + + *pHOST_STATUS |= HIRQ; // write 1 to clear + ssync(); + g_reject_host_read = true; + // *pHOST_STATUS &= ~HSHK; + // ssync(); +} + +/** + * + */ +__attribute__((interrupt_handler)) static void _hostdp_dma1_isr(void) { + + // Error check + if (!(*pDMA1_IRQ_STATUS & DMA_DONE)) { + // *pHOST_STATUS |= HSHK; // signal error to host + // ssync(); + return; // @TODO: HANDLE ERRORS + } + + *pDMA1_IRQ_STATUS = DMA_DONE; // write 1 to clear + ssync(); + *pHOST_STATUS = DMA_CMPLT; + ssync(); +} + +/** + * @brief Happens when the host has read the entire FIFO. We signal + * DMA_CMPLT so that the host can break out of it's blocking + * operation. + */ +__attribute__((interrupt_handler)) static void _host_read_done_isr(void) { + + // // Error check + // if (!(*pHOST_STATUS & HOSTRD_DONE)) { + // // *pHOST_STATUS |= HSHK; // signal error to host + // // ssync(); + // return; // @TODO: HANDLE ERRORS + // } + + *pHOST_STATUS = HOSTRD_DONE; // write 1 to clear + ssync(); + *pHOST_STATUS = DMA_CMPLT; + ssync(); +} + +static inline bool _is_bus_available_hardware() { + uint16_t status = *pHOST_STATUS; + return !(status & DMA_RDY) && (status & ALLOW_CNFG); +} + +static void _test_buffer() { + volatile uint16_t *ptr = (uint16_t*)(0x00000010); + uint16_t number = 0; + int j; + for (j = 0; j < 64; j++) { + *ptr++ = number++; + } + *(volatile uint16_t*)0x00000002 = 0x1234; + *(volatile uint16_t*)0x00000004 = 0xAABB; + *(volatile uint16_t*)0x00000006 = 0xCCDD; + *(volatile uint16_t*)0x00000008 = 0xEEFF; +} \ No newline at end of file diff --git a/dsp/src/kernel/peripheral/per_hostdma.c.old b/dsp/src/kernel/peripheral/per_hostdma.c.old new file mode 100644 index 00000000..e415a77f --- /dev/null +++ b/dsp/src/kernel/peripheral/per_hostdma.c.old @@ -0,0 +1,241 @@ +#include +#include +#include + +#include +#include + +#include "per_hostdma.h" +#include "per_gpio.h" + +/*----- Macros -------------------------------------------------------*/ + +// #define PGMUX_HOSTDP 0x2800 +// #define PGFER_SPI 0x001e +// #define PGFER_UART 0x0180 +// #define PGFER_HOSTDP 0xf800 +// #define PHFER_HOSTDP 0xffff + +/*----- Static function prototypes -----------------------------------*/ + +/*----- Static variable definitions ----------------------------------*/ + +static bool g_initialised = false; +static bool g_reject_host_read = false; + +/*----- Extern variable definitions ----------------------------------*/ + +__attribute__((interrupt_handler)) static void _hostdp_status_isr(void); +__attribute__((interrupt_handler)) static void _hostdp_dma1_isr(void); +__attribute__((interrupt_handler)) static void _host_read_done_isr(void); +static inline bool _is_bus_available_hardware(); +static void _test_buffer(); + +/*----- Extern function implementations ------------------------------*/ + +void per_hostdma_init() { + + // Register HOSTDP status interrupt + *pSIC_IAR6 |= P49_IVG(10); + ssync(); + *pEVT10 = &_hostdp_status_isr; + ssync(); + *pSIC_IMASK1 |= IRQ_HOSTDP_STATUS; + ssync(); + int i; + asm volatile("cli %0; bitset(%0, 10); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + // Register HOST READ DONE interrupt + *pSIC_IAR6 |= P50_IVG(13); + ssync(); + *pEVT13 = &_host_read_done_isr; + ssync(); + *pSIC_IMASK1 |= IRQ_HOSTRD_DONE; + ssync(); + asm volatile("cli %0; bitset(%0, 13); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + // Register DMA1 HOSTDP interrupt + *pSIC_IAR3 |= P28_IVG(12); + ssync(); + *pEVT12 = &_hostdp_dma1_isr; + ssync(); + *pSIC_IMASK0 |= IRQ_DMA1; + ssync(); + asm volatile("cli %0; bitset(%0, 12); sti %0; csync;" : "=d"(i)); // Unmask in the core event processor + ssync(); + + _test_buffer(); + + per_hostdma_reset(); + g_initialised = true; +} + +void per_hostdma_reset() { + // Configure HostDMA + // *pHOST_STATUS = 0x000C; // reset: DMA_CMPLT | FIFOEMPTY + // ssync(); + *pHOST_CONTROL = BDR | EHW | EHR | HOSTDP_DATA_SIZE | HOSTDP_EN; // @TODO: this necessary every time during DSP transfer? + ssync(); +} + +/** + * @brief Transfers a block of data to the host. + * + * @param host_address Address in the host's virtual memory map to write to. + * @param words Buffer of data + * @param word_count Number of words, must be atleast 2 + * + * @return 0 on success, otherwise an error + */ +t_hostdma_status per_hostdma_transfer(uint32_t host_address, uint16_t *words, uint16_t word_count) { + + if (!g_initialised) + return HOSTDMA_UNINITIALISED; + + // First check to quickly see if the host is occupied right now. + // This does not mean we are free to go yet since we need a handshake + // to occur between us and the host in order to safely send data. + if (!_is_bus_available_hardware()) + return HOSTDMA_BUS_OCCUPIED; + + // Calculate block count; Each block is a full FIFO unless there + // are less words remaining. Only exception is when the block has + // only one word left, then we add an additional word for padding. + // This is to make burst mode happy. + uint16_t block_count = ((uint32_t)word_count + 15) / 16; + + // Prepare a header for transfer first + *(volatile uint16_t*)0x00000000 = word_count; + *(volatile uint16_t*)0x00000002 = block_count; + *(volatile uint16_t*)0x00000004 = host_address & 0xFFFF; + *(volatile uint16_t*)0x00000006 = (host_address >> 16) & 0xFFFF; + *(volatile uint16_t*)0x00000008 = (uint32_t)words & 0xFFFF; + *(volatile uint16_t*)0x0000000A = ((uint32_t)words >> 16) & 0xFFFF; + + // @TODO: Make it unable to handshake if handshake bit is already set by host! + + // Signal to host using handshake bit that we want to initiate a host_read. + *pHOST_STATUS |= HSHK; + ssync(); + while (*pHOST_STATUS & HSHK) { + + // ... Wait for host to acknowledge our request by clearing HSHK bit, + // this happens when the last data word is read by the host. + + // If the host issues a HOST STATUS IRQ it means the host already claimed + // the bus. + if (g_reject_host_read) { + *pHOST_STATUS &= ~HSHK; + ssync(); + g_reject_host_read = false; + return HOSTDMA_BUS_OCCUPIED; + } + } + + while (!(*pHOST_STATUS & DMA_CMPLT)) { + // @TODO: Handle host-side errors that cause DSP to infinitely stall here! + // (maybe using bus timeout?!) + // (or detect a HOSTDP reset command??? (dma finish)) + } + per_hostdma_reset(); + + // Block transfer loop; All we have to do is wait for the DMA + // to happen, and reset the HostDMA peripheral afterwards. + uint16_t i; + for (i = 0; i < block_count; i++) { + while (!(*pHOST_STATUS & DMA_CMPLT)) { + // @TODO: Handle host-side errors that cause DSP to infinitely stall here! + // (maybe using bus timeout?!) + // (or detect a HOSTDP reset command??? (dma finish)) + } + per_hostdma_reset(); + } + + return HOSTDMA_SUCCESS; +} + +/** + * @brief + */ +bool per_hostdma_is_bus_available() { + uint16_t status = *pHOST_STATUS; + return !(status & DMA_RDY) && (status & ALLOW_CNFG); +} + +/*----- Static function implementations ------------------------------*/ + +/** + * @brief Software interrupt requested by the host. + * In our implementation the host uses to tell us that it's + * busy doing a host write operation. Hence, we cancel our pending + * operation. + */ +__attribute__((interrupt_handler)) static void _hostdp_status_isr(void) { + + *pHOST_STATUS |= HIRQ; // write 1 to clear + ssync(); + g_reject_host_read = true; + // *pHOST_STATUS &= ~HSHK; + // ssync(); +} + +/** + * + */ +__attribute__((interrupt_handler)) static void _hostdp_dma1_isr(void) { + + // Error check + if (!(*pDMA1_IRQ_STATUS & DMA_DONE)) { + // *pHOST_STATUS |= HSHK; // signal error to host + // ssync(); + return; // @TODO: HANDLE ERRORS + } + + *pDMA1_IRQ_STATUS = DMA_DONE; // write 1 to clear + ssync(); + *pHOST_STATUS = DMA_CMPLT; + ssync(); +} + +/** + * @brief Happens when the host has read the entire FIFO. We signal + * DMA_CMPLT so that the host can break out of it's blocking + * operation. + */ +__attribute__((interrupt_handler)) static void _host_read_done_isr(void) { + + *pHOST_STATUS = HOSTRD_DONE; // write 1 to clear + ssync(); + + // // Error check + // if (!(*pHOST_STATUS & HOSTRD_DONE)) { + // // *pHOST_STATUS |= HSHK; // signal error to host + // // ssync(); + // return; // @TODO: HANDLE ERRORS + // } + + // *pHOST_STATUS = HOSTRD_DONE; // write 1 to clear + // ssync(); + *pHOST_STATUS = DMA_CMPLT; + ssync(); +} + +static inline bool _is_bus_available_hardware() { + uint16_t status = *pHOST_STATUS; + return !(status & DMA_RDY) && (status & ALLOW_CNFG); +} + +static void _test_buffer() { + volatile uint16_t *ptr = (uint16_t*)(0x00000010); + uint16_t number = 0; + int j; + for (j = 0; j < 64; j++) { + *ptr++ = number++; + } + *(volatile uint16_t*)0x00000002 = 0x1234; + *(volatile uint16_t*)0x00000004 = 0xAABB; + *(volatile uint16_t*)0x00000006 = 0xCCDD; + *(volatile uint16_t*)0x00000008 = 0xEEFF; +} \ No newline at end of file diff --git a/dsp/src/kernel/peripheral/per_hostdma.h b/dsp/src/kernel/peripheral/per_hostdma.h new file mode 100644 index 00000000..103d144f --- /dev/null +++ b/dsp/src/kernel/peripheral/per_hostdma.h @@ -0,0 +1,30 @@ +#ifndef PER_HOSTDMA_H +#define PER_HOSTDMA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +/*----- Typedefs -----------------------------------------------------*/ + +typedef enum { + HOSTDMA_SUCCESS = 0, + HOSTDMA_UNINITIALISED, + HOSTDMA_BUS_OCCUPIED, +} t_hostdma_status; + +void per_hostdma_init(); +void per_hostdma_reset(); +t_hostdma_status per_hostdma_transfer(uint32_t host_address, uint16_t *words, uint16_t word_count); +bool per_hostdma_is_bus_available(); + +#ifdef __cplusplus +} +#endif +#endif +/*----- End of file --------------------------------------------------*/ diff --git a/dsp/src/kernel/peripheral/per_hostdma.h.old b/dsp/src/kernel/peripheral/per_hostdma.h.old new file mode 100644 index 00000000..103d144f --- /dev/null +++ b/dsp/src/kernel/peripheral/per_hostdma.h.old @@ -0,0 +1,30 @@ +#ifndef PER_HOSTDMA_H +#define PER_HOSTDMA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----- Includes -----------------------------------------------------*/ + +#include +#include + +/*----- Typedefs -----------------------------------------------------*/ + +typedef enum { + HOSTDMA_SUCCESS = 0, + HOSTDMA_UNINITIALISED, + HOSTDMA_BUS_OCCUPIED, +} t_hostdma_status; + +void per_hostdma_init(); +void per_hostdma_reset(); +t_hostdma_status per_hostdma_transfer(uint32_t host_address, uint16_t *words, uint16_t word_count); +bool per_hostdma_is_bus_available(); + +#ifdef __cplusplus +} +#endif +#endif +/*----- End of file --------------------------------------------------*/ diff --git a/dsp/src/kernel/service/dev_ipc.h b/dsp/src/kernel/service/dev_ipc.h new file mode 100644 index 00000000..0b2fcf24 --- /dev/null +++ b/dsp/src/kernel/service/dev_ipc.h @@ -0,0 +1,6 @@ +#ifndef DEV_IPC_H +#define DEV_IPC_H + +void dev_ipc_init(); + +#endif diff --git a/dsp/src/kernel/service/svc_cpu.c b/dsp/src/kernel/service/svc_cpu.c index 8d9e5235..7c52eab8 100644 --- a/dsp/src/kernel/service/svc_cpu.c +++ b/dsp/src/kernel/service/svc_cpu.c @@ -45,6 +45,8 @@ under the terms of the GNU Affero General Public License as published by #include "per_gpio.h" #include "per_spi.h" +#include "per_hostdma.h" +#include "dev_cpu_spi.h" #include "module.h" @@ -56,7 +58,7 @@ under the terms of the GNU Affero General Public License as published by /*----- Typedefs -----------------------------------------------------*/ -typedef enum { STATE_INIT, STATE_RUN, STATE_ERROR } t_cpu_task_state; +typedef enum { STATE_INIT, STATE_RUN, STATE_HANDSHAKE, STATE_ERROR } t_cpu_task_state; typedef enum { PARSE_START, @@ -86,6 +88,9 @@ enum e_system_msg_id { SYSTEM_PORT_STATE, SYSTEM_GET_PROFILE, SYSTEM_PROFILE, + SYSTEM_REQUEST_HOSTDMA_CONFIG, + SYSTEM_AWAIT_HOSTDMA_HANDSHAKE, + SYSTEM_CONFIRM_HOSTDMA_HANDSHAKE, }; /*----- Static variable definitions ----------------------------------*/ @@ -121,7 +126,7 @@ static t_status _handle_module_get_param_name(uint8_t *payload, uint8_t length); static t_status _handle_system_check_ready(void); static t_status _handle_system_get_port_state(void); -static t_status _handle_system_set_port_state(uint8_t *payload, uint8_t length); +// static t_status _handle_system_set_port_state(uint8_t *payload, uint8_t length); static t_status _handle_system_get_profile(void); @@ -139,6 +144,7 @@ static t_status _respond_system_port_state(uint16_t port_f, uint16_t port_g, static t_status _respond_system_profile(t_profile stats); +static int32_t g_test; /*----- Extern function implementations ------------------------------*/ void svc_cpu_task(void) { @@ -150,6 +156,7 @@ void svc_cpu_task(void) { switch (state) { case STATE_INIT: + g_test = 80000; if (_cpu_init() == SUCCESS) { state = STATE_RUN; } @@ -160,7 +167,13 @@ void svc_cpu_task(void) { /// TODO: case STATE_HANDSHAKE: case STATE_RUN: - + + if (g_test <= 0) { + per_hostdma_transfer(0xC0000000, (uint16_t*)0x00000010, 12); + g_test = 800000; + } else { + g_test--; + } // Handle received bytes. if (dev_cpu_spi_rx_dequeue(&cpu_byte) == SUCCESS) { _cpu_receive(cpu_byte); @@ -189,10 +202,14 @@ static t_status _cpu_init(void) { // Initialise CPU SPI device driver. dev_cpu_spi_init(); + per_hostdma_init(); + // _transmit_message(MSG_TYPE_SYSTEM, SYSTEM_REQUEST_HOSTDMA_CONFIG, NULL, 0); // @TODO: maybe we can do without and rely on timing + // _transmit_message(MSG_TYPE_SYSTEM, SYSTEM_READY, NULL, 0); /// TODO: Handhsake. + result = SUCCESS; return result; @@ -336,6 +353,11 @@ static t_status _handle_system_message(uint8_t msg_id, uint8_t *payload, case SYSTEM_GET_PROFILE: result = _handle_system_get_profile(); break; + + case SYSTEM_AWAIT_HOSTDMA_HANDSHAKE: + // per_hostdma_send_delayed_handshake_signal(); + result = SUCCESS; + break; default: result = ERROR; @@ -367,8 +389,9 @@ static t_status _handle_module_get_param_value(uint8_t *payload, static t_status _handle_module_set_param_value(uint8_t *payload, uint8_t length) { + /// TODO: Union struct for message parsing. - int16_t module_id = (payload[1] << 8) | payload[0]; + // int16_t module_id = (payload[1] << 8) | payload[0]; uint16_t param_index = (payload[3] << 8) | payload[2]; diff --git a/dsp/src/modules/system23/delayline.c b/dsp/src/modules/system23/delayline.c new file mode 100644 index 00000000..f6b9f724 --- /dev/null +++ b/dsp/src/modules/system23/delayline.c @@ -0,0 +1,34 @@ +#include "fix.h" +#include "types.h" +#include "fract_math.h" +#include + +#include "delayline.h" + +// #define FR32_ZERO_POINT_FIVE 0x80000000 + +#define MEM_OFFSET (16*1024*1024) +extern void sdram_start; + + + +void delayline_init(t_delayline *const me, int samples, fract32 feedback, fract32 mix) { + me->buffer = (fract32*)((uint8_t*)&sdram_start+MEM_OFFSET); + memset(me->buffer, 0, samples*sizeof(fract32)); + me->index = 0; + me->size = samples; + me->feedback = feedback; + me->mix = mix; + me->one_minus_mix = sub_fr1x32(FR32_MAX, mix); +} + +fract32 delayline_process(t_delayline *const me, fract32 input) { + + fract32 delayed_sample = me->buffer[me->index]; + me->buffer[me->index] = input + mult_fr1x32x32(delayed_sample, me->feedback); + me->index = (me->index + 1) % me->size; + + fract32 output = add_fr1x32(mult_fr1x32x32(input, me->one_minus_mix), mult_fr1x32x32(delayed_sample, me->mix)); + return output; +} + diff --git a/dsp/src/modules/system23/delayline.h b/dsp/src/modules/system23/delayline.h new file mode 100644 index 00000000..24635c83 --- /dev/null +++ b/dsp/src/modules/system23/delayline.h @@ -0,0 +1,20 @@ +#ifndef DELAYLINE_H +#define DELAYLINE_H + +#include "types.h" + +typedef struct { + fract32 *buffer; + int index; + int size; + fract32 feedback; + fract32 mix; + fract32 one_minus_mix; +} t_delayline; + +void delayline_init(t_delayline *const, int samples, fract32 feedback, fract32 mix); +fract32 delayline_process(t_delayline *const, fract32 input); + +// void delayline_free(); + +#endif \ No newline at end of file diff --git a/dsp/src/modules/system23/system23.c b/dsp/src/modules/system23/system23.c new file mode 100644 index 00000000..8d4f44da --- /dev/null +++ b/dsp/src/modules/system23/system23.c @@ -0,0 +1,287 @@ +/*---------------------------------------------------------------------- + + This file is part of Freetribe + + https://github.com/bangcorrupt/freetribe + + License + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + AGPL-3.0-or-later + + Freetribe is free software: you can redistribute it and/or modify it +under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Freetribe is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Copyright bangcorrupt 2023 + +----------------------------------------------------------------------*/ + +/** + * @file template_module.c + * + * @brief Template for DSP module source files. + * + */ + +/*----- Includes -----------------------------------------------------*/ + +#include "fract_math.h" +#include "types.h" + +#include "module.h" + +#include "utils.h" + +#include "aleph.h" +#include "aleph_monovoice.h" + +#include "delayline.h" + +/*----- Macros -------------------------------------------------------*/ + +#define MEMPOOL_SIZE (0x1000) + +#define PARAM_NOTE_FREQ 2 +#define PARAM_GATE_TICKS 3 +#define PARAM_NEXT_NOTE_FREQ 4 +#define PARAM_TICKS_TIL_NEXT 5 +#define PARAM_CUTOFF 10 +#define PARAM_RESONANCE 11 + +/*----- Typedefs -----------------------------------------------------*/ + +typedef struct { + Aleph_MonoVoice voice[1]; + bool gate_on; + uint32_t gate_ticks; + fract32 note_freq; + uint32_t ticks_til_next; + fract32 next_note_freq; + + float cutoff; + float resonance; +} t_module; + +/*----- Static variable definitions ----------------------------------*/ + +__attribute__((section(".l1.data.a"))) +__attribute__((aligned(32))) static char g_mempool[MEMPOOL_SIZE]; +// __attribute__((aligned(32))) static char g_sample[1024]; + + +// static float TUNING[256] = { // @TODO: move this to param change buffers +// 0.500000, 0.503906, 0.507812, 0.511719, 0.515625, 0.519531, 0.523438, 0.527344, +// 0.531250, 0.535156, 0.539062, 0.542969, 0.546875, 0.550781, 0.554688, 0.558594, +// 0.562500, 0.566406, 0.570312, 0.574219, 0.578125, 0.582031, 0.585938, 0.589844, +// 0.593750, 0.597656, 0.601562, 0.605469, 0.609375, 0.613281, 0.617188, 0.621094, +// 0.625000, 0.628906, 0.632812, 0.636719, 0.640625, 0.644531, 0.648438, 0.652344, +// 0.656250, 0.660156, 0.664062, 0.667969, 0.671875, 0.675781, 0.679688, 0.683594, +// 0.687500, 0.691406, 0.695312, 0.699219, 0.703125, 0.707031, 0.710938, 0.714844, +// 0.718750, 0.722656, 0.726562, 0.730469, 0.734375, 0.738281, 0.742188, 0.746094, +// 0.750000, 0.753906, 0.757812, 0.761719, 0.765625, 0.769531, 0.773438, 0.777344, +// 0.781250, 0.785156, 0.789062, 0.792969, 0.796875, 0.800781, 0.804688, 0.808594, +// 0.812500, 0.816406, 0.820312, 0.824219, 0.828125, 0.832031, 0.835938, 0.839844, +// 0.843750, 0.847656, 0.851562, 0.855469, 0.859375, 0.863281, 0.867188, 0.871094, +// 0.875000, 0.878906, 0.882812, 0.886719, 0.890625, 0.894531, 0.898438, 0.902344, +// 0.906250, 0.910156, 0.914062, 0.917969, 0.921875, 0.925781, 0.929688, 0.933594, +// 0.937500, 0.941406, 0.945312, 0.949219, 0.953125, 0.957031, 0.960938, 0.964844, +// 0.968750, 0.972656, 0.976562, 0.980469, 0.984375, 0.988281, 0.992188, 0.996094, +// 1.000000, 1.007874, 1.015748, 1.023622, 1.031496, 1.039370, 1.047244, 1.055118, +// 1.062992, 1.070866, 1.078740, 1.086614, 1.094488, 1.102362, 1.110236, 1.118110, +// 1.125984, 1.133858, 1.141732, 1.149606, 1.157480, 1.165354, 1.173228, 1.181102, +// 1.188976, 1.196850, 1.204724, 1.212598, 1.220472, 1.228346, 1.236220, 1.244094, +// 1.251969, 1.259843, 1.267717, 1.275591, 1.283465, 1.291339, 1.299213, 1.307087, +// 1.314961, 1.322835, 1.330709, 1.338583, 1.346457, 1.354331, 1.362205, 1.370079, +// 1.377953, 1.385827, 1.393701, 1.401575, 1.409449, 1.417323, 1.425197, 1.433071, +// 1.440945, 1.448819, 1.456693, 1.464567, 1.472441, 1.480315, 1.488189, 1.496063, +// 1.503937, 1.511811, 1.519685, 1.527559, 1.535433, 1.543307, 1.551181, 1.559055, +// 1.566929, 1.574803, 1.582677, 1.590551, 1.598425, 1.606299, 1.614173, 1.622047, +// 1.629921, 1.637795, 1.645669, 1.653543, 1.661417, 1.669291, 1.677165, 1.685039, +// 1.692913, 1.700787, 1.708661, 1.716535, 1.724409, 1.732283, 1.740157, 1.748031, +// 1.755906, 1.763780, 1.771654, 1.779528, 1.787402, 1.795276, 1.803150, 1.811024, +// 1.818898, 1.826772, 1.834646, 1.842520, 1.850394, 1.858268, 1.866142, 1.874016, +// 1.881890, 1.889764, 1.897638, 1.905512, 1.913386, 1.921260, 1.929134, 1.937008, +// 1.944882, 1.952756, 1.960630, 1.968504, 1.976378, 1.984252, 1.992126, 2.000000 +// }; + +// static float t_tb303pattern g_pattern_notes[255]; // full module transmit buffer + +static t_Aleph g_aleph; +static t_module g_module; +static t_delayline g_delayline; + +/*----- Extern variable definitions ----------------------------------*/ + +/*----- Static function prototypes -----------------------------------*/ + +// static uint32_t xorshift32(uint32_t *state) { +// uint32_t x = *state; +// x ^= x << 13; +// x ^= x >> 17; +// x ^= x << 5; +// return *state = x; +// } + +/*----- Extern function implementations ------------------------------*/ + +/** + * @brief Initialise module. + */ +void module_init(void) { + + Aleph_init(&g_aleph, SAMPLERATE, g_mempool, MEMPOOL_SIZE, NULL); + Aleph_MonoVoice_init(&g_module.voice[0], &g_aleph); + Aleph_MonoVoice_set_amp(&g_module.voice[0], FR32_MAX); + Aleph_MonoVoice_set_amp_slew(&g_module.voice[0], SLEW_1MS); + Aleph_MonoVoice_set_shape(&g_module.voice[0], WAVEFORM_SHAPE_SAW); + Aleph_MonoVoice_set_freq_offset(&g_module.voice[0], FIX16_ONE); + Aleph_MonoVoice_set_cutoff(&g_module.voice[0], 0x326f6abb); + Aleph_MonoVoice_set_res(&g_module.voice[0], FR32_MAX); + + delayline_init(&g_delayline, 19200, 0x43000000, 0x33333333); // ((60000/150)/1000)*48000 + +} + +/** + * @brief Process audio. + * + * @param[in] in Pointer to input buffer. + * @param[out] out Pointer to input buffer. + */ +void module_process(fract32 *in, fract32 *out) { + + if (g_module.gate_ticks > 0) + g_module.gate_ticks--; + + if (g_module.gate_on) { + // Wait til next note has to play when gate hold is over + if (g_module.gate_ticks == 0) { + g_module.gate_on = false; + g_module.gate_ticks = g_module.ticks_til_next; + } + } else { + // Start playing cached note if the DSP is earlier than the CPU can feed us notes + if (g_module.gate_ticks == 0) { + g_module.gate_on = true; + g_module.note_freq = g_module.next_note_freq; + } + } + + + Aleph_MonoVoice_set_amp(&g_module.voice[0], g_module.gate_on ? FR32_MAX : 0x00000000); + Aleph_MonoVoice_set_freq(&g_module.voice[0], g_module.note_freq); + fract32 sample = Aleph_MonoVoice_next(&g_module.voice[0]); + + // fract32 sample; + // if (g_module.gate_on) { + // Aleph_MonoVoice_set_freq(&g_module.voice[0], g_module.note_freq); + // sample = Aleph_MonoVoice_next(&g_module.voice[0]); + // } else { + // sample = 0x00000000; + // } + + // Apply delay + sample = shr_fr1x32(sample, 1); // temporarily lower amplitude to prevent clipping + sample = delayline_process(&g_delayline, sample); + + + out[0] = sample; + out[1] = sample; +} + +/** + * @brief Set parameter. + * + * @param[in] param_index Index of parameter to set. + * @param[in] value Value of parameter. + */ +void module_set_param(uint16_t param_index, int32_t value) { + + switch (param_index) { + + case PARAM_NOTE_FREQ: + g_module.note_freq = value; + break; + case PARAM_GATE_TICKS: + g_module.gate_ticks = value; + g_module.gate_on = true; // reset to make sure + break; + case PARAM_NEXT_NOTE_FREQ: + g_module.next_note_freq = value; + break; + case PARAM_TICKS_TIL_NEXT: + g_module.ticks_til_next = value; + break; + + case PARAM_CUTOFF: + g_module.cutoff = value; + Aleph_MonoVoice_set_cutoff(&g_module.voice[0], value); + break; + case PARAM_RESONANCE: + g_module.resonance = value; + Aleph_MonoVoice_set_res(&g_module.voice[0], value); + break; + + } +} + +/** + * @brief Get parameter. + * + * @param[in] param_index Index of parameter to get. + * + * @return value Value of parameter. + */ +int32_t module_get_param(uint16_t param_index) { + + int32_t value = 0; + + switch (param_index) { + default: + break; + } + + return value; +} + +/** + * @brief Get number of parameters. + * + * @return Number of parameters + */ +uint32_t module_get_param_count(void) { return 0; } + +/** + * @brief Get name of parameter at index. + * + * @param[in] param_index Index pf parameter. + * @param[out] text Buffer to store string. + * Must provide 'MAX_PARAM_NAME_LENGTH' + * bytes of storage. + */ +void module_get_param_name(uint16_t param_index, char *text) { + + switch (param_index) { + + default: + copy_string(text, "Unknown", MAX_PARAM_NAME_LENGTH); + break; + } +} + + +/*----- Static function implementations ------------------------------*/ + +/*----- End of file --------------------------------------------------*/