nrfxlib API 3.4.99
Loading...
Searching...
No Matches
softperipheral_regif.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2025 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5 */
6
7#ifndef SOFTPERIPHERAL_REGIF_H__
8#define SOFTPERIPHERAL_REGIF_H__
9
10/* Shared between Host and Service, varies between platforms. */
11#if defined (NRF54L05_XXAA) || defined (NRF54L09_ENGA_XXAA) || defined (NRF54L10_XXAA) || \
12 defined (NRF54L15_XXAA) || defined (NRF54LM20A_ENGA_XXAA) || \
13 defined (NRF54LV10A_XXAA) || defined (NRF54LM20A_XXAA) || defined (NRF54LM20B_XXAA)
14#define SP_VPR_EVENT_IDX 20
15#define NRF_VPR NRF_VPR00
16/* Tasks 16..19 are the only ones the hardware wires to DPPIC00 (channel = index - 16), so they are
17 * the DPPI slots. Tasks 20..22 are triggerable and raise interrupts but have no DPPI path, which is
18 * where the barriers that must never be reachable from DPPI live. */
19#define SP_VPR_TASK_DPPI_0_IDX 16 // DPPIC00 Channel 0 - slot 0
20#define SP_VPR_TASK_SLOT1_IDX 17 // DPPIC00 Channel 1 - slot 1
21#define SP_VPR_TASK_STOP_IDX 18 // DPPIC00 Channel 2 - slot 2
22#define SP_VPR_TASK_SLOT3_IDX 19 // DPPIC00 Channel 3 - slot 3
23#define SP_VPR_TASK_CONFIG_IDX 20 // no DPPI channel
24#define SP_VPR_TASK_ACTION_IDX 21 // no DPPI channel
25#define SP_VPR_IRQHandler VPR00_IRQHandler
26#define SP_VPR_IRQn VPR00_IRQn
27#define SP_VPR_BASE_FREQ_HZ 128000000
28#elif defined(NRF54H20_XXAA)
29#define SP_VPR_EVENT_IDX 28
30#define NRF_VPR NRF_VPR121
31/* Tasks 24..27 are the only ones the hardware wires to DPPIC120, so they are the DPPI slots. Tasks
32 * 16..23 are triggerable and raise interrupts but have no DPPI path - see the L series note. */
33#define SP_VPR_TASK_DPPI_0_IDX 24 // DPPIC120 Channel 4 - slot 0
34#define SP_VPR_TASK_SLOT1_IDX 25 // DPPIC120 Channel 5 - slot 1
35#define SP_VPR_TASK_STOP_IDX 26 // DPPIC120 Channel 6 - slot 2
36#define SP_VPR_TASK_SLOT3_IDX 27 // DPPIC120 Channel 7 - slot 3
37#define SP_VPR_TASK_CONFIG_IDX 16 // no DPPI channel
38#define SP_VPR_TASK_ACTION_IDX 17 // no DPPI channel
39#define SP_VPR_IRQHandler VPR121_IRQHandler
40#define SP_VPR_IRQn VPR121_IRQn
41#define SP_VPR_BASE_FREQ_HZ 320000000
42#else
43#pragma warning "Processor not defined."
44#endif
45
46/* Task/role map, carried in the SPSYNC.DPPIMAP register of each soft peripheral's
47 * register interface, where it is documented field by field and where its reset value defines the
48 * safe default map.
49 *
50 * The definitions below mirror that register so host and service can pack and unpack it without
51 * depending on a particular peripheral's header. Keep them in step with register interface header
52 * file (for example, for sQSPI: nrf_sp_qspi.h).
53 *
54 * The host writes the register and raises the config barrier; the service applies it while parsing
55 * the configuration.
56 *
57 * The soft peripheral owns SP_TASK_ENTRY_COUNT VEVIF tasks, addressed here as entries:
58 * entry 0..3 the DPPI slots - the tasks the hardware wires to a DPPI channel, entry n is
59 * channel n of the SoC's DPPI instance
60 * entry 4..5 tasks with no DPPI path at all, so nothing on the DPPI fabric can reach them
61 *
62 * Two independent fields:
63 * ROLE[entry] which handler that task invokes (allocation)
64 * PERMIT whether the service may subscribe that slot's DPPI channel (enabling, slots only)
65 *
66 * A role lives on exactly one task: both a host task-register write and a DPPI event for that role
67 * end up in the same handler. Moving a role therefore moves everything that reaches it, including
68 * the barrier the host uses - see sp_dppi_role_task_update() below.
69 *
70 * VALID must read as SP_DPPI_MAP_VALID_Value or the whole word is ignored, so a cleared register
71 * can never re-bind a role or permit a subscription.
72 */
73#define SP_DPPI_SLOT_COUNT 4
74#define SP_TASK_ENTRY_COUNT 6
75#define SP_TASK_ENTRY_NODPPI 4 /* first entry without a DPPI channel */
76
77#define SP_DPPI_MAP_VALID_Pos 0UL
78#define SP_DPPI_MAP_VALID_Msk (0xFUL << SP_DPPI_MAP_VALID_Pos)
79#define SP_DPPI_MAP_VALID_Value 0x5UL
80
81#define SP_DPPI_MAP_ROLE_Pos(entry) (4UL + (4UL * (entry)))
82#define SP_DPPI_MAP_ROLE_Msk(entry) (0xFUL << SP_DPPI_MAP_ROLE_Pos(entry))
83
84#define SP_DPPI_MAP_PERMIT_Pos 28UL
85#define SP_DPPI_MAP_PERMIT_Msk (0xFUL << SP_DPPI_MAP_PERMIT_Pos)
86
87/* VPR task index of an entry. Entries 0..3 are consecutive from the first slot. */
88#define SP_VPR_TASK_IDX(entry) \
89 (((entry) < SP_DPPI_SLOT_COUNT) ? (SP_VPR_TASK_DPPI_0_IDX + (entry)) \
90 : (((entry) == SP_TASK_ENTRY_NODPPI) ? SP_VPR_TASK_CONFIG_IDX \
91 : SP_VPR_TASK_ACTION_IDX))
92
93/* Role values. Must stay in sync with the service side. */
94#define SP_DPPI_ROLE_NONE 0UL /* task dispatches nothing */
95#define SP_DPPI_ROLE_DPPI_0 1UL /* green0_fsm_handle_dppi_0 - start a prepared transfer */
96#define SP_DPPI_ROLE_CONFIG 2UL /* green0_fsm_handle_config - target of __CSB */
97#define SP_DPPI_ROLE_ACTION 3UL /* green0_fsm_handle_action - target of __ASB */
98#define SP_DPPI_ROLE_STOP 4UL /* green0_fsm_handle_stop - target of __SSB */
99#define SP_DPPI_ROLE_COUNT 5UL
100
101/* The reset value of SPSYNC.DPPIMAP, repeated here because the service needs it before it has read
102 * the register and the host needs it as the base for a partial change: channel 0 starts a prepared
103 * request, channel 2 stops it, and the two channels in between dispatch nothing - those are the
104 * ones other subsystems (MPSL/SDC) drive. Config and action sit on tasks with no DPPI path.
105 * Nothing is permitted. Must equal <SP>_SPSYNC_DPPIMAP_ResetValue. */
106#define SP_DPPI_MAP_DEFAULT \
107 ((SP_DPPI_MAP_VALID_Value << SP_DPPI_MAP_VALID_Pos) \
108 | (SP_DPPI_ROLE_DPPI_0 << SP_DPPI_MAP_ROLE_Pos(0)) \
109 | (SP_DPPI_ROLE_NONE << SP_DPPI_MAP_ROLE_Pos(1)) \
110 | (SP_DPPI_ROLE_STOP << SP_DPPI_MAP_ROLE_Pos(2)) \
111 | (SP_DPPI_ROLE_NONE << SP_DPPI_MAP_ROLE_Pos(3)) \
112 | (SP_DPPI_ROLE_CONFIG << SP_DPPI_MAP_ROLE_Pos(4)) \
113 | (SP_DPPI_ROLE_ACTION << SP_DPPI_MAP_ROLE_Pos(5)))
114
115/* The wiring soft peripherals used before: every role on a DPPI-capable slot, so all four
116 * channels reach the peripheral. Reachable through the API for applications that own those
117 * channels and want the old behaviour back; combine with SP_DPPI_MAP_PERMIT_ALL. */
118#define SP_DPPI_MAP_LEGACY \
119 ((SP_DPPI_MAP_VALID_Value << SP_DPPI_MAP_VALID_Pos) \
120 | (SP_DPPI_ROLE_DPPI_0 << SP_DPPI_MAP_ROLE_Pos(0)) \
121 | (SP_DPPI_ROLE_CONFIG << SP_DPPI_MAP_ROLE_Pos(1)) \
122 | (SP_DPPI_ROLE_ACTION << SP_DPPI_MAP_ROLE_Pos(2)) \
123 | (SP_DPPI_ROLE_STOP << SP_DPPI_MAP_ROLE_Pos(3)) \
124 | (SP_DPPI_ROLE_NONE << SP_DPPI_MAP_ROLE_Pos(4)) \
125 | (SP_DPPI_ROLE_NONE << SP_DPPI_MAP_ROLE_Pos(5)))
126
127#define SP_DPPI_MAP_PERMIT_ALL (0xFUL << SP_DPPI_MAP_PERMIT_Pos)
128
129#define SP_DPPI_MAP_ROLE_GET(word, slot) \
130 (((word) & SP_DPPI_MAP_ROLE_Msk(slot)) >> SP_DPPI_MAP_ROLE_Pos(slot))
131
132#define SP_DPPI_MAP_PERMIT_GET(word) \
133 (((word) & SP_DPPI_MAP_PERMIT_Msk) >> SP_DPPI_MAP_PERMIT_Pos)
134
135#if defined(NRF_APPLICATION)
136
137#ifndef SP_VPR_FIRMWARE_ADDRESS
138#define SP_VPR_FIRMWARE_ADDRESS 0x00040000
139#endif
140
141/* Config Synchronization Barrier (ASB). */
142#if 1
143#define __XSBx(R, P, T) \
144 do { \
145 sp_handshake_set(R, m_task_count, 0); \
146 nrf_vpr_task_trigger(P, (nrf_vpr_task_t)offsetof(NRF_VPR_Type, TASKS_TRIGGER[T])); \
147 while (sp_handshake_get(R, 0) != sp_handshake_get(R, 1)) { \
148 __NOP(); \
149 __NOP(); \
150 __NOP(); \
151 } \
152 m_task_count++; \
153 } while (0);
154#else
155#define __XSBx(R, P, T) \
156 do { \
157 nrf_vpr_task_trigger(P, (nrf_vpr_task_t)offsetof(NRF_VPR_Type, TASKS_TRIGGER[T])); \
158 } while (0);
159#endif
160
161/* Task index per role. The barriers below trigger the task that currently carries the role, so a
162 * role that the application moves to another slot keeps working - the service dispatches by role,
163 * the host triggers by role. Defaults are the dedicated tasks: config and action have no DPPI
164 * channel at all, stop sits on its slot. sp_dppi_role_task_update() refreshes this whenever a new
165 * map is published; until then the defaults apply. */
166static uint8_t m_sp_role_task[SP_DPPI_ROLE_COUNT] __attribute__((unused)) =
167{
168 [SP_DPPI_ROLE_NONE] = SP_VPR_TASK_CONFIG_IDX, /* never triggered */
169 [SP_DPPI_ROLE_DPPI_0] = SP_VPR_TASK_DPPI_0_IDX,
170 [SP_DPPI_ROLE_CONFIG] = SP_VPR_TASK_CONFIG_IDX,
171 [SP_DPPI_ROLE_ACTION] = SP_VPR_TASK_ACTION_IDX,
172 [SP_DPPI_ROLE_STOP] = SP_VPR_TASK_STOP_IDX,
173};
174
175/* Helpers for the DPPI map word (see the field definitions above). A word that does not carry the
176 * VALID pattern is treated as "not written yet" and replaced by the default map, so an application
177 * that only wants to change one field does not have to know the rest. */
178static inline uint32_t sp_dppi_map_normalize(uint32_t word)
179{
181 ? word
182 : (uint32_t)SP_DPPI_MAP_DEFAULT;
183}
184
185static inline void sp_dppi_role_task_update(uint32_t map_word)
186{
187 map_word = sp_dppi_map_normalize(map_word);
188
189 for (uint8_t entry = 0; entry < SP_TASK_ENTRY_COUNT; entry++)
190 {
191 uint32_t role = SP_DPPI_MAP_ROLE_GET(map_word, entry);
192
193 if (role != SP_DPPI_ROLE_NONE)
194 {
195 m_sp_role_task[role] = (uint8_t)SP_VPR_TASK_IDX(entry);
196 }
197 }
198}
199
200#define __CSB(R) __XSBx(R, NRF_VPR, m_sp_role_task[SP_DPPI_ROLE_CONFIG]);
201#define __ASB(R) __XSBx(R, NRF_VPR, m_sp_role_task[SP_DPPI_ROLE_ACTION]);
202#define __SSB(R) __XSBx(R, NRF_VPR, m_sp_role_task[SP_DPPI_ROLE_STOP]);
203
204/* Pack a complete role map into a map word, keeping the permission bits that are already there.
205 * p_roles has one SP_DPPI_ROLE_* per task entry, index 0..SP_TASK_ENTRY_COUNT-1. Returns false and
206 * leaves *p_word alone if a role is out of range or claimed by more than one entry - a role has to
207 * resolve to a single task, otherwise a task register write and a DPPI event for it would end up in
208 * different handlers. */
209static inline bool sp_dppi_role_map_pack(uint32_t * p_word, const uint8_t * p_roles)
210{
211 uint32_t word = sp_dppi_map_normalize(*p_word) & SP_DPPI_MAP_PERMIT_Msk;
212 uint8_t seen = 0;
213
214 for (uint8_t entry = 0; entry < SP_TASK_ENTRY_COUNT; entry++)
215 {
216 uint8_t role = p_roles[entry];
217
218 if (role >= SP_DPPI_ROLE_COUNT)
219 {
220 return false;
221 }
222 if (role != SP_DPPI_ROLE_NONE)
223 {
224 if ((seen & (1U << role)) != 0U)
225 {
226 return false;
227 }
228 seen |= (uint8_t)(1U << role);
229 }
230 word |= ((uint32_t)role << SP_DPPI_MAP_ROLE_Pos(entry));
231 }
232
234 return true;
235}
236
237static inline uint32_t sp_dppi_map_permit_set(uint32_t word, uint8_t slot, bool enable)
238{
239 uint32_t bit = 1UL << (SP_DPPI_MAP_PERMIT_Pos + slot);
240
241 word = sp_dppi_map_normalize(word);
242 return enable ? (word | bit) : (word & ~bit);
243}
244
245#endif // NRF_APPLICATION
246#endif // SOFTPERIPHERAL_REGIF_H__
#define SP_DPPI_MAP_VALID_Pos
Definition softperipheral_regif.h:77
#define SP_DPPI_MAP_DEFAULT
Definition softperipheral_regif.h:106
#define SP_DPPI_ROLE_DPPI_0
Definition softperipheral_regif.h:95
#define SP_DPPI_ROLE_COUNT
Definition softperipheral_regif.h:99
#define SP_DPPI_ROLE_ACTION
Definition softperipheral_regif.h:97
#define SP_DPPI_MAP_PERMIT_Pos
Definition softperipheral_regif.h:84
#define SP_DPPI_MAP_PERMIT_Msk
Definition softperipheral_regif.h:85
#define SP_DPPI_MAP_ROLE_Pos(entry)
Definition softperipheral_regif.h:81
#define SP_DPPI_MAP_ROLE_GET(word, slot)
Definition softperipheral_regif.h:129
#define SP_DPPI_MAP_VALID_Value
Definition softperipheral_regif.h:79
#define SP_TASK_ENTRY_COUNT
Definition softperipheral_regif.h:74
#define SP_DPPI_MAP_VALID_Msk
Definition softperipheral_regif.h:78
#define SP_DPPI_ROLE_CONFIG
Definition softperipheral_regif.h:96
#define SP_DPPI_ROLE_NONE
Definition softperipheral_regif.h:94
#define SP_DPPI_ROLE_STOP
Definition softperipheral_regif.h:98
#define SP_VPR_TASK_IDX(entry)
Definition softperipheral_regif.h:88