Skip to content
Merged
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
45 changes: 36 additions & 9 deletions pyocd/rtos/zephyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) 2016-2020 Arm Limited
# Copyright (c) 2022 Intel Corporation
# Copyright (c) 2022 Chris Reed
# Copyright (c) 2025 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -147,22 +148,29 @@ def read_core_registers_raw(self, reg_list):
class ZephyrThread(TargetThread):
"""@brief A Zephyr task."""

READY = 0
# Keep in sync with include/zephyr/kernel_structs.h
DUMMY = 1 << 0 # attribute rather than state
PENDING = 1 << 1
PRESTART = 1 << 2
SLEEPING = 1 << 2
DEAD = 1 << 3
SUSPENDED = 1 << 4
POLLING = 1 << 5
RUNNING = 1 << 6
ABORTING = 1 << 5
SUSPENDING = 1 << 6
QUEUED = 1 << 7 # thread in ready queue

# Not a real value; for bookkeeping purposes only
RUNNING = 1 << 31

STATE_NAMES = {
READY : "Ready",
DUMMY : "Dummy",
PENDING : "Pending",
PRESTART : "Prestart",
SLEEPING : "Sleeping",
DEAD : "Dead",
SUSPENDED : "Suspended",
POLLING : "Polling",
RUNNING : "Running",
ABORTING : "Aborting",
SUSPENDING : "Suspending",
QUEUED : "Ready",
RUNNING : "Running"
}

def __init__(self, targetContext, provider, base, offsets):
Expand All @@ -172,7 +180,7 @@ def __init__(self, targetContext, provider, base, offsets):
self._base = base
self._thread_context = ZephyrThreadContext(self._target_context, self)
self._offsets = offsets
self._state = ZephyrThread.READY
self._state = 0
self._priority = 0
self._name = "Unnamed"

Expand Down Expand Up @@ -224,6 +232,25 @@ def name(self):

@property
def description(self):
# Idle threads must be handled separately: when not running,
# they are neither SLEEPING, PENDING nor QUEUED and simply
# exist in a "limbo state" outside scheduler consideration.
Comment on lines +235 to +237
Copy link
Author

@mathieuchopstm mathieuchopstm Dec 5, 2025

Choose a reason for hiding this comment

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

Hi @mathieuchopstm,

thank you for the contributions.

Can you please explain what is the reason and added value of changing current idle thread behavior?

It looks that current display of idle thread can alternate between Ready and Running state which seems to be correct from behavior point of view. Also, idle thread has the lowest priority defined as K_IDLE_PRIO which is displayed as such, yes, it does not compete with other threads of the same priority but it does not seem wrong.

On the screenshot below you can see comparison of old idle display vs your suggested :

Idle_old_new

Hi @MiloradCvjetkovic,

I hoped the following comment in the code would be a sufficient explanation. But since you are asking, I will write a more verbose justification in the next section:


When idle threads are not running (which, as a reminder, is not reflected by any flag in the k_thread structure), they have state equal to 0: this does not correspond to any value in STATE_NAMES and would result in state UNKNOWN being displayed.

Special handling is required to handle this situation properly. I could have reproduced the old behavior, but I wanted to reflect that idle threads are special: they don't compete with other threads for CPU time. Regardless of system configuration, an idle thread will only be scheduled if there is no other runnable thread.

In a sense, idle threads behave as if they had a priority lower than any other thread in the system, but this is not reflected in the base.prio field (which is irrelevant for idle threads anyways).

One possible implementation would be to say that since K_IDLE_PRIO = K_LOWEST_THREAD_PRIO:

if #is idle thread
  if self.state == ZephyrThread.RUNNING:
    return "Running; Priority %d" % self.priority + 1
  else:
    return "Ready; Priority %d" % self.priority + 1

But (1) this relies on implementation details and (2) in my opinion it is clearer to mark idle threads explicitly.

Let me know if you want any change or if the current implementation is acceptable.

Choose a reason for hiding this comment

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

Thank you @mathieuchopstm .

#
# NOTE: RUNNING state indicates that 'current' of a CPU is
# equal to self._base, unlike all other values which come
# directly from 'base.thread_state'; as such, it will be
# valid even for idle threads, unlike all other values.
#
# HACK: assume threads called "idle" are idle threads.
# A proper method would check that this is one of the threads
# in the "z_idle_threads" array (or that it matches one of
# _kernel.cpus[].idle_thread, which is the same thing).
if self._name == "idle":
if self.state == ZephyrThread.RUNNING:
return "Running"
else:
return "Ready"

return "%s; Priority %d" % (self.STATE_NAMES.get(self.state, "UNKNOWN"), self.priority)

@property
Expand Down
Loading