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