You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by we...@apache.org on 2019/06/19 23:55:44 UTC

[mynewt-core] branch master updated: hw/mcu/dialog: SNC (#1868)

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 <de...@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;
+}