1 /*
2  * Copyright (c) 2018-2019 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/toolchain.h>
8 #include <zephyr/types.h>
9 
10 #include "hal/ccm.h"
11 #include "hal/radio.h"
12 
13 #include "util/memq.h"
14 
15 #include "pdu_vendor.h"
16 #include "pdu.h"
17 
18 #include "lll.h"
19 
20 static uint8_t latency_min = (uint8_t) -1;
21 static uint8_t latency_max;
22 static uint8_t latency_prev;
23 static uint8_t cputime_min = (uint8_t) -1;
24 static uint8_t cputime_max;
25 static uint8_t cputime_prev;
26 static uint32_t timestamp_latency;
27 
lll_prof_latency_capture(void)28 void lll_prof_latency_capture(void)
29 {
30 	/* sample the packet timer, use it to calculate ISR latency
31 	 * and generate the profiling event at the end of the ISR.
32 	 */
33 	radio_tmr_sample();
34 }
35 
36 #if defined(HAL_RADIO_GPIO_HAVE_PA_PIN)
37 static uint32_t timestamp_radio_end;
38 
lll_prof_radio_end_backup(void)39 uint32_t lll_prof_radio_end_backup(void)
40 {
41 	/* PA enable is overwriting packet end used in ISR profiling, hence
42 	 * back it up for later use.
43 	 */
44 	timestamp_radio_end = radio_tmr_end_get();
45 
46 	return timestamp_radio_end;
47 }
48 #endif /* !HAL_RADIO_GPIO_HAVE_PA_PIN */
49 
lll_prof_cputime_capture(void)50 void lll_prof_cputime_capture(void)
51 {
52 	/* get the ISR latency sample */
53 	timestamp_latency = radio_tmr_sample_get();
54 
55 	/* sample the packet timer again, use it to calculate ISR execution time
56 	 * and use it in profiling event
57 	 */
58 	radio_tmr_sample();
59 }
60 
lll_prof_send(void)61 void lll_prof_send(void)
62 {
63 	uint8_t latency, cputime, prev;
64 	uint8_t chg = 0U;
65 
66 	/* calculate the elapsed time in us since on-air radio packet end
67 	 * to ISR entry
68 	 */
69 #if defined(HAL_RADIO_GPIO_HAVE_PA_PIN)
70 	latency = timestamp_latency - timestamp_radio_end;
71 #else /* !HAL_RADIO_GPIO_HAVE_PA_PIN */
72 	latency = timestamp_latency - radio_tmr_end_get();
73 #endif /* !HAL_RADIO_GPIO_HAVE_PA_PIN */
74 
75 	/* check changes in min, avg and max of latency */
76 	if (latency > latency_max) {
77 		latency_max = latency;
78 		chg = 1U;
79 	}
80 	if (latency < latency_min) {
81 		latency_min = latency;
82 		chg = 1U;
83 	}
84 
85 	/* check for +/- 1us change */
86 	prev = ((uint16_t)latency_prev + latency) >> 1;
87 	if (prev != latency_prev) {
88 		latency_prev = latency;
89 		chg = 1U;
90 	}
91 
92 	/* calculate the elapsed time in us since ISR entry */
93 	cputime = radio_tmr_sample_get() - timestamp_latency;
94 
95 	/* check changes in min, avg and max */
96 	if (cputime > cputime_max) {
97 		cputime_max = cputime;
98 		chg = 1U;
99 	}
100 
101 	if (cputime < cputime_min) {
102 		cputime_min = cputime;
103 		chg = 1U;
104 	}
105 
106 	/* check for +/- 1us change */
107 	prev = ((uint16_t)cputime_prev + cputime) >> 1;
108 	if (prev != cputime_prev) {
109 		cputime_prev = cputime;
110 		chg = 1U;
111 	}
112 
113 	/* generate event if any change */
114 	if (chg) {
115 		struct node_rx_pdu *rx;
116 
117 		/* NOTE: enqueue only if rx buffer available, else ignore */
118 		rx = ull_pdu_rx_alloc_peek(3);
119 		if (rx) {
120 			struct pdu_data *pdu;
121 			struct profile *p;
122 
123 			ull_pdu_rx_alloc();
124 
125 			rx->hdr.type = NODE_RX_TYPE_PROFILE;
126 			rx->hdr.handle = 0xFFFF;
127 
128 			pdu = (void *)rx->pdu;
129 			p = &pdu->profile;
130 			p->lcur = latency;
131 			p->lmin = latency_min;
132 			p->lmax = latency_max;
133 			p->cur = cputime;
134 			p->min = cputime_min;
135 			p->max = cputime_max;
136 
137 			ull_rx_put_sched(rx->hdr.link, rx);
138 		}
139 	}
140 }
141