Skip to content

drivers: misc: ethos_u: support nuvoton numaker m55m1x #88941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MAINTAINERS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5081,6 +5081,7 @@
- wearyzen
collaborators:
- ithinuel
- ccli8
files:
- drivers/misc/ethos_u/
- modules/hal_ethos_u/
Expand Down Expand Up @@ -5115,7 +5116,7 @@

"West project: hal_microchip":
status: maintained
maintainers:

Check failure on line 5119 in MAINTAINERS.yml

View workflow job for this annotation

GitHub Actions / Check MAINTAINERS.yml changes

User lacks access

ccli8 does not have needed access level to zephyrproject-rtos/zephyr
- jvasanth1
collaborators:
- VenkatKotakonda
Expand Down
1 change: 1 addition & 0 deletions drivers/misc/ethos_u/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
zephyr_library()
zephyr_library_sources(ethos_u_common.c)
zephyr_library_sources_ifdef(CONFIG_ETHOS_U_ARM ethos_u_arm.c)
zephyr_library_sources_ifdef(CONFIG_ETHOS_U_NUMAKER ethos_u_numaker.c)
6 changes: 6 additions & 0 deletions drivers/misc/ethos_u/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ choice
prompt "Select vendor Ethos-U NPU driver"
depends on ETHOS_U
default ETHOS_U_ARM if DT_HAS_ARM_ETHOS_U_ENABLED
default ETHOS_U_NUMAKER if DT_HAS_NUVOTON_NUMAKER_NPU_ENABLED

config ETHOS_U_ARM
bool "Arm Ethos-U NPU driver"
help
Enables Arm Ethos-U NPU driver.

config ETHOS_U_NUMAKER
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the discussion here could influence this change so I would appreciate your feedback there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Besides, this PR will update according to result of #90787.

bool "Nuvoton NuMaker Ethos-U NPU driver"
help
Enables Nuvoton NuMaker frontend of Arm Ethos-U NPU driver

endchoice
130 changes: 130 additions & 0 deletions drivers/misc/ethos_u/ethos_u_numaker.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2025 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nuvoton_numaker_npu

#include <zephyr/kernel.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/clock_control_numaker.h>
#include <zephyr/drivers/reset.h>

#include <ethosu_driver.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ethos_u_numaker, CONFIG_ETHOS_U_LOG_LEVEL);

#include <soc.h>

struct ethos_u_numaker_config {
void *base_addr;
const struct device *clkctrl_dev;
struct numaker_scc_subsys pcc;
struct reset_dt_spec reset;
void (*irq_config)(const struct device *dev);
bool secure_enable;
bool privilege_enable;
uint8_t flush_mask;
uint8_t invalidate_mask;
};

struct ethos_u_numaker_data {
struct ethosu_driver drv;
};

static void ethos_u_numaker_irq_handler(const struct device *dev)
{
struct ethos_u_numaker_data *data = dev->data;
struct ethosu_driver *drv = &data->drv;

ethosu_irq_handler(drv);
}

static int ethos_u_numaker_init(const struct device *dev)
{
const struct ethos_u_numaker_config *config = dev->config;
struct ethos_u_numaker_data *data = dev->data;
struct ethosu_driver *drv = &data->drv;
int rc;

/* Invoke Clock controller to enable module clock */

/* Equivalent to CLK_EnableModuleClock() */
rc = clock_control_on(config->clkctrl_dev, (clock_control_subsys_t)&config->pcc);
if (rc < 0) {
return rc;
}
rc = clock_control_on(config->clkctrl_dev, (clock_control_subsys_t)&config->pcc);
if (rc < 0) {
return rc;
}

/* Equivalent to CLK_SetModuleClock() */
rc = clock_control_configure(config->clkctrl_dev, (clock_control_subsys_t)&config->pcc,
NULL);
if (rc < 0) {
return rc;
}

/* Invoke Reset controller to reset module to default state */
/* Equivalent to SYS_ResetModule() */
rc = reset_line_toggle_dt(&config->reset);
if (rc < 0) {
return rc;
}

LOG_DBG("Ethos-U DTS info: base_addr=0x%p, secure_enable=%u, privilege_enable=%u",
config->base_addr, config->secure_enable, config->privilege_enable);

if (ethosu_init(drv, config->base_addr, NULL, 0, config->secure_enable,
config->privilege_enable)) {
LOG_ERR("Failed to initialize NPU with ethosu_init().");
return -EINVAL;
}

ethosu_set_basep_cache_mask(drv, config->flush_mask, config->invalidate_mask);

config->irq_config(dev);

return 0;
}

