This is an automated email from the ASF dual-hosted git repository. wes3 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
The following commit(s) were added to refs/heads/master by this push: new af296c0 hw/mcu/dialog: SNC (#1868) af296c0 is described below commit af296c05b67a2fe77d8a8b0651dcfa6757c11185 Author: wes3 <wi...@runtime.io> AuthorDate: Wed Jun 19 16:55:39 2019 -0700 hw/mcu/dialog: SNC (#1868) * hw/mcu/dialog: SNC Sensor node controller API. This commit allows users to create SNC programs using the macros in da1469x_snc.h and run them either under software control or use the PDC to start them. Note that PDC control of the SNC is not contained here; the PDC api already contain all that is needed to have the PDC start the SNC. Added in this commit is a test program (hosted) that illustrates how a program can be created and run under software control. The code for the test program is in hw/mcu/dialong/da1469x/hosttest. This directory is also intended for other host tests and not just the SNC test. * hw/mcu/dialog: SNC This commit adds interrupt registering and handling code for the SNC. Adds additional test cases to test the interrupt handling API. Addressed PR review comments as well. * hw/mcu/dialog/da1469x/hosttest: Add license file to syscfg.yml --- .../hosttest/include/da1469x_test/da1469x_test.h | 43 ++ hw/mcu/dialog/da1469x/hosttest/pkg.yml | 30 ++ hw/mcu/dialog/da1469x/hosttest/src/da1469x_test.c | 42 ++ .../src/testcases/da1469x_snc_test_cases.c | 467 +++++++++++++++++++ hw/mcu/dialog/da1469x/hosttest/syscfg.yml | 26 ++ hw/mcu/dialog/da1469x/include/mcu/da1469x_snc.h | 514 +++++++++++++++++++++ hw/mcu/dialog/da1469x/src/da1469x_snc.c | 212 +++++++++ 7 files changed, 1334 insertions(+) diff --git a/hw/mcu/dialog/da1469x/hosttest/include/da1469x_test/da1469x_test.h b/hw/mcu/dialog/da1469x/hosttest/include/da1469x_test/da1469x_test.h new file mode 100644 index 0000000..a040641 --- /dev/null +++ b/hw/mcu/dialog/da1469x/hosttest/include/da1469x_test/da1469x_test.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _DA1469X_TEST_H +#define _DA1469X_TEST_H + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stddef.h> +#include "os/mynewt.h" +#include "testutil/testutil.h" + +#ifdef __cplusplus +extern "C" { +#endif + +TEST_CASE_DECL(da1469x_snc_test_case_1); +TEST_CASE_DECL(da1469x_snc_test_case_2); +TEST_CASE_DECL(da1469x_snc_test_case_3); +TEST_SUITE_DECL(da1469x_test_suite); + +#ifdef __cplusplus +} +#endif + +#endif /* _DA1469X_TEST_H */ diff --git a/hw/mcu/dialog/da1469x/hosttest/pkg.yml b/hw/mcu/dialog/da1469x/hosttest/pkg.yml new file mode 100644 index 0000000..c10f645 --- /dev/null +++ b/hw/mcu/dialog/da1469x/hosttest/pkg.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: hw/mcu/dialog/da1469x/hosttest +pkg.type: lib +pkg.description: "Hosted da1469x unit tests." +pkg.author: "Apache Mynewt <d...@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/test/testutil" + - "@apache-mynewt-core/hw/mcu/dialog" + +pkg.init: + da1469x_hosttest_init: 'MYNEWT_VAL(DA1469x_HOSTTEST_SYSINIT_STAGE)' diff --git a/hw/mcu/dialog/da1469x/hosttest/src/da1469x_test.c b/hw/mcu/dialog/da1469x/hosttest/src/da1469x_test.c new file mode 100644 index 0000000..49fdcad --- /dev/null +++ b/hw/mcu/dialog/da1469x/hosttest/src/da1469x_test.c @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stddef.h> +#include "os/mynewt.h" +#include "syscfg/syscfg.h" +#include "testutil/testutil.h" +#include "da1469x_test/da1469x_test.h" + +TEST_SUITE(da1469x_test_suite) +{ +#if MYNEWT_VAL(TESTBENCH_DA1469X_SNC == 1) + da1469x_snc_test_case_1(); + da1469x_snc_test_case_2(); + da1469x_snc_test_case_3(); +#endif +} + +void +da1469x_hosttest_init(void) +{ + TEST_SUITE_REGISTER(da1469x_test_suite); +} diff --git a/hw/mcu/dialog/da1469x/hosttest/src/testcases/da1469x_snc_test_cases.c b/hw/mcu/dialog/da1469x/hosttest/src/testcases/da1469x_snc_test_cases.c new file mode 100644 index 0000000..f35c3ec --- /dev/null +++ b/hw/mcu/dialog/da1469x/hosttest/src/testcases/da1469x_snc_test_cases.c @@ -0,0 +1,467 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "syscfg/syscfg.h" +#include "da1469x_test/da1469x_test.h" + +/* XXX: not tested + - WADAD register addresses for op1 and op2 + - WADVA using registers. + - RDCGR reigster for addr1 and addr2 +*/ + +#if MYNEWT_VAL(TESTBENCH_DA1469X_SNC == 1) +#include <modlog/modlog.h> +#include "mcu/da1469x_snc.h" + +#define SNC_TEST_XOR_MASK (0x003C00F0) + +/* + * This is an ugly hack, but to use the 64-bit macros it requires hard-coded + * addresses. These are defined at the bottom of the stack so highly unlikely + * they will get used as the stack is extremely large. + */ +uint32_t da1469x_test_var0; +uint32_t da1469x_test_var1; +uint32_t da1469x_test_var2; +uint32_t da1469x_test_var3; +uint32_t da1469x_test_var4; +uint32_t da1469x_test_var5; +uint32_t da1469x_test_var6; +uint32_t da1469x_test_var7; +uint32_t da1469x_test_var8; +uint32_t da1469x_test_var9; +uint32_t da1469x_test_var10; +uint32_t da1469x_test_var11; +uint32_t da1469x_test_var12; +uint32_t da1469x_test_var13; +uint32_t da1469x_test_var14; +uint32_t da1469x_test_var15; +uint32_t da1469x_test_var16; + +/* + * The test program. Note that the X: in the comment refers to the "line" + * number of the program (the offset into the program array). Some instructions + * are 64-bits which is why one initializer occupies two "lines". + */ +uint32_t snc_program[] = +{ + /* 0: No operation */ + SNC_CMD_NOOP(), + + /* 1: Delay 10 ticks */ + SNC_CMD_DEL(10), + + /* 2: Increment test var 1 by 1 */ + SNC_CMD_INC_BY_1(&da1469x_test_var1), + + /* 3: Increment test var 2 by 4 */ + SNC_CMD_INC_BY_4(&da1469x_test_var2), + + /* 4: + * This compares bit position 2 of test var 2 with 1. Sets the EQUALHIGH + * flag if set (which it is in this case). + */ + SNC_CMD_RDCBI_RAM(&da1469x_test_var2, 2), + + /* 5: + * Branch if EQUALHIGH flag is true. This branch should move past the + * next two instructions. + */ + SNC_CMD_COBR_EQ_DIR(&snc_program[8]), + + /* 6, 7: These two instructions should get skipped*/ + SNC_CMD_INC_BY_4(&da1469x_test_var3), + SNC_CMD_INC_BY_4(&da1469x_test_var3), + + /* 8: Just cause I feel like it */ + SNC_CMD_NOOP(), + + /* 9, 10: These two instructions should cause a loop here 10 times */ + SNC_CMD_INC_BY_1(&da1469x_test_var4), + SNC_CMD_COBR_LOOP(&snc_program[9], 10), + + /* 11, 12: + * These two instructions should cause a loop here 20 times. + * Purpose here is to see if the loop counter is a decrementing counter + * and after it gets exhausted it restarts. + */ + SNC_CMD_INC_BY_1(&da1469x_test_var5), + SNC_CMD_COBR_LOOP(&snc_program[11], 20), + + /* 13, 14: Move the contents of test var 7 to test var 8 */ + SNC_CMD_WADAD_RAM2RAM(&da1469x_test_var8, SNC_WADAD_AM1_DIRECT, + SNC_WADAD_AM2_DIRECT, &da1469x_test_var7), + + /* 15, 16: + * var 9 is pointer; move contents of what var 9 points to, to what var + * 10 points to (var 10 is a pointer). + */ + SNC_CMD_WADAD_RAM2RAM(&da1469x_test_var10, SNC_WADAD_AM1_INDIRECT, + SNC_WADAD_AM2_INDIRECT, &da1469x_test_var9), + + /* 17, 18: XOR */ + SNC_CMD_TOBRE_RAM(&da1469x_test_var12, SNC_TEST_XOR_MASK), + + /* 19, 20: XOR register (SNC_CTRL_REG). Should toggle both IRQ config bits */ + SNC_CMD_TOBRE_REG(0x50020C00, 0xC0), + + /* 21, 22: Moves immediate (the address of var 12) into var 13 */ + SNC_CMD_WADVA_DIR_RAM(&da1469x_test_var13, &da1469x_test_var12), + + /* 23, 24: Moves immediate into address pointed to by var14 */ + SNC_CMD_WADVA_IND_RAM(&da1469x_test_var14, 0x33333333), + + /* 25, 26: + * Compare the contents of test var 9 and 7. This instruction basically + * does this: if (var9 > var7) set GREATERVAL_FLAG. In this case, var9 + * should be greater than var 7 + */ + SNC_CMD_RDCGR_RAMRAM(&da1469x_test_var9, &da1469x_test_var7), + + /* 27: + * Branch if EQUALHIGH flag is true. This branch should move past the + * next instruction + */ + SNC_CMD_COBR_GT_DIR(&snc_program[30]), + + /* 28, 29: These two instructions should get skipped*/ + SNC_CMD_INC_BY_4(&da1469x_test_var0), + SNC_CMD_INC_BY_4(&da1469x_test_var0), + + /* 30: Increment test var 0 by 1 */ + SNC_CMD_INC_BY_1(&da1469x_test_var0), + + /* 31: Check if SW contrl bit is set in SNC control register. It should! */ + SNC_CMD_RDCBI_REG(0x50020C00, SNC_SNC_CTRL_REG_SNC_SW_CTRL_Pos), + + /* 32: Branch past next instruction if EQUALHIGH_FLAG is set (should be!) */ + SNC_CMD_COBR_EQ_DIR(&snc_program[34]), + SNC_CMD_INC_BY_4(&da1469x_test_var16), + + /* 34: Sleep (program ends) */ + SNC_CMD_SLEEP() +}; + +/* Number of words (32-bits) in the static program */ +#define SNC_STATIC_PROGRAM_NUM_WORDS (sizeof(snc_static_program) / 4) + +TEST_CASE(da1469x_snc_test_case_1) +{ + int rc; + + MODLOG_INFO(LOG_MODULE_TEST, "DA1469x snc test 1"); + + /* + * Initialize to some non-zero number. The test program should increment + * var1 by 1 and var2 by 4 using the increment instruction. + */ + da1469x_test_var1 = 10; + da1469x_test_var2 = 10; + + /* Initialize test var 7 with a value */ + da1469x_test_var7 = 0x12345678; + + /* Make test var 9 a pointer that points to test var 7 */ + da1469x_test_var9 = (uint32_t)&da1469x_test_var7; + + /* Make test var 10 a pointer that points to test var 11 */ + da1469x_test_var10 = (uint32_t)&da1469x_test_var11; + + /* test var 12 will test xor */ + da1469x_test_var12 = 0xC3A78F; + + /* Test var 14 is a pointer to var 15 */ + da1469x_test_var14 = (uint32_t)&da1469x_test_var15; + + /* Configure the SNC (base address and divider) */ + rc = da1469x_snc_config(&snc_program, SNC_CLK_DIV_1); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc config failed"); + TEST_ASSERT_FATAL(0); + } + + /* Initialize the SNC */ + rc = da1469x_snc_sw_init(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc init failed"); + TEST_ASSERT_FATAL(0); + } + + /* + * Make sure IRQ config bits are 0. The init function clears these but + * we do it here as well. + */ + da1469x_snc_irq_config(SNC_IRQ_MASK_NONE, NULL, NULL); + if ((SNC->SNC_CTRL_REG & SNC_SNC_CTRL_REG_SNC_IRQ_CONFIG_Msk) != 0) { + MODLOG_INFO(LOG_MODULE_TEST, "snc irq config failed"); + TEST_ASSERT_FATAL(0); + } + + /* Start the program */ + da1469x_snc_sw_start(); + + /* Wait 1 second for program to finish. */ + os_time_delay(OS_TICKS_PER_SEC); + if (!da1469x_snc_program_is_done()) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed (not done)"); + da1469x_snc_sw_stop(); + TEST_ASSERT_FATAL(0); + } + + /* Check test var 1 and test var 2 have correct values */ + if (da1469x_test_var1 != 11) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: inc by 1"); + TEST_ASSERT_FATAL(0); + } + if (da1469x_test_var2 != 14) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: inc by 4"); + TEST_ASSERT_FATAL(0); + } + + /* Test var 3 should be 0 */ + if (da1469x_test_var3 != 0) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: RDCBI and/or COBR_EQ"); + TEST_ASSERT_FATAL(0); + } + + /* Test var 4 should be 10 */ + if (da1469x_test_var4 != 11) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: COBR loop. tv4=%lu", + da1469x_test_var4); + TEST_ASSERT_FATAL(0); + } + + /* Test var 5 should be 20 */ + if (da1469x_test_var5 != 21) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: COBR loop 2. tv5=%lu", + da1469x_test_var5); + TEST_ASSERT_FATAL(0); + } + + /* Test var 8 should be equal to test var 7 */ + if (da1469x_test_var8 != da1469x_test_var7) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: WADAD direct. tv7=%lx" + " tv8=%lx", + da1469x_test_var7, da1469x_test_var8); + TEST_ASSERT_FATAL(0); + } + + /* Test var 11 should have value in test var 7 */ + if (da1469x_test_var11 != da1469x_test_var7) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: WADAD indirect. tv7=%lx" + " tv11=%lx", + da1469x_test_var7, da1469x_test_var11); + TEST_ASSERT_FATAL(0); + } + + + /* Test var 12 should be those two values xor'd (should equal 0xFFA77F) */ + if (da1469x_test_var12 != (SNC_TEST_XOR_MASK ^ 0xC3A78F)) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: TOBRE. tv12=%lx", + da1469x_test_var12); + TEST_ASSERT_FATAL(0); + } + + /* SNC control registers should have both IRQ bits set */ + if ((SNC->SNC_CTRL_REG & SNC_SNC_CTRL_REG_SNC_IRQ_CONFIG_Msk) != + SNC_SNC_CTRL_REG_SNC_IRQ_CONFIG_Msk) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: TOBRE register %lx", + SNC->SNC_CTRL_REG); + TEST_ASSERT_FATAL(0); + } + + /* Contents of test var 13 should equal address of test var 12 */ + if (da1469x_test_var13 != (uint32_t)&da1469x_test_var12) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: WADVA direct. &tv12=%lx" + " tv13=%lx", + &da1469x_test_var12, + da1469x_test_var13); + TEST_ASSERT_FATAL(0); + } + + /* Test var 15 should be equal 0x33333333 */ + if (da1469x_test_var15 != 0x33333333) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: WADVA indirect tv15=%lx", + da1469x_test_var15); + TEST_ASSERT_FATAL(0); + } + + /* Test var 0 should be equal to 1 */ + if (da1469x_test_var0 != 1) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: RDCGR RAMRAM tv0=%lx", + da1469x_test_var0); + TEST_ASSERT_FATAL(0); + } + + /* Test var 16 should be equal to 0 */ + if (da1469x_test_var16 != 0) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: RDCBI reg tv16=%lx", + da1469x_test_var16); + TEST_ASSERT_FATAL(0); + } + + /* Check for hard fault or bus status errors */ + if (SNC->SNC_STATUS_REG & (SNC_SNC_STATUS_REG_HARD_FAULT_STATUS_Msk | + SNC_SNC_STATUS_REG_BUS_ERROR_STATUS_Msk)) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed: ERR snc status %lx", + SNC->SNC_STATUS_REG); + TEST_ASSERT_FATAL(0); + } + + da1469x_snc_sw_stop(); + rc = da1469x_snc_sw_deinit(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc s/w deinit failed"); + TEST_ASSERT_FATAL(0); + } + + MODLOG_INFO(LOG_MODULE_TEST, "snc test 1 success"); +} + +/*====================== TEST CASE 2 ===================================== +The intent of this test case is to test the interrupt API. +========================================================================*/ +uint32_t g_snc_tc2_cntr; +uint32_t snc_prog_test_case2[] = +{ + /* This should toggle the IRQ_EN bit, thus generating an interrupt */ + SNC_CMD_TOBRE_REG(0x50020C00, SNC_SNC_CTRL_REG_SNC_IRQ_EN_Msk), + SNC_CMD_SLEEP() +}; + +void snc_tc2_irq_cb(void *arg) +{ + uint32_t *tmp; + + tmp = (uint32_t *)arg; + if (tmp) { + *tmp += 1; + } +} + +TEST_CASE(da1469x_snc_test_case_2) +{ + int rc; + + MODLOG_INFO(LOG_MODULE_TEST, "DA1469x snc test 2"); + + /* Configure the SNC (base address and divider) */ + rc = da1469x_snc_config(&snc_prog_test_case2, SNC_CLK_DIV_1); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc config failed"); + TEST_ASSERT_FATAL(0); + } + + /* Initialize the SNC */ + rc = da1469x_snc_sw_init(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc init failed"); + TEST_ASSERT_FATAL(0); + } + + /* + * Register an interrupt routine. Pass it an argument as well. This + * argument points to a global counter that should get incremented once + */ + da1469x_snc_irq_config(SNC_IRQ_MASK_HOST, snc_tc2_irq_cb, &g_snc_tc2_cntr); + + /* Start the program */ + da1469x_snc_sw_start(); + + /* This program should finish very quickly */ + os_time_delay(OS_TICKS_PER_SEC / 10); + if (!da1469x_snc_program_is_done()) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test 2 failed (not done)"); + da1469x_snc_sw_stop(); + TEST_ASSERT_FATAL(0); + } + + /* Check that the counter got incremented */ + if (g_snc_tc2_cntr != 1) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed tc2=%u", g_snc_tc2_cntr); + TEST_ASSERT_FATAL(0); + } + + da1469x_snc_sw_stop(); + rc = da1469x_snc_sw_deinit(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc s/w deinit failed"); + TEST_ASSERT_FATAL(0); + } + + MODLOG_INFO(LOG_MODULE_TEST, "snc test 2 success"); +} + +/* + * This test case enables only the PDC interrupt. Should not get a SNC + * interrupt to the M33 in this case + */ +TEST_CASE(da1469x_snc_test_case_3) +{ + int rc; + + MODLOG_INFO(LOG_MODULE_TEST, "DA1469x snc test 3"); + + /* Configure the SNC (base address and divider) */ + rc = da1469x_snc_config(&snc_prog_test_case2, SNC_CLK_DIV_1); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc config failed"); + TEST_ASSERT_FATAL(0); + } + + /* Initialize the SNC */ + rc = da1469x_snc_sw_init(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc init failed"); + TEST_ASSERT_FATAL(0); + } + + /* + * Register an interrupt routine. Pass it an argument as well. This + * argument points to a global counter that should get incremented once + */ + da1469x_snc_irq_config(SNC_IRQ_MASK_PDC, snc_tc2_irq_cb, &g_snc_tc2_cntr); + + /* Start the program */ + da1469x_snc_sw_start(); + + /* This program should finish very quickly */ + os_time_delay(OS_TICKS_PER_SEC / 10); + if (!da1469x_snc_program_is_done()) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test 3 failed (not done)"); + da1469x_snc_sw_stop(); + TEST_ASSERT_FATAL(0); + } + + /* Check that the counter got incremented */ + if (g_snc_tc2_cntr != 1) { + MODLOG_INFO(LOG_MODULE_TEST, "snc test failed tc2=%u", g_snc_tc2_cntr); + TEST_ASSERT_FATAL(0); + } + + da1469x_snc_sw_stop(); + rc = da1469x_snc_sw_deinit(); + if (rc) { + MODLOG_INFO(LOG_MODULE_TEST, "snc s/w deinit failed"); + TEST_ASSERT_FATAL(0); + } + + MODLOG_INFO(LOG_MODULE_TEST, "snc test 3 success"); +} +#endif diff --git a/hw/mcu/dialog/da1469x/hosttest/syscfg.yml b/hw/mcu/dialog/da1469x/hosttest/syscfg.yml new file mode 100644 index 0000000..d203e01 --- /dev/null +++ b/hw/mcu/dialog/da1469x/hosttest/syscfg.yml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + DA1469x_HOSTTEST_SYSINIT_STAGE: + description: The sysinit stage number for the da1469x hosttest package + value: 800 + + TESTBENCH_DA1469X_SNC: + description: Enables dialog SNC support in the testbench. + value: 0 diff --git a/hw/mcu/dialog/da1469x/include/mcu/da1469x_snc.h b/hw/mcu/dialog/da1469x/include/mcu/da1469x_snc.h new file mode 100644 index 0000000..0ee27e5 --- /dev/null +++ b/hw/mcu/dialog/da1469x/include/mcu/da1469x_snc.h @@ -0,0 +1,514 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __MCU_DA1469X_SNC_H_ +#define __MCU_DA1469X_SNC_H_ + +#include <stdint.h> +#include "mcu/mcu.h" +#include "DA1469xAB.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The following two defintions are used by some instructions to determine if + * the address used in the instruction refers to a peripheral register location + * or is an address in system RAM. + */ +#define SNC_PERIPH_ADDR (0x50000000) +#define SNC_SYSRAM_ADDR (MCU_MEM_SYSRAM_START_ADDRESS) +#define SNC_REG_MASK (1 << 19) +#define SNC_OP_IS_REG(op) ((((uint32_t)op) & SNC_PERIPH_ADDR) ? SNC_REG_MASK : 0) +#define SNC_ADDR(addr) (((uint32_t)addr) - SNC_SYSRAM_ADDR) +#define SNC_REG(addr) (((uint32_t)addr) - SNC_PERIPH_ADDR) + +/* Proto for isr callback function (for M33) */ +typedef void (*snc_isr_cb_t)(void *arg); + +/* + * For certain commands which use direct or indirect addresses. A direct address + * specifies a memory location. An indirect address (a pointer) means that the + * address contains the address of the desired memory location. + * + * Please refer to the specific command to see if these definitions should be + * used for that command! + */ +#define SNC_ADDR_MODE_DIRECT (0) +#define SNC_ADDR_MODE_INDIRECT (1) + +/* + * No operation instruction. + * + * There are no operands for this instruction. + * + * ----------------------- + * | opcode (0) | N/C | + * ----------------------- + * | 31 - 28 | 27 - 0 | + * ----------------------- + */ +#define SNC_OPCODE_NOP (0) /* No operands */ +#define SNC_CMD_NOOP() (uint32_t)(SNC_OPCODE_NOP << 28) + +/* + * Store Contents + * + * Stores the contents of addr2 in addr1. Addr1 and/or Addr2 can be addresses + * or pointers and can reference either system RAM or a register + * + * am1: Addressing mode for addr1 + * SNC_WADAD_AM1_INDIRECT: Indirect addressing mode. + * SNC_WADAD_AM1_DIRECT: Direct addressing mode + * + * am2: Addressing mode for addr2 + * SNC_WADAD_AM2_INDIRECT: Indirect addressing mode. + * SNC_WADAD_AM2_DIRECT: Direct addressing mode + * ---------------------------------------------- + * | opcode (1) | am1 | am2 | N/C | addr1 | + * ---------------------------------------------- + * | 31 - 28 | 27 | 26 | 25 - 20 | 19 - 0 | + * ---------------------------------------------- + * + * ---------- + * | addr2 | + * ---------- + * | 31 - 0 | + * ---------- + * + * NOTE: The nomenclature used is addr2 first then addr1. + * + * Ex. RAM2RAM: addr2 and addr1 are both in system RAM. + * RAM2REG: addr2 is in system RAM and addr1 is in register space + * REG2RAM: addr2 is a register and addr1 is in system RAM. + */ +#define SNC_OPCODE_WADAD (1) +#define SNC_CMD_WADAD_RAM2RAM(addr1, am1, am2, addr2) \ + (uint32_t)((SNC_OPCODE_WADAD << 28) + (am1 << 27) + (am2 << 26) + \ + SNC_ADDR(addr1)), \ + (uint32_t)(SNC_ADDR(addr2)) +#define SNC_CMD_WADAD_RAM2REG(addr1, am1, am2, addr2) \ + (uint32_t)((SNC_OPCODE_WADAD << 28) + (am1 << 27) + (am2 << 26) + \ + SNC_REG_MASK + SNC_REG(addr1)), \ + (uint32_t)(SNC_ADDR(addr2)) +#define SNC_CMD_WADAD_REG2RAM(addr1, am1, am2, addr2) \ + (uint32_t)((SNC_OPCODE_WADAD << 28) + (am1 << 27) + (am2 << 26) + \ + SNC_ADDR(addr1)), \ + (uint32_t)(SNC_REG_MASK + SNC_REG(addr2)) +#define SNC_CMD_WADAD_REG2REG(addr1, am1, am2, addr2) \ + (uint32_t)((SNC_OPCODE_WADAD << 28) + (am1 << 27) + (am2 << 26) + \ + SNC_REG_MASK + SNC_REG(addr1)), \ + (uint32_t)(SNC_REG_MASK + SNC_REG(addr2)) + +#define SNC_WADAD_AM1_INDIRECT (0) +#define SNC_WADAD_AM1_DIRECT (1) +#define SNC_WADAD_AM2_DIRECT (0) +#define SNC_WADAD_AM2_INDIRECT (1) + +/* + * Store Value + * + * Stores immediate value (32-bits) in either addr (direct) or the address + * pointed to by addr (indirect) + * + * addr_mode: + * SNC_WADVA_AM_IND: Indirect address Ex. *addr = value + * SNC_WADVA_AM_DIR: Direct address mode Ex. addr = value + * + * ---------------------------------------------- + * | opcode (2) | addr_mode | N/C | addr | + * ---------------------------------------------- + * | 31 - 28 | 27 | 26 - 20 | 19 - 0 | + * ---------------------------------------------- + * + * ---------- + * | value | + * ---------- + * | 31 - 0 | + * ---------- + */ +#define SNC_OPCODE_WADVA (2) +#define SNC_CMD_WADVA_REG(addr, addr_mode, value) \ + (uint32_t)((SNC_OPCODE_WADVA << 28) + (addr_mode << 27) + \ + SNC_REG_MASK + SNC_REG(addr)), \ + (uint32_t)(value) + +#define SNC_CMD_WADVA_RAM(addr, addr_mode, value) \ + (uint32_t)((SNC_OPCODE_WADVA << 28) + (addr_mode << 27) + \ + SNC_ADDR(addr)), \ + (uint32_t)(value) + +#define SNC_WADVA_AM_IND (0) +#define SNC_WADVA_AM_DIR (1) + +#define SNC_CMD_WADVA_IND_RAM(addr, val) \ + SNC_CMD_WADVA_RAM(addr, SNC_WADVA_AM_IND, val) +#define SNC_CMD_WADVA_IND_REG(addr, val) \ + SNC_CMD_WADVA_REG(addr, SNC_WADVA_AM_IND, val) +#define SNC_CMD_WADVA_DIR_RAM(addr, val) \ + SNC_CMD_WADVA_RAM(addr, SNC_WADVA_AM_DIR, val) +#define SNC_CMD_WADVA_DIR_REG(addr, val) \ + SNC_CMD_WADVA_REG(addr, SNC_WADVA_AM_DIR, val) + +/* + * XOR + * + * Performs an XOR operation with the contents of addr and mask; stores contents + * in addr. Note that addr can be a register or RAM address, + * + * Ex: x = x ^ mask + * + * ------------------------- + * | opcode (3) | addr | + * ------------------------- + * | 31 - 28 | 19 - 0 | + * ------------------------- + * + * ---------- + * | mask | + * ---------- + * | 31 - 0 | + * ---------- + */ +#define SNC_OPCODE_TOBRE (3) +#define SNC_CMD_TOBRE_RAM(addr, mask) \ + (uint32_t)((SNC_OPCODE_TOBRE << 28) + SNC_ADDR(addr)), \ + (uint32_t)(mask) +#define SNC_CMD_TOBRE_REG(addr, mask) \ + (uint32_t)((SNC_OPCODE_TOBRE << 28) + SNC_REG_MASK + SNC_REG(addr)), \ + (uint32_t)(mask) + +/* + * Compare Bit in Address + * + * Compares the contents of addr and sets the EQUALHIGH_FLAG if the bit at + * position 'bitpos' is set to 1. NOTE: addr can be either RAM address + * or a register. Note that bitpos is a bit position (0 to 31). + * + * Ex: if (addr & (1 << bitpos)) { + * EQUALHIGH_FLAG = 1; + * } else { + * EQUALHIGH_FLAG = 0; + * } + * + * -------------------------------------------- + * | opcode (4) | bitpos | N/C | addr | + * -------------------------------------------- + * | 31 - 28 | 27 - 23 | 22 - 20 | 19 - 0 | + * -------------------------------------------- + */ +#define SNC_OPCODE_RDCBI (4) +#define SNC_CMD_RDCBI_REG(addr, bitpos) \ + (uint32_t)((SNC_OPCODE_RDCBI << 28) + ((bitpos & 0x1F) << 23) + \ + SNC_REG_MASK + SNC_REG(addr)) +#define SNC_CMD_RDCBI_RAM(addr, bitpos) \ + (uint32_t)((SNC_OPCODE_RDCBI << 28) + ((bitpos & 0x1F) << 23) + \ + SNC_ADDR(addr)) + +/* + * Compare Address Contents + * + * Compares the contents of addr1 and addr2 and sets the GREATERVAL_FLAG if the + * contents of addr1 are greater than the contents of addr2. Note that addr1 + * and addr2 can be either RAM addresses or a register. + * + * --------------------------------- + * | opcode (5) | N/C | addr1 | + * --------------------------------- + * | 31 - 28 | 27 - 20 | 19 - 0 | + * --------------------------------- + * + * -------------------- + * | N/C | addr2 | + * -------------------- + * | 31 - 20 | 19 - 0 | + * -------------------- + * + * NOTE: the nomenclature here is addr1 first, then addr2 + * EX: RAMRAM: both addr1 and addr2 are in system RAM + * RAMREG: addr1 is in system RAM, addr2 is a register + * REGRAM: addr1 is a register, addr2 is in system RAM + * REGREG: both addr1 and addr2 are registers + */ +#define SNC_OPCODE_RDCGR (5) +#define SNC_CMD_RDCGR_RAMRAM(addr1, addr2) \ + (uint32_t)((SNC_OPCODE_RDCGR << 28) + SNC_ADDR(addr1)), \ + (uint32_t)(SNC_ADDR(addr2)) +#define SNC_CMD_RDCGR_RAMREG(addr1, addr2) \ + (uint32_t)((SNC_OPCODE_RDCGR << 28) + SNC_ADDR(addr1)), \ + (uint32_t)(SNC_REG_MASK + SNC_REG(addr2)) +#define SNC_CMD_RDCGR_REGRAM(addr1, addr2) \ + (uint32_t)((SNC_OPCODE_RDCGR << 28) + SNC_REG_MASK + SNC_REG(addr1)), \ + (uint32_t)(SNC_ADDR(addr2)) +#define SNC_CMD_RDCGR_REGREG(addr1, addr2) \ + (uint32_t)((SNC_OPCODE_RDCGR << 28) + SNC_REG_MASK + SNC_REG(addr1)), \ + (uint32_t)(SNC_REG_MASK + SNC_REG(addr2)) + +/* + * Conditional branch instruction + * + * Perform a conditional branch to a direct or indirect memory + * address (RAM). There are three types of branches: + * + * EQUALHIGH_FLAG: branch if flag is true. (direct or indirect) + * 0x0A => branch to direct address + * 0x1A => branch to indirect address + * GREATERVAL_FLAG: branch if flag is true. (direct or indirect) + * 0x05 => branch to direct address + * 0x15 => branch to indirect address + * LOOP: perform branch up to 128 times. (direct only) + * 0b1yyyyyyy where yyyyyyy is loop count. + * + * ---------------------------------------- + * | opcode (6) | flags | N/C | ADDR | + * ---------------------------------------- + * | 31 - 28 | 27 - 20 | 19 | 18 - 0 | + * ---------------------------------------- + * + * addr: Either a direct address (specifies an address in RAM) or an indirect + * memory address (addr contains the RAM address to branch to). + * loops: Number of times to loop (max 127). + */ +#define SNC_OPCODE_COBR (6) +#define SNC_CMD_COBR_EQ_DIR(addr) \ + (uint32_t)((SNC_OPCODE_COBR << 28) + (0x0A << 20) + SNC_ADDR(addr)) +#define SNC_CMD_COBR_EQ_IND(addr) \ + (uint32_t)((SNC_OPCODE_COBR << 28) + (0x1A << 20) + SNC_ADDR(addr)) +#define SNC_CMD_COBR_GT_DIR(addr) \ + (uint32_t)((SNC_OPCODE_COBR << 28) + (0x05 << 20) + SNC_ADDR(addr)) +#define SNC_CMD_COBR_GT_IND(addr) \ + (uint32_t)((SNC_OPCODE_COBR << 28) + (0x15 << 20) + SNC_ADDR(addr)) +#define SNC_CMD_COBR_LOOP(addr, loops) \ + (uint32_t)((SNC_OPCODE_COBR << 28) + ((0x80 + (loops & 0x7f)) << 20) + \ + SNC_ADDR(addr)) + +/* + * Increment instruction + * + * Increments the contents of a memory address (RAM address) by + * either 1 or 4. + * + * INC bit value of 0: increment by 1 + * INC bit value of 1: increment by 4 + * + * ----------------------------------- + * | opcode (7) | N/C | INC | ADDR | + * ----------------------------------- + * | 31 - 28 | 27-20 | 19 | 18-0 | + * ----------------------------------- + */ +#define SNC_OPCODE_INC (7) +#define SNC_CMD_INC(addr, inc_by_4) \ + (uint32_t)((SNC_OPCODE_INC << 28) + (inc_by_4 << 19) + SNC_ADDR(addr)) +#define SNC_CMD_INC_BY_1(addr) SNC_CMD_INC(addr, 0) +#define SNC_CMD_INC_BY_4(addr) SNC_CMD_INC(addr, 1) + +/* + * Delay Instruction + * + * Delay for up to 255 LP clock ticks. + * + * ------------------------------- + * | opcode (8) | N/C | Delay | + * ------------------------------- + * | 31 - 28 | 27 - 8 | 7 - 0 | + * ------------------------------- + */ +#define SNC_OPCODE_DEL (8) +#define SNC_CMD_DEL(ticks) (uint32_t)((SNC_OPCODE_DEL << 28) | (ticks & 0xff)) + +/* + * Sleep instruction + * + * This instruction is used to halt program execution. Will + * generate a signal pulse to PDC and power down the + * sensor node controller. + * + * There are no operands for this instruction. + * + * ----------------------- + * | opcode (9) | N/C | + * ----------------------- + * | 31 - 28 | 27 - 0 | + * ----------------------- + */ +#define SNC_OPCODE_SLP (9) /* No operands */ +#define SNC_CMD_SLEEP() (uint32_t)(SNC_OPCODE_SLP << 28) + +/* + * API NOTES: + * + * 1) The SNC API are not protected by critical sections. If any of these + * API are called by more than one task or inside an ISR they need to be + * protected. + * + * 2) API with _sw_ are intended to be used when the host processor has control + * of the SNC (as opposed to the PDC). Typically these API are for debugging as + * the PDC usually controls the SNC. + */ + +/** + * da1469x snc sw init + * + * initialize the SNC for software control + * + * Called when the host processor wants control of the SNC (PDC + * no longer controls SNC). The SNC must be stopped or this function will return + * an error. + * + * NOTE: this function will acquire the COM power domain. + * + * @return int 0: success, -1: error (SNC not currently stopped). + */ +int da1469x_snc_sw_init(void); + +/** + * da1469x snc sw deinit + * + * Takes the SNC out of software control. The SNC must be + * stopped and in software control or an error will be returned + * + * NOTE: this function releases the COM power domain when + * called. + * + * @return int 0: success, -1: error + */ +int da1469x_snc_sw_deinit(void); + +/** + * da1469x snc sw start + * + * Starts the SNC. Note that the user should have called + * snc_sw_load prior to starting the SNC via software control. + * + * @return int 0: success -1: error + */ +int da1469x_snc_sw_start(void); + +/** + * da1469x snc sw stop + * + * Stops the SNC from running a program. This should only be + * called when the SNC is under software control. + * + * @return int 0: success -1: error + */ +int da1469x_snc_sw_stop(void); + +/** + * da1469x snc program is done + * + * Checks if the SNC program has finished + * + * @return int 0: not finished 1: finished + */ +int da1469x_snc_program_is_done(void); + +/** + * da1469x snc irq config + * + * Configures the SNC to interrupt the host processor and/or PDC + * when SNC generates an interrupt. + * + * @param mask One or more of the following: + * SNC_IRQ_MASK_NONE: Do not interrupt host or PDC + * SNC_IRQ_MASK_HOST: Interrupt host processor (CM33) + * SNC_IRQ_MASK_PDC: Interrupt PDC + * @param isr_cb Callback function for M33 irq handler. Can + * be NULL. + * @param isr_arg Argument to isr callback function. + * + * @return int 0: success -1: invalid mask parameter + * + * NOTES: + * + * 1) The IRQ configuration cannot be changed if there is a + * pending IRQ. The IRQ must be cleared in that case. This + * function will automatically clear the IRQ if that is the case + * and will not report an error. + */ +int da1469x_snc_irq_config(uint8_t mask, snc_isr_cb_t isr_cb, void *arg); + +#define SNC_IRQ_MASK_NONE (0x00) /* Do not interrupt host or PDC */ +#define SNC_IRQ_MASK_HOST (0x01) /* Interrupt host processor (CM33) */ +#define SNC_IRQ_MASK_PDC (0x02) /* Interrupt PDC */ + +/** + * da1469x snc irq clear + * + * Clears the IRQ from the SNC to the PDC and/or Host processor. + */ +static inline void +da1469x_snc_irq_clear(void) +{ + SNC->SNC_CTRL_REG |= SNC_SNC_CTRL_REG_SNC_IRQ_ACK_Msk; +} + +/** + * da1469x snc error status + * + * Returns error status for the SNC + * + * @return uint8_t Error status. + * Returns one or more of the following errors: + * SNC_BUS_ERROR: Bus Fault error (invalid memory access) + * SNC_HARD_FAULT_ERROR: Hard Fault error (invalid + * instruction) + */ +uint8_t da1469x_snc_error_status(void); + +#define SNC_BUS_ERROR (0x01) +#define SNC_HARD_FAULT_ERROR (0x02) + +static inline void +da1469x_snc_enable_bus_err_detect(void) +{ + SNC->SNC_CTRL_REG |= SNC_SNC_CTRL_REG_BUS_ERROR_DETECT_EN_Msk; +} + +/** + * da1469x snc config + * + * Configures the starting program address of the SNC in the + * memory controller and sets SNC clock divider. + * + * @param prog_addr This is the starting address in system RAM + * of program + * + * @param clk_div Clock divider. One of the following: + * SNC_CLK_DIV_1: Divide low power clock by 1 + * SNC_CLK_DIV_2: Divide low power clock by 2 + * SNC_CLK_DIV_4: Divide low power clock by 4 + * SNC_CLK_DIV_8: Divide low power clock by 8 + * + * @return int 0: success, -1 otherwise. + */ +int da1469x_snc_config(void *prog_addr, int clk_div); + +#define SNC_CLK_DIV_1 (0) +#define SNC_CLK_DIV_2 (1) +#define SNC_CLK_DIV_4 (2) +#define SNC_CLK_DIV_8 (3) + +#ifdef __cplusplus +} +#endif + +#endif /* __MCU_DA1469X_SNC_H_ */ diff --git a/hw/mcu/dialog/da1469x/src/da1469x_snc.c b/hw/mcu/dialog/da1469x/src/da1469x_snc.c new file mode 100644 index 0000000..01b8c34 --- /dev/null +++ b/hw/mcu/dialog/da1469x/src/da1469x_snc.c @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> +#include "os/os_trace_api.h" +#include "mcu/da1469x_snc.h" +#include "mcu/da1469x_pd.h" + +/* IRQ callback for M33 and argument */ +snc_isr_cb_t g_snc_isr_cb_func; +void *g_snc_isr_arg; + +static void +da1469x_snc_irq_handler(void) +{ + os_trace_isr_enter(); + da1469x_snc_irq_clear(); + if (g_snc_isr_cb_func) { + g_snc_isr_cb_func(g_snc_isr_arg); + } + os_trace_isr_exit(); +} + +int +da1469x_snc_sw_init(void) +{ + /* SNC better be stopped! */ + if ((SNC->SNC_STATUS_REG & SNC_SNC_STATUS_REG_SNC_IS_STOPPED_Msk) == 0) { + return -1; + } + + /* First, just put it S/W control. */ + SNC->SNC_CTRL_REG = SNC_SNC_CTRL_REG_SNC_SW_CTRL_Msk; + + + /* We will be using the COM power domain so acquire it here */ + da1469x_pd_acquire(MCU_PD_DOMAIN_COM); + + /* Reset the SNC (keep in SW ctrl as well) */ + SNC->SNC_CTRL_REG = SNC_SNC_CTRL_REG_SNC_SW_CTRL_Msk | + SNC_SNC_CTRL_REG_SNC_RESET_Msk; + + /* + * Program the following in control register + * SNC_SW_CTRL: Puts SNC in software control (PDC does not use SNC). + * IRQ_ACK: set just in case to clear any interrupts. + * BRANCH_LOOP_INIT: set to clear loop counter. + * BUS_ERROR_DETECT: set to enable bus error detection. + */ + SNC->SNC_CTRL_REG = SNC_SNC_CTRL_REG_SNC_SW_CTRL_Msk | + SNC_SNC_CTRL_REG_SNC_BRANCH_LOOP_INIT_Msk | + SNC_SNC_CTRL_REG_BUS_ERROR_DETECT_EN_Msk | + SNC_SNC_CTRL_REG_SNC_IRQ_ACK_Msk; + return 0; +} + +int +da1469x_snc_sw_deinit(void) +{ + /* SNC better be in SW control! */ + if ((SNC->SNC_CTRL_REG & SNC_SNC_CTRL_REG_SNC_SW_CTRL_Msk) == 0) { + return -1; + } + + /* SNC better be stopped! */ + if ((SNC->SNC_STATUS_REG & SNC_SNC_STATUS_REG_SNC_IS_STOPPED_Msk) == 0) { + return -1; + } + + /* Take out of SW control */ + SNC->SNC_CTRL_REG &= ~SNC_SNC_CTRL_REG_SNC_SW_CTRL_Msk; + + /* Release COM power domain */ + da1469x_pd_release(MCU_PD_DOMAIN_COM); + + return 0; +} + +int +da1469x_snc_sw_start(void) +{ + /* XXX: Should we check if already running and return error if not? */ + SNC->SNC_CTRL_REG |= SNC_SNC_CTRL_REG_SNC_EN_Msk; + return 0; +} + +int +da1469x_snc_sw_stop(void) +{ + /* XXX: Should we check if in S/W control and return error if not? */ + SNC->SNC_CTRL_REG &= ~SNC_SNC_CTRL_REG_SNC_EN_Msk; + return 0; +} + +int +da1469x_snc_program_is_done(void) +{ + int rc; + uint32_t status; + + status = SNC->SNC_STATUS_REG & SNC_SNC_STATUS_REG_SNC_DONE_STATUS_Msk; + if (status) { + rc = 1; + } else { + rc = 0; + } + return rc; +} + +uint8_t +da1469x_snc_error_status(void) +{ + uint8_t err; + uint32_t status; + + err = 0; + status = SNC->SNC_STATUS_REG; + if (status & SNC_SNC_STATUS_REG_BUS_ERROR_STATUS_Msk) { + err |= SNC_BUS_ERROR; + } + if (status & SNC_SNC_STATUS_REG_HARD_FAULT_STATUS_Msk) { + err |= SNC_HARD_FAULT_ERROR; + } + + return err; +} + +int +da1469x_snc_irq_config(uint8_t mask, snc_isr_cb_t isr_cb, void *arg) +{ + int rc; + uint32_t irqs; + + NVIC_DisableIRQ(SNC_IRQn); + NVIC_SetVector(SNC_IRQn, (uint32_t)da1469x_snc_irq_handler); + + /* Clear the bits first... */ + SNC->SNC_CTRL_REG &= ~SNC_SNC_CTRL_REG_SNC_IRQ_CONFIG_Msk; + + rc = 0; + if (SNC->SNC_STATUS_REG & SNC_SNC_CTRL_REG_SNC_IRQ_EN_Msk) { + da1469x_snc_irq_clear(); + } + + if (mask != SNC_IRQ_MASK_NONE) { + if (mask > (SNC_IRQ_MASK_HOST | SNC_IRQ_MASK_PDC)) { + rc = -1; + } else { + irqs = 0; + if (mask & SNC_IRQ_MASK_HOST) { + irqs |= (1 << 6); + g_snc_isr_arg = arg; + g_snc_isr_cb_func = isr_cb; + NVIC_EnableIRQ(SNC_IRQn); + } + if (mask & SNC_IRQ_MASK_PDC) { + irqs |= (1 << 7); + } + SNC->SNC_CTRL_REG |= irqs; + } + } + + return rc; +} + +int +da1469x_snc_config(void *prog_addr, int clk_div) +{ + uint32_t offset; + + /* Do some basic checks to make sure it is OK */ + offset = (uint32_t)prog_addr; + if ((offset & 0x3) || (offset < MCU_MEM_SYSRAM_START_ADDRESS)) { + return -1; + } + + /* + * Program the SNC base address register. This is where the device + * will execute code. + * + * XXX: can probably just write the entire address without mask + * but just in case mask it. + */ + MEMCTRL->SNC_BASE_REG = offset & + MEMCTRL_SNC_BASE_REG_SNC_BASE_ADDRESS_Msk; + + /* Only two bits for clock divider */ + if (clk_div > 3) { + return -1; + } + + CRG_COM->CLK_COM_REG &= ~(clk_div << CRG_COM_CLK_COM_REG_SNC_DIV_Pos); + CRG_COM->CLK_COM_REG |= (clk_div << CRG_COM_CLK_COM_REG_SNC_DIV_Pos); + + return 0; +}