1 /*
2 * Copyright (c) 2024, Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * PPIB - PPI Bridge
9 *
10 * This file provides the implementation of the nRF54 PPIB peripherals,
11 * and instantiates N of them (and initializes them at startup and frees them on exit),
12 * as described in the configuration (NHW_config.h)
13 *
14 * Each PPIB instance has a configurable number of channels
15 *
16 * Notes:
17 * * There is no handshake delay in between PPIB bridges,
18 * and therefore overflows cannot occur.
19 * OVERFLOW.SEND is just reset to 0 and never raised by the models.
20 *
21 * * Just like in real HW, TASKS_SEND and EVENT_RECEIVE registers are not
22 * connected to anything. The HW model does not toggle them or react to
23 * them being written.
24 */
25
26 #include <string.h>
27 #include <stdint.h>
28 #include "nsi_tasks.h"
29 #include "bs_tracing.h"
30 #include "bs_oswrap.h"
31 #include "NHW_common_types.h"
32 #include "NHW_config.h"
33 #include "NHW_peri_types.h"
34 #include "NHW_DPPI.h"
35
36 static void nhw_ppib_propagate_event_mate(uint inst, uint ppib_ch);
37
38 struct ppib_status {
39 NRF_PPIB_Type *NRF_PPIB_regs;
40 uint n_ch; /* Number of channels */
41 uint ppib_mate;
42
43 uint dppi_map; //To which DPPI instance are this PPIB subscribe&publish ports connected to
44 //Which of the subscription ports are currently connected, and to which channel:
45 struct nhw_subsc_mem* subscribed; //[n_ch]
46 };
47
48 static struct ppib_status nhw_ppib_st[NHW_PPIB_TOTAL_INST];
49 NRF_PPIB_Type NRF_PPIB_regs[NHW_PPIB_TOTAL_INST];
50
51 /**
52 * Initialize the PPIB model
53 */
nhw_ppib_init(void)54 static void nhw_ppib_init(void) {
55 /* Mapping of peripheral instance to DPPI instance */
56 const uint nhw_ppib_n_ch[NHW_PPIB_TOTAL_INST] = NHW_PPIB_N_CH;
57 const uint nhw_PPIB_dppi_map[NHW_PPIB_TOTAL_INST] = NHW_PPIB_DPPI_MAP;
58 const uint nhw_PPIB_mates[NHW_PPIB_TOTAL_INST] = NHW_PPIB_MATE;
59
60 memset(NRF_PPIB_regs, 0, sizeof(NRF_PPIB_Type) * NHW_PPIB_TOTAL_INST);
61
62 for (int i = 0; i< NHW_PPIB_TOTAL_INST; i++) {
63 nhw_ppib_st[i].NRF_PPIB_regs = &NRF_PPIB_regs[i];
64 nhw_ppib_st[i].n_ch = nhw_ppib_n_ch[i];
65 nhw_ppib_st[i].ppib_mate = nhw_PPIB_mates[i];
66
67 nhw_ppib_st[i].dppi_map = nhw_PPIB_dppi_map[i];
68 nhw_ppib_st[i].subscribed = (struct nhw_subsc_mem*)bs_calloc(nhw_ppib_n_ch[i], sizeof(struct nhw_subsc_mem));
69 }
70 }
71
72 NSI_TASK(nhw_ppib_init, HW_INIT, 100);
73
74 /*
75 * Free all PPIB instances resources before program exit
76 */
nhw_ppib_free(void)77 static void nhw_ppib_free(void)
78 {
79 for (int i = 0; i < NHW_PPIB_TOTAL_INST; i++) {
80 free(nhw_ppib_st[i].subscribed);
81 nhw_ppib_st[i].subscribed = NULL;
82 }
83 }
84
85 NSI_TASK(nhw_ppib_free, ON_EXIT_PRE, 100);
86
87 /**
88 * Check that this is an existing instance and ppib channel in that instance
89 */
nhw_ppib_is_inst_ch_valid(uint ppib_inst,uint ppib_ch,const char * type)90 static inline void nhw_ppib_is_inst_ch_valid(uint ppib_inst, uint ppib_ch, const char *type)
91 {
92 if ((ppib_inst >= NHW_PPIB_TOTAL_INST) || (ppib_ch >= nhw_ppib_st[ppib_inst].n_ch) ) {
93 bs_trace_error_time_line("Attempted to access non existent %s %u (>= %u) in PPIB instance %u\n",
94 type, ppib_ch, nhw_ppib_st[ppib_inst].n_ch, ppib_inst);
95 }
96 }
97
98 /**
99 * Check that a given instance and channel is configured in a given direction
100 * source == 0 => must be a sink
101 * source == 1 => must be a source
102 */
nhw_ppib_confirm_direction(uint inst,uint ppib_ch,bool source)103 static void nhw_ppib_confirm_direction(uint inst, uint ppib_ch, bool source)
104 {
105 bool subscribe_enable = (NRF_PPIB_regs[inst].SUBSCRIBE_SEND[ppib_ch] & (0x1UL << 31)) != 0;
106 bool publish_enable = (NRF_PPIB_regs[inst].PUBLISH_RECEIVE[ppib_ch] & (0x1UL << 31)) != 0;
107
108 if (subscribe_enable && publish_enable) {
109 bs_trace_warning_time_line("PPIB channels must only be configured as sinks or sources, not both."
110 "PPIB %i channel %i\n",
111 inst, ppib_ch);
112 }
113 if (!publish_enable && !source) {
114 bs_trace_warning_time_line("PPIB channel is expected to be a sink (it is receiving an event thru PPIB bus) but its publish register is not enabled"
115 "PPIB %i channel %i\n",
116 inst, ppib_ch);
117 }
118 }
119
nhw_ppib_hardwired_check(uint32_t reg,uint ppib_ch)120 static void nhw_ppib_hardwired_check(uint32_t reg, uint ppib_ch){
121 if (HWH_PPIB_HARDWIRESCHANNELS) {
122 uint dppi_ch = reg & 0xFFU;
123 if (ppib_ch != dppi_ch) {
124 bs_trace_error_time_line("In this device PPIB channels are hardwired to DPPI channels => "
125 "The selected subscribe and publish DPPI channels must match the PPIB channel"
126 "%u != %u\n",
127 ppib_ch, dppi_ch);
128 }
129 }
130 }
131
132 /**
133 * Transfer the event to the mate PPIB
134 */
nhw_ppib_TASK_SEND(uint inst,uint ppib_ch)135 static void nhw_ppib_TASK_SEND(uint inst, uint ppib_ch)
136 {
137 nhw_ppib_confirm_direction(inst, ppib_ch, true);
138 nhw_ppib_propagate_event_mate(nhw_ppib_st[inst].ppib_mate, ppib_ch);
139 }
140
141 /*
142 * Helper called by the DPPI when it gets an event is the subscribed DPPI channel
143 */
nhw_ppib_tasksend_wrap(void * param)144 static void nhw_ppib_tasksend_wrap(void* param) {
145 unsigned int inst = (uintptr_t)param >> 16;
146 uint ppib_ch = (uintptr_t)param & 0xFFFF;
147 nhw_ppib_TASK_SEND(inst, ppib_ch);
148 }
149
nhw_PPIB_regw_sideeffects_SUBSCRIBE_SEND(uint inst,uint ppib_ch)150 void nhw_PPIB_regw_sideeffects_SUBSCRIBE_SEND(uint inst, uint ppib_ch){
151 struct ppib_status *this = &nhw_ppib_st[inst];
152
153 nhw_ppib_is_inst_ch_valid(inst, ppib_ch, "SUBSCRIBE_SEND");
154
155 nhw_ppib_hardwired_check(NRF_PPIB_regs[inst].SUBSCRIBE_SEND[ppib_ch], ppib_ch);
156
157 nhw_dppi_common_subscribe_sideeffect(this->dppi_map,
158 this->NRF_PPIB_regs->SUBSCRIBE_SEND[ppib_ch],
159 &this->subscribed[ppib_ch],
160 nhw_ppib_tasksend_wrap,
161 (void*)((inst << 16) + ppib_ch));
162 }
163
164 /*
165 * This PPIB is receiving an event from its mate PPIB, publish it
166 */
nhw_ppib_propagate_event_mate(uint inst,uint ppib_ch)167 static void nhw_ppib_propagate_event_mate(uint inst, uint ppib_ch) {
168 nhw_ppib_confirm_direction(inst, ppib_ch, false);
169
170 nhw_ppib_hardwired_check(NRF_PPIB_regs[inst].PUBLISH_RECEIVE[ppib_ch], ppib_ch);
171
172 nhw_dppi_event_signal_if(nhw_ppib_st[inst].dppi_map,
173 NRF_PPIB_regs[inst].PUBLISH_RECEIVE[ppib_ch]);
174 }
175