/* Peripheral Clock Control */
#define NUMAKER_PCC_INST_GET(inst) \
{ \
.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC, \
.pcc.clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \
.pcc.clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \
.pcc.clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \
}

#define NUMAKER_ETHOS_U_INIT(inst) \
static void ethos_u_numaker_irq_config_##inst(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQ(inst, irq), DT_INST_IRQ(inst, priority), \
ethos_u_numaker_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
\
irq_enable(DT_INST_IRQ(inst, irq)); \
} \
\
static const struct ethos_u_numaker_config ethos_u_numaker_config_##inst = { \
.base_addr = (void *)DT_INST_REG_ADDR(inst), \
.clkctrl_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))), \
.pcc = NUMAKER_PCC_INST_GET(inst), \
.reset = RESET_DT_SPEC_INST_GET(inst), \
.irq_config = ethos_u_numaker_irq_config_##inst, \
.secure_enable = DT_INST_PROP(inst, secure_enable), \
.privilege_enable = DT_INST_PROP(inst, privilege_enable), \
.flush_mask = DT_INST_PROP(inst, flush_mask), \
.invalidate_mask = DT_INST_PROP(inst, invalidate_mask), \
}; \
\
static struct ethos_u_numaker_data ethos_u_numaker_data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, ethos_u_numaker_init, NULL, &ethos_u_numaker_data_##inst, \
&ethos_u_numaker_config_##inst, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);

DT_INST_FOREACH_STATUS_OKAY(NUMAKER_ETHOS_U_INIT);
11 changes: 11 additions & 0 deletions dts/arm/nuvoton/m55m1h2l.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,16 @@
reg = <0x100000 DT_SIZE_K(2048)>;
};
};

npu0: npu@40003000 {
compatible = "nuvoton,numaker-npu";
reg = <0x40003000 0x1000>;
interrupts = <13 0>;
resets = <&rst NUMAKER_SYS_NPURST>;
clocks = <&pcc NUMAKER_NPU0_MODULE 0 0>;
secure-enable;
privilege-enable;
status = "disabled";
};
};
};
37 changes: 37 additions & 0 deletions dts/bindings/arm/nuvoton,numaker-npu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (c) 2025 Nuvoton Technology Corporation
# SPDX-License-Identifier: Apache-2.0

description: Nuvoton NuMaker frontend of Arm Ethos-U NPU driver

compatible: "nuvoton,numaker-npu"

include: ["arm,ethos-u.yaml", reset-device.yaml]

properties:
reg:
required: true

interrupts:
required: true

resets:
required: true

clocks:
required: true

flush-mask:
type: int
default: 2
description: |
Base pointer cache flush mask passed to ethos-u core driver
ethosu_set_basep_cache_mask(). Default is to follow ethos-u
core driver for scratch region.

invalidate-mask:
type: int
default: 2
description: |
Base pointer cache invalidation mask passed to ethos-u core driver
ethosu_set_basep_cache_mask(). Default is to follow ethos-u
core driver for scratch region.
13 changes: 13 additions & 0 deletions lib/libc/newlib/libc-hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,19 @@ __weak void _exit(int status)
}
}

/* Address undefined _fini for the exit call
*
* With gcc command-line options -nostartfiles or -nostdlib, the gcc
* built-in object files crti.o/crtn.o which implement _init/_fini
* won't get linked in automatically and will cause undefined reference
* to _fini error when exit is invoked.
*
* This is fixed by providing one dummey _fini to let linker pass.
*/
__weak void _fini(void)
{
}
Comment on lines +310 to +321
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@wearyzen wearyzen Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ccli8 maybe I missed something but how is this change related to this PR? did you face this issue while running the new samples?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This sample modules/tflite-micro/tflm_ethosu uses newlib and invokes exit(), but meets compile failure with _fini undefined.


#ifndef CONFIG_NEWLIB_LIBC_CUSTOM_SBRK
void *_sbrk(intptr_t count)
{
Expand Down
1 change: 1 addition & 0 deletions modules/hal_ethos_u/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ choice ETHOS_U_NPU_CONFIG
default ETHOS_U55_128 if SOC_SERIES_MPS3
default ETHOS_U65_256 if SOC_MPS4_CORSTONE315
default ETHOS_U85_256 if SOC_MPS4_CORSTONE320
default ETHOS_U55_256 if SOC_SERIES_M55M1X
config ETHOS_U55_64
bool "using Ethos-U55 with 64 macs"
config ETHOS_U55_128
Expand Down
18 changes: 16 additions & 2 deletions samples/modules/tflite-micro/tflm_ethosu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,22 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(tflm_ethosu_app)

target_include_directories(app PRIVATE src/models/keyword_spotting_cnn_small_int8)
# Choose appropriate model
if(CONFIG_TAINT_BLOBS_TFLM_ETHOSU)
# Vela-compiled model must match Ethos-U RTL config, e.g. U55/U65/U85 and MACs per cycle
target_include_directories_ifdef(CONFIG_BOARD_MPS3 app PRIVATE src/models/keyword_spotting_cnn_small_int8_ethos_u55_128)
target_include_directories_ifdef(CONFIG_BOARD_NUMAKER_M55M1 app PRIVATE src/models/keyword_spotting_cnn_small_int8_ethos_u55_256)

# No Vela-compiled model for these boards, fall back to not optimized
target_include_directories_ifdef(CONFIG_BOARD_MPS4_CORSTONE315_FVP app PRIVATE src/models/keyword_spotting_cnn_small_int8)
target_include_directories_ifdef(CONFIG_BOARD_MPS4_CORSTONE320_FVP app PRIVATE src/models/keyword_spotting_cnn_small_int8)
elseif(CONFIG_TAINT_BLOBS_TFLM)
target_include_directories(app PRIVATE src/models/keyword_spotting_cnn_small_int8)
endif()

target_sources(app PRIVATE src/main.cpp src/inference_process.cpp)
target_sources_ifdef(CONFIG_BOARD_NUMAKER_M55M1 app PRIVATE src/npu_cache.c)

zephyr_linker_sources(SECTIONS linker.ld)
# Add platform specific linker source snippet
zephyr_linker_sources_ifdef(CONFIG_BOARD_MPS3 SECTIONS linker.ld)
zephyr_linker_sources_ifdef(CONFIG_BOARD_MPS4 SECTIONS linker.ld)
7 changes: 6 additions & 1 deletion samples/modules/tflite-micro/tflm_ethosu/Kconfig
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Copyright 2022 Arm Limited and/or its affiliates <[email protected]>
# SPDX-License-Identifier: Apache-2.0

config TFLM_ETHOSU_TAINT_BLOBS
config TAINT_BLOBS_TFLM
bool
default y
select TAINT_BLOBS

config TAINT_BLOBS_TFLM_ETHOSU
bool "Choose Vela-compiled model targeting Ethos-U"
default y
depends on TAINT_BLOBS_TFLM

source "Kconfig.zephyr"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* SPDX-License-Identifier: Apache-2.0 */

&npu0 {
status = "okay";
};
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,16 @@ bool InferenceProcess::runJob(InferenceJob &job)
}

/* Create the TFL micro interpreter */
#ifdef CONFIG_TAINT_BLOBS_TFLM_ETHOSU
tflite::MicroMutableOpResolver <1> resolver;
resolver.AddEthosU();

#else
tflite::MicroMutableOpResolver <4> resolver;
resolver.AddReshape();
resolver.AddConv2D();
resolver.AddFullyConnected();
resolver.AddSoftmax();
#endif
tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize);

/* Allocate tensors */
Expand Down
28 changes: 11 additions & 17 deletions samples/modules/tflite-micro/tflm_ethosu/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ volatile int totalCompletedJobs = 0;
/* TensorArena static initialisation */
const size_t arenaSize = TENSOR_ARENA_SIZE_PER_INFERENCE;

__attribute__((section("tflm_arena"), aligned(16)))
TENSOR_ARENA_ATTR
uint8_t inferenceProcessTensorArena[NUM_INFERENCE_TASKS][arenaSize];

/* Allocate and initialize heap */
Expand Down Expand Up @@ -225,18 +225,15 @@ int main()
k_queue_init(&inferenceQueue);

/* inferenceSender tasks to create and queue the jobs */
const size_t inferenceSenderStackSize = 2048;
static K_THREAD_STACK_ARRAY_DEFINE(inferenceSenderStacks, NUM_JOB_TASKS,
inferenceSenderStackSize);
for (int n = 0; n < NUM_JOB_TASKS; n++) {
const size_t stackSize = 2048;
k_thread_stack_t *stack = static_cast<k_thread_stack_t *>(k_malloc(stackSize));
if (stack == nullptr) {
printf("Failed to allocate stack to 'inferenceSenderTask%i'\n", n);
exit(1);
}

auto &thread = threads[nthreads];
string *name = new string("sender " + to_string(n));

thread.id = k_thread_create(&thread.thread, stack, stackSize, inferenceSenderTask,
thread.id = k_thread_create(&thread.thread, inferenceSenderStacks[n],
inferenceSenderStackSize, inferenceSenderTask,
name, heapPtr, &inferenceQueue, 3, 0, K_FOREVER);
if (thread.id == 0) {
printf("Failed to create 'inferenceSenderTask%i'\n", n);
Expand All @@ -248,21 +245,18 @@ int main()

/* Create inferenceProcess tasks to process the queued jobs */
InferenceProcessParams taskParams[NUM_INFERENCE_TASKS];
const size_t inferenceProcessStackSize = 8192;
static K_THREAD_STACK_ARRAY_DEFINE(inferenceProcessStacks, NUM_INFERENCE_TASKS,
inferenceProcessStackSize);
for (int n = 0; n < NUM_INFERENCE_TASKS; n++) {
const size_t stackSize = 8192;
k_thread_stack_t *stack = static_cast<k_thread_stack_t *>(k_malloc(stackSize));
if (stack == nullptr) {
printf("Failed to allocate stack to 'inferenceSenderTask%i'\n", n);
exit(1);
}

auto &thread = threads[nthreads];
auto &taskParam = taskParams[n];
taskParam = InferenceProcessParams(&inferenceQueue, inferenceProcessTensorArena[n],
arenaSize);
string *name = new string("runner " + to_string(n));

thread.id = k_thread_create(&thread.thread, stack, stackSize, inferenceProcessTask,
thread.id = k_thread_create(&thread.thread, inferenceProcessStacks[n],
inferenceProcessStackSize, inferenceProcessTask,
name, heapPtr, &taskParam, 2, 0, K_FOREVER);
if (thread.id == 0) {
printf("Failed to create 'inferenceProcessTask%i'\n", n);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

__aligned(4) __attribute__((section("tflm_input"))) uint8_t inputData[] = {
__aligned(4) __attribute__((section(".rodata.tflm_input"))) uint8_t inputData[] = {
0x2c, 0x8a, 0xff, 0x0c, 0xaf, 0x2a, 0x44, 0x17, 0xf5, 0x26, 0x96, 0x37, 0x40, 0x4c, 0xa1,
0x58, 0xc3, 0x33, 0xce, 0x1a, 0x7b, 0xd2, 0x22, 0x5b, 0x43, 0xf6, 0xfd, 0x0b, 0xe7, 0xfd,
0x65, 0x58, 0x89, 0x24, 0xf4, 0xec, 0x53, 0x5e, 0x21, 0x1f, 0x95, 0xd1, 0xd9, 0x25, 0x72,
Expand Down
Loading
Loading