1 /*
2  * Copyright (c) 2017 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * RADIO - 2.4 GHz Radio
10  * https://infocenter.nordicsemi.com/topic/ps_nrf52833/radio.html?cp=5_1_0_5_17
11  * https://infocenter.nordicsemi.com/topic/ps_nrf52833/radio.html?cp=5_1_0_5_17
12  *
13  * Note: as of now, only 1&2Mbps BLE & 15.4 packet formats are supported, there is quite many notes around in the code
14  * where changes would be required to support other formats. PCNF1.STATLEN is always assumed 0
15  *
16  * Note3: Only logical address 0 (in Tx or Rx) is supported
17  *
18  * Note4: only default freq. map supported
19  *
20  * Note5: Only little endian hosts supported (x86 is little endian)
21  *
22  * Note6: RSSI is always sampled at the end of the address (and RSSIEND raised there)
23  *
24  * Note7: Whitening is always/never "used" (it is the phy who would use or not whitening), the radio model can assume it is always used (even that we ignore the initialization register)
25  *
26  * Note8: During idle nothing is sent to the air
27  *
28  * Note9: Double buffering of registers is not implemented. Changing the register during the packet Tx/Rx will cause trouble
29  *        It should be (at least): PACKETPTR @ START
30  *                                 MODE @ TXEN | RXEN
31  *                                 CRC config @ START
32  *
33  * Note10: Regarding MAXLEN:
34  *           if CRCINC==1, the CRC LEN is deducted from the length field, before MAXLEN is checked.
35  *           This seems to be also the real HW behavior
36  *
37  * Note11: Only the BLE & 15.4 CRC polynomials are supported
38  *         During reception we assume that CRCPOLY and CRCINIT are correct on both sides, and just rely on the phy bit error reporting to save processing time
39  *         On transmission we generate the correct CRC for correctness of the channel dump traces (and Ellisys traces)
40  * Note11b:The CRC configuration is directly deduced from the modulation, only BLE and 154 CRCs are supported so far
41  *
42  * Note12: * CCA or ED procedures cannot be performed while the RADIO is performing an actual packet reception (they are exclusive)
43  *         * In CCA Mode2 & 3, this model (due to the Phy) does not search for a SFD, or for a correlation peak
44  *           instead it searches for a compatible modulation of sufficient power (which is in line with what the 802.15.4
45  *           standard specifies)
46  *
47  * Note13: Nothing related to AoA/AoD features (CTE, DFE) is implemented
48  *
49  * Note14: Several 52833 radio state change events are not yet implemented
50  *         (EVENTS_RATEBOOST, EVENTS_MHRMATCH & EVENTS_CTEPRESENT)
51  *
52  * Note15: PDUSTAT not yet implemented
53  *
54  * Note16: No antenna switching
55  *
56  * Note17: Interrupts are modeled as pulses to the NVIC, not level interrupts as they are in reality
57  *
58  * Note18: EVENTS_SYNC:
59  *         a) It is not generated at the exact correct time:
60  *         In this model it is generated at the end of the address (or SFD)
61  *         while according to the infocenter spec this should come at the end of the preamble,
62  *         and only if in coded and 15.4 mode.
63  *         In reality it seems to come somewhere during the preamble for 15.4 and coded phy,
64  *         and somewhere during the address for 1&2Mbps BLE.
65  *         In any case this seems to be a debug signal, and a quite imprecise one,
66  *         so the assumption is that nobody uses it for anything timing critical.
67  *         b) it is only generated when there is a full address match. While in real HW this is not required
68  *         (so false positives happen in real HW)
69  *
70  * Note19: EVENTS_PHYEND
71  *         It is not generated at the exact correct time. In the model it is generated at the
72  *         exact same time as END. While according to the spec, it should be generated with the last
73  *         bit on *air* (That is the Tx chain delay later for Tx, and RxChainDelay earlier for Rx)
74  *
75  * Note20: The LQI value is based on a single measurement at the end of the SFD.
76  *         While the real HW samples it in 3 places during the payload, and the middle one selected.
77  *
78  * Note21: Timings:
79  *          * Radio ramp down time for 2Mbps BLE is 2 microseconds too long.
80  *          * Many timings are simplified, and some events which take slightly different amounts of time to occur
81  *            are produced at the same time as others, or so. Check NRF_RADIO_timings.c for some more notes.
82  *
83  * Note22: EVENTS_FRAMESTART
84  *          * It is generated for all modulation types, this seems to be how the HW behaves even if the spec
85  *            seems to mildly imply it is only for 15.4
86  *          * In Tx: The spec seems unclear about the FRAMESTART being generated or not (after SHR).
87  *            Drawings imply it is, the text that it does not. The HW does. The model does generate it after SHR.
88  *          * In Rx: In the model it is generated at the SHR/SFD end (not PHR), meaning, at the same time as the ADDRESS EVENT
89  *            The spec seems to contradict itself here. But seems in real HW it is generated at the end of the PHR.
90  *
91  * Note23: Powering off/on is not properly modeled (it is mostly ignored)
92  *
93  * Implementation Specification:
94  *   A diagram of the main state machine can be found in docs/RADIO_states.svg
95  *   That main state machine is driven by a timer (Timer_RADIO) which results in calls to nhw_radio_timer_triggered()
96  *   and the tasks which cause transitions and/or the timer to be set to a new value.
97  *
98  *   Apart from this main state machine there is a small state machine for handling the automatic TIFS re-enabling.
99  *   See TIFS_state, Timer_TIFS, nhw_RADIO_fake_task_TRXEN_TIFS, and maybe_prepare_TIFS()
100  *   This TIFS machine piggybacks on the main machine and its timer.
101  *
102  *   And apart from this, there is an "abort" state machine, which is used to handle SW or another peripheral
103  *   triggering a TASK which requires us to stop a transaction with the Phy midway.
104  *   The idea here, is that when we start a transaction with the Phy (say a Tx), we do not know at the start if something
105  *   will want to stop it midway. So we tell the Phy when we start, when we expect to end, but also, when we
106  *   want the Phy to recheck with us if the transaction needs to be aborted midway.
107  *   This recheck time is set to the time anything may decide to stop. Which for simplicity is whenever *anything* may run.
108  *   That is, whenever any timer is scheduled. As this includes other peripherals which may trigger tasks thru the PPI,
109  *   or SW doing so after an interrupt.
110  *   If at any point, a TASK that stops a transaction comes while that transaction is ongoing, the abort state machine will flag it,
111  *   and the next time we need to respond to the Phy we will tell that we are stopping.
112  *
113  *   Apart from these, there is the interaction with the Phy (check ext_2G4_libPhyComv1/docs):
114  *   There is 3 different procedures for this (Tx, Rx & CCA) which fundamentally work in the same way.
115  *   At start_Rx/Tx/CCA_ED() (which is called at the micros when the actual Tx/Rx/CCA/ED measurement starts),
116  *   the Phy is told we want to start, and immediately we block until we get a response from the Phy.
117  *   (1) Here the response may be that:
118  *     * The Phy finished the procedure, in which case we just pre-program the main state machine timer,
119  *       and set registers and other state accordingly. (as we are done interacting with the Phy for this operation)
120  *       OR
121  *     * The Phy asks us to reevaluate if we want to abort. In this case, we hold responding to the Phy
122  *       and instead set the Timer_RADIO_abort_reeval to the time in which we need to respond to the Phy
123  *       and let time pass until that microsecond is ended.
124  *       At that point in time:
125  *          * If SW (or whatever else may trigger a TASK) has caused the procedure to end, we tell the Phy
126  *            we are aborting right now
127  *          * If nothing stopped it yet, we respond with a new abort reevaluation time in the future to the Phy,
128  *            and continue from (1).
129  *       The idea is that Timer_RADIO_abort_reeval is a separate timer that runs in parallel to the main Timer_RADIO and any other
130  *       HW event timer. And as Timer_RADIO_abort_reeval is the last timer scheduled by the HW_model_top in a given time, we will know if
131  *       anything else has affected the RADIO state in a way that requires us to stop the interaction with the Phy or not.
132  *   When we receive the CCA end from the Phy, we will also check the result, set registers accordingly, and pre-set the cca_status so
133  *   as to raise or not the CCABUSY/IDLE signals.
134  *   For an Rx it is marginally more complex, as we not only receive the end (either no sync, or crcok/failed), but also an intermediate
135  *   notification when the address has been received. At this point (address end) we pre-check some of the packet content (address for BLE adv,
136  *   length, etc) and already set some status registers and make some decisions about if we should proceed with the packet or not.
137  *
138  *   The CCA and ED procedures are so similar that they are handled with the same CCA_ED state in the main state machine,
139  *   most of the same CCA_ED code, and the same CCA procedure to the Phy.
140  */
141 
142 #include <string.h>
143 #include <stdbool.h>
144 #include <stdint.h>
145 #include "bs_types.h"
146 #include "bs_tracing.h"
147 #include "bs_utils.h"
148 #include "bs_pc_2G4.h"
149 #include "bs_pc_2G4_utils.h"
150 #include "NHW_common_types.h"
151 #include "NHW_config.h"
152 #include "NHW_peri_types.h"
153 #include "NHW_RADIO.h"
154 #include "NHW_RADIO_signals.h"
155 #include "NHW_RADIO_utils.h"
156 #include "NHW_RADIO_timings.h"
157 #include "NHW_RADIO_bitcounter.h"
158 #include "NHW_RADIO_priv.h"
159 #include "nsi_hw_scheduler.h"
160 #include "NHW_AES_CCM.h"
161 #include "irq_ctrl.h"
162 #include "NRF_HWLowL.h"
163 #include "crc.h"
164 #include "nsi_tasks.h"
165 #include "nsi_hws_models_if.h"
166 #include "weak_stubs.h"
167 
168 #if NHW_RADIO_TOTAL_INST > 1
169 #error "This model only supports 1 instance so far"
170 #endif
171 
172 NRF_RADIO_Type NRF_RADIO_regs;
173 
174 static bs_time_t Timer_RADIO = TIME_NEVER; //main radio timer
175 static bs_time_t Timer_RADIO_abort_reeval = TIME_NEVER; //Abort reevaluation response timer, this timer must have the lowest priority of all events (which may cause an abort)
176 
177 static TIFS_state_t TIFS_state = TIFS_DISABLE;
178 static bool TIFS_ToTxNotRx = false; //Are we in a TIFS automatically starting a Tx from a Rx (true), or Rx from Tx (false)
179 static bs_time_t Timer_TIFS = TIME_NEVER;
180 static bool from_hw_tifs = false; /* Unfortunate hack due to the SW racing the HW to clear SHORTS*/
181 
182 static RADIO_Rx_status_t rx_status;
183 static RADIO_Tx_status_t tx_status;
184 static RADIO_CCA_status_t cca_status;
185 
186 static double bits_per_us; //Bits per us for the ongoing Tx or Rx
187 
188 static bs_time_t next_recheck_time; // when we asked the phy to recheck (in our own time) next time
189 static abort_state_t abort_fsm_state = No_pending_abort_reeval; //This variable shall be set to Tx/Rx_Abort_reeval when the phy is waiting for an abort response (and in no other circumstance)
190 static int aborting_set = 0; //If set, we will abort the current Tx/Rx/CCA at the next abort reevaluation
191 
192 static nrfra_state_t radio_state;
193 static nrfra_sub_state_t radio_sub_state;
194 
195 static uint8_t tx_buf[_NRF_MAX_PACKET_SIZE]; //starting from the header, and including CRC
196 static uint8_t rx_buf[_NRF_MAX_PACKET_SIZE]; // "
197 static uint8_t *rx_pkt_buffer_ptr = (uint8_t*)&rx_buf;
198 
199 static bool radio_on = false;
200 
201 static bool rssi_sampling_on = false;
202 
203 static void start_Tx(void);
204 static void start_Rx(void);
205 static void start_CCA_ED(bool CCA_not_ED);
206 static void Rx_Addr_received(void);
207 static void Tx_abort_eval_respond(void);
208 static void Rx_abort_eval_respond(void);
209 static void CCA_abort_eval_respond(void);
210 static void nhw_radio_device_address_match(uint8_t rx_buf[]);
211 
radio_reset(void)212 static void radio_reset(void) {
213   memset(&NRF_RADIO_regs, 0, sizeof(NRF_RADIO_regs));
214   radio_state = RAD_DISABLED;
215   radio_sub_state = SUB_STATE_INVALID;
216   Timer_RADIO = TIME_NEVER;
217   rssi_sampling_on = false;
218 
219   TIFS_state = TIFS_DISABLE;
220   TIFS_ToTxNotRx = false;
221   Timer_TIFS = TIME_NEVER;
222 
223   //Registers' reset values:
224   NRF_RADIO_regs.FREQUENCY = 0x00000002;
225   NRF_RADIO_regs.DATAWHITEIV = 0x00000040;
226   NRF_RADIO_regs.MODECNF0 = 0x00000200;
227   NRF_RADIO_regs.SFD = 0xA7;
228   NRF_RADIO_regs.CCACTRL = 0x052D0000;
229   NRF_RADIO_regs.CTEINLINECONF = 0x00002800;
230   NRF_RADIO_regs.DFECTRL1 = 0x00023282;
231   for (int i = 0; i < 8; i++)
232     NRF_RADIO_regs.PSEL.DFEGPIO[i] = 0xFFFFFFFF;
233   NRF_RADIO_regs.DFEPACKET.MAXCNT = 0x00001000;
234   NRF_RADIO_regs.POWER = 1;
235 
236   nhwra_signalif_reset();
237 }
238 
nhw_radio_init(void)239 static void nhw_radio_init(void) {
240   nrfra_timings_init();
241   radio_reset();
242   radio_on = false;
243   bits_per_us = 1;
244 }
245 
246 NSI_TASK(nhw_radio_init, HW_INIT, 100);
247 
nhw_radio_get_bpus(void)248 double nhw_radio_get_bpus(void) {
249   return bits_per_us;
250 }
251 
nhwra_set_Timer_RADIO(bs_time_t t)252 static inline void nhwra_set_Timer_RADIO(bs_time_t t){
253   Timer_RADIO = t;
254   nsi_hws_find_next_event();
255 }
256 
nhwra_set_Timer_abort_reeval(bs_time_t t)257 static inline void nhwra_set_Timer_abort_reeval(bs_time_t t){
258   Timer_RADIO_abort_reeval = t;
259   nsi_hws_find_next_event();
260 }
261 
nhw_RADIO_TASK_TXEN(void)262 void nhw_RADIO_TASK_TXEN(void) {
263   if ( ( radio_state != RAD_DISABLED )
264       && ( radio_state != RAD_TXIDLE )
265       && ( radio_state != RAD_RXIDLE ) ){
266     bs_trace_warning_line_time(
267         "NRF_RADIO: TXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
268         radio_state);
269     return;
270   }
271   radio_state = RAD_TXRU;
272   NRF_RADIO_regs.STATE = RAD_TXRU;
273 
274   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(1, from_hw_tifs));
275 }
276 
nhw_RADIO_TASK_RXEN(void)277 void nhw_RADIO_TASK_RXEN(void) {
278   if ( ( radio_state != RAD_DISABLED )
279       && ( radio_state != RAD_TXIDLE )
280       && ( radio_state != RAD_RXIDLE ) ){
281     bs_trace_warning_line_time(
282         "NRF_RADIO: RXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
283         radio_state);
284     return;
285   }
286   TIFS_state = TIFS_DISABLE;
287   radio_state = RAD_RXRU;
288   NRF_RADIO_regs.STATE = RAD_RXRU;
289   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(0, from_hw_tifs));
290 }
291 
abort_if_needed(void)292 static void abort_if_needed(void) {
293   if ( ( abort_fsm_state == Tx_Abort_reeval )
294       || ( abort_fsm_state == Rx_Abort_reeval )
295       || ( abort_fsm_state == CCA_Abort_reeval ) ){
296     //If the phy is waiting for a response from us, we need to tell it, that we are aborting whatever it was doing
297     aborting_set = 1;
298   }
299   /* Note: In Rx, we may be
300    *   waiting to respond to the Phy to an abort reevaluation request abort_fsm_state == Rx_Abort_reeval
301    *   or waiting to reach the address end time to respond to the Phy if we accepted the packet or not
302    * but not both
303    */
304   if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ){
305     //we answer immediately to the phy rejecting the packet
306     p2G4_dev_rxv2_cont_after_addr_nc_b(false, NULL);
307     radio_sub_state = SUB_STATE_INVALID;
308   }
309 }
310 
nhw_RADIO_TASK_START(void)311 void nhw_RADIO_TASK_START(void) {
312   if ( radio_state == RAD_TXIDLE ) {
313     bs_time_t Tx_start_time = nsi_hws_get_time() + nhwra_timings_get_TX_chain_delay();
314     radio_state = RAD_TXSTARTING;
315     NRF_RADIO_regs.STATE = RAD_TX;
316     nhwra_set_Timer_RADIO(Tx_start_time);
317   } else if ( radio_state == RAD_RXIDLE ) {
318     start_Rx();
319   } else {
320     bs_trace_warning_line_time(
321         "NRF_RADIO: TASK_START received while the radio was not in either TXIDLE or RXIDLE but in state %i. It will be ignored => expect problems\n",
322         radio_state);
323   }
324 }
325 
nhw_RADIO_TASK_CCASTART(void)326 void nhw_RADIO_TASK_CCASTART(void) {
327   if ((radio_state != RAD_RXIDLE)){
328     bs_trace_warning_line_time(
329         "NRF_RADIO: CCASTART received when the radio was not RXIDLE but in state %i. "
330         "It will be ignored. Expect problems\n",
331         radio_state);
332     return;
333   }
334   start_CCA_ED(1);
335 }
336 
nhw_RADIO_TASK_CCASTOP(void)337 void nhw_RADIO_TASK_CCASTOP(void) {
338   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED )) {
339     abort_if_needed();
340     radio_state = RAD_RXIDLE;
341     NRF_RADIO_regs.STATE = RAD_RXIDLE;
342     nhwra_set_Timer_RADIO(TIME_NEVER);
343     nhw_RADIO_signal_EVENTS_CCASTOPPED(0);
344   } else {
345     bs_trace_info_line_time(3,
346         "NRF_RADIO: TASK_CCASTOP received while the radio was not on a CCA procedure (was %i, %i). "
347         "It will be ignored\n",
348         radio_state, cca_status.CCA_notED);
349   }
350 }
351 
nhw_RADIO_TASK_EDSTART(void)352 void nhw_RADIO_TASK_EDSTART(void) {
353   if ((radio_state != RAD_RXIDLE)){
354     bs_trace_warning_line_time(
355         "NRF_RADIO: EDSTART received when the radio was not RXIDLE but in state %i. "
356         "It will be ignored. Expect problems\n",
357         radio_state);
358     return;
359   }
360   start_CCA_ED(0);
361 }
362 
nhw_RADIO_TASK_EDSTOP(void)363 void nhw_RADIO_TASK_EDSTOP(void) {
364   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED == 0)) {
365     abort_if_needed();
366     radio_state = RAD_RXIDLE;
367     NRF_RADIO_regs.STATE = RAD_RXIDLE;
368     nhwra_set_Timer_RADIO(TIME_NEVER);
369     nhw_RADIO_signal_EVENTS_EDSTOPPED(0);
370   } else {
371     bs_trace_info_line_time(3,
372         "NRF_RADIO: TASK_EDSTOP received while the radio was not on a ED procedure (was %i, %i). "
373         "It will be ignored\n",
374         radio_state, cca_status.CCA_notED);
375   }
376 }
377 
nhw_RADIO_TASK_STOP(void)378 void nhw_RADIO_TASK_STOP(void) {
379   nhw_radio_stop_bit_counter();
380 
381   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
382     if (radio_state == RAD_TX) {
383       abort_if_needed();
384     }
385     radio_state = RAD_TXIDLE;
386     NRF_RADIO_regs.STATE = RAD_TXIDLE;
387     nhwra_set_Timer_RADIO(TIME_NEVER);
388   } else if ( radio_state == RAD_RX ){
389     abort_if_needed();
390     radio_state = RAD_RXIDLE;
391     NRF_RADIO_regs.STATE = RAD_RXIDLE;
392     nhwra_set_Timer_RADIO(TIME_NEVER);
393   } else if ( radio_state == RAD_CCA_ED ){
394     //The documentation is not clear about what happens if we get a STOP during a CCA or ED procedure,
395     //but it seems for CCA it can cause a bit of a mess depending on CCA mode.
396     //the behavior here is that we stop just like if it was an active Rx, and do *not* trigger a CCASTOPPED or EDSTOPPED event
397     bs_trace_warning_line_time(
398         "NRF_RADIO: TASK_STOP received while the radio was performing a CCA or ED procedure. "
399         "In this models we stop the procedure, but this can cause a mess in real HW\n");
400     abort_if_needed();
401     radio_state = RAD_RXIDLE;
402     NRF_RADIO_regs.STATE = RAD_RXIDLE;
403     nhwra_set_Timer_RADIO(TIME_NEVER);
404   } else {
405     bs_trace_warning_line_time(
406         "NRF_RADIO: TASK_STOP received while the radio was not on either TX or RX but in state %i. "
407         "It will be ignored\n",
408         radio_state);
409   }
410 }
411 
nhw_RADIO_TASK_DISABLE(void)412 void nhw_RADIO_TASK_DISABLE(void) {
413   nhw_radio_stop_bit_counter();
414 
415   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
416     if (radio_state == RAD_TX) {
417       abort_if_needed();
418     }
419     radio_state = RAD_TXIDLE; //Momentary (will be changed in the if below)
420     NRF_RADIO_regs.STATE = RAD_TXIDLE;
421   } else if ( radio_state == RAD_RX ){
422     abort_if_needed();
423     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
424     NRF_RADIO_regs.STATE = RAD_RXIDLE;
425   } else if ( radio_state == RAD_CCA_ED ){
426     //The documentation is not clear about what happens if we get a disable during a CCA  or ED procedure,
427     //the assumption here is that we stop just like if it was an active Rx, but do not trigger a CCASTOPPED or EDSTOPPED event
428     abort_if_needed();
429     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
430     NRF_RADIO_regs.STATE = RAD_RXIDLE;
431   }
432 
433   if (TIFS_state != TIFS_DISABLE) {
434     TIFS_state = TIFS_DISABLE;
435     nhwra_set_Timer_RADIO(TIME_NEVER);
436     Timer_TIFS = TIME_NEVER;
437   }
438 
439   if ( ( radio_state == RAD_TXRU ) || ( radio_state == RAD_TXIDLE ) ) {
440     radio_state = RAD_TXDISABLE;
441     NRF_RADIO_regs.STATE = RAD_TXDISABLE;
442     TIFS_state = TIFS_DISABLE;
443     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_TX_rampdown_time());
444   } else if ( ( radio_state == RAD_RXRU ) || ( radio_state == RAD_RXIDLE ) ) {
445     radio_state = RAD_RXDISABLE;
446     NRF_RADIO_regs.STATE = RAD_RXDISABLE;
447     TIFS_state = TIFS_DISABLE;
448     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_RX_rampdown_time());
449   } else if ( radio_state == RAD_DISABLED ) {
450     //It seems the radio will also signal a DISABLED event even if it was already disabled
451     nhw_radio_stop_bit_counter();
452     nhw_RADIO_signal_EVENTS_DISABLED(0);
453   }
454 }
455 
nhw_RADIO_TASK_RSSISTART(void)456 void nhw_RADIO_TASK_RSSISTART(void) {
457   rssi_sampling_on = true;
458 }
459 
nhw_RADIO_TASK_RSSISTOP(void)460 void nhw_RADIO_TASK_RSSISTOP(void) {
461   rssi_sampling_on = false;
462 }
463 
nhw_RADIO_regw_sideeffects_POWER(void)464 void nhw_RADIO_regw_sideeffects_POWER(void) {
465   if ( NRF_RADIO_regs.POWER == 0 ){
466     radio_on = false;
467   } else {
468     if ( radio_on == false ){
469       radio_on = true;
470       abort_if_needed();
471       radio_reset();
472       nsi_hws_find_next_event();
473     }
474   }
475 }
476 
477 /**
478  * This is a fake task meant to start a HW timer for the TX->RX or RX->TX TIFS
479  */
nhw_RADIO_fake_task_TRXEN_TIFS(void)480 void nhw_RADIO_fake_task_TRXEN_TIFS(void) {
481   if ( TIFS_state == TIFS_WAITING_FOR_DISABLE ) {
482     TIFS_state = TIFS_TRIGGERING_TRX_EN;
483     nhwra_set_Timer_RADIO(Timer_TIFS);
484     if ( Timer_RADIO < nsi_hws_get_time() ){
485       bs_trace_warning_line_time("NRF_RADIO: TIFS Ups: The Ramp down from Rx/Tx into a Tx/Rx takes more than the programmed TIFS time\n");
486     }
487   }
488 }
489 
490 /**
491  * If the HW automatic TIFS switch is enabled, prepare the internals for an automatic switch
492  * (when a fake_task_TRXEN_TIFS is automatically triggered after a disable due to a shortcut)
493  * otherwise do nothing
494  *
495  * Input Tx_Not_Rx: Are we finishing a Tx (true) or an Rx (false)
496  */
maybe_prepare_TIFS(bool Tx_Not_Rx)497 void maybe_prepare_TIFS(bool Tx_Not_Rx){
498   bs_time_t delta;
499   if ( !nhwra_is_HW_TIFS_enabled() ) {
500     TIFS_state = TIFS_DISABLE;
501     return;
502   }
503   if ( NRF_RADIO_regs.SHORTS & RADIO_SHORTS_DISABLED_TXEN_Msk ){
504     TIFS_ToTxNotRx = true;
505   } else {
506     TIFS_ToTxNotRx = false;
507   }
508 
509   if ( Tx_Not_Rx ){ //End of Tx
510     delta = NRF_RADIO_regs.TIFS + nhwra_timings_get_TX_chain_delay() - nhwra_timings_get_rampup_time(0, 1) - 3; /*open slightly earlier to have jitter margin*/
511   } else { //End of Rx
512     delta = NRF_RADIO_regs.TIFS - nhwra_timings_get_Rx_chain_delay() - nhwra_timings_get_TX_chain_delay() - nhwra_timings_get_rampup_time(1, 1) + 1;
513   }
514   Timer_TIFS = nsi_hws_get_time() + delta;
515   TIFS_state = TIFS_WAITING_FOR_DISABLE; /* In Timer_TIFS we will trigger a TxEN or RxEN */
516 }
517 
518 /**
519  * The main radio timer (Timer_RADIO) has just triggered,
520  * continue whatever activity we are on
521  * (typically do something at the end/start of a state, set the new state
522  * and schedule further the next state change)
523  */
nhw_radio_timer_triggered(void)524 static void nhw_radio_timer_triggered(void) {
525   if ( radio_state == RAD_TXRU ){
526     radio_state = RAD_TXIDLE;
527     NRF_RADIO_regs.STATE = RAD_TXIDLE;
528     nhwra_set_Timer_RADIO(TIME_NEVER);
529     nhw_RADIO_signal_EVENTS_READY(0);
530     nhw_RADIO_signal_EVENTS_TXREADY(0);
531   } else if ( radio_state == RAD_RXRU ){
532     radio_state = RAD_RXIDLE;
533     NRF_RADIO_regs.STATE = RAD_RXIDLE;
534     nhwra_set_Timer_RADIO(TIME_NEVER);
535     nhw_RADIO_signal_EVENTS_READY(0);
536     nhw_RADIO_signal_EVENTS_RXREADY(0);
537   } else if ( radio_state == RAD_TXSTARTING ){
538     nhwra_set_Timer_RADIO(TIME_NEVER);
539     start_Tx();
540   } else if ( radio_state == RAD_TX ){
541     if ( radio_sub_state == TX_WAIT_FOR_ADDRESS_END ){
542       radio_sub_state = TX_WAIT_FOR_PAYLOAD_END;
543       nhwra_set_Timer_RADIO(tx_status.PAYLOAD_end_time);
544       nhw_RADIO_signal_EVENTS_ADDRESS(0);
545       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
546     } else if ( radio_sub_state == TX_WAIT_FOR_PAYLOAD_END ) {
547       radio_sub_state = TX_WAIT_FOR_CRC_END;
548       nhwra_set_Timer_RADIO(tx_status.CRC_end_time);
549       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
550     } else if ( radio_sub_state == TX_WAIT_FOR_CRC_END ) {
551       radio_sub_state = SUB_STATE_INVALID;
552       radio_state = RAD_TXIDLE;
553       NRF_RADIO_regs.STATE = RAD_TXIDLE;
554       nhwra_set_Timer_RADIO(TIME_NEVER);
555       nhw_radio_stop_bit_counter();
556       nhw_RADIO_signal_EVENTS_END(0);
557       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
558       maybe_prepare_TIFS(true);
559     }  else { //SUB_STATE_INVALID
560       bs_trace_error_time_line("programming error\n");
561     }
562   } else if ( radio_state == RAD_RX ){
563     if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ) {
564       nhwra_set_Timer_RADIO(TIME_NEVER);
565       nsi_hws_find_next_event();
566       nhw_RADIO_signal_EVENTS_SYNC(0); //See note on EVENTS_SYNC
567       nhw_RADIO_signal_EVENTS_ADDRESS(0);
568       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
569       Rx_Addr_received();
570       radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
571       nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
572     } else if ( radio_sub_state == RX_WAIT_FOR_PAYLOAD_END ) {
573       radio_sub_state = RX_WAIT_FOR_CRC_END;
574       nhwra_set_Timer_RADIO(rx_status.CRC_End_Time);
575       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
576     } else if ( radio_sub_state == RX_WAIT_FOR_CRC_END ) {
577       radio_sub_state = SUB_STATE_INVALID;
578       radio_state = RAD_RXIDLE;
579       NRF_RADIO_regs.STATE = RAD_RXIDLE;
580       nhwra_set_Timer_RADIO(TIME_NEVER);
581       if ( rx_status.CRC_OK ) {
582         nhw_RADIO_signal_EVENTS_CRCOK(0);
583       } else {
584         nhw_RADIO_signal_EVENTS_CRCERROR(0);
585       }
586       nhw_radio_stop_bit_counter();
587       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
588       nhw_RADIO_signal_EVENTS_END(0);
589       maybe_prepare_TIFS(false);
590     } else { //SUB_STATE_INVALID
591       bs_trace_error_time_line("programming error\n");
592     }
593   } else if ( radio_state == RAD_CCA_ED ){
594     radio_state = RAD_RXIDLE;
595     NRF_RADIO_regs.STATE = RAD_RXIDLE;
596     nhwra_set_Timer_RADIO(TIME_NEVER);
597     if (cca_status.CCA_notED) { //CCA procedure ended
598       if (cca_status.is_busy) {
599         nhw_RADIO_signal_EVENTS_CCABUSY(0);
600       } else {
601         nhw_RADIO_signal_EVENTS_CCAIDLE(0);
602       }
603     } else { //ED procedure ended
604       nhw_RADIO_signal_EVENTS_EDEND(0);
605     }
606   } else if ( radio_state == RAD_TXDISABLE ){
607     radio_state = RAD_DISABLED;
608     NRF_RADIO_regs.STATE = RAD_DISABLED;
609     nhwra_set_Timer_RADIO(TIME_NEVER);
610     nhw_radio_stop_bit_counter();
611     nhw_RADIO_signal_EVENTS_DISABLED(0);
612   } else if ( radio_state == RAD_RXDISABLE ){
613     radio_state = RAD_DISABLED;
614     NRF_RADIO_regs.STATE = RAD_DISABLED;
615     nhwra_set_Timer_RADIO(TIME_NEVER);
616     nhw_radio_stop_bit_counter();
617     nhw_RADIO_signal_EVENTS_DISABLED(0);
618   } else {
619     if ( ( radio_state == RAD_DISABLED ) && ( TIFS_state == TIFS_TRIGGERING_TRX_EN ) ) {
620       if ( Timer_RADIO != Timer_TIFS ){
621         bs_trace_warning_line_time("NRF_RADIO: TIFS Ups 3\n");
622       }
623       TIFS_state = TIFS_DISABLE;
624       nhwra_set_Timer_RADIO(TIME_NEVER);
625       from_hw_tifs = true;
626       if ( TIFS_ToTxNotRx ) {
627         nhw_RADIO_TASK_TXEN();
628       } else {
629         nhw_RADIO_TASK_RXEN();
630       }
631       from_hw_tifs = false;
632     } else {
633       bs_trace_error_line_time(
634           "NRF_RADIO: this should not have happened (radio_state =%i)\n",
635           radio_state);
636     }
637   }
638 }
639 
640 NSI_HW_EVENT(Timer_RADIO, nhw_radio_timer_triggered, 990 /*We want the radio to be one of the very last to avoid unnecessary abort re-evaluations to the Phy*/);
641 
642 /**
643  * The abort reevaluation timer has just triggered,
644  * => we can now respond to the Phy with our abort decision
645  */
nhw_radio_timer_abort_reeval_triggered(void)646 static void nhw_radio_timer_abort_reeval_triggered(void) {
647   nhwra_set_Timer_abort_reeval(TIME_NEVER);
648 
649   if ( abort_fsm_state == Tx_Abort_reeval ){
650     abort_fsm_state = No_pending_abort_reeval;
651     Tx_abort_eval_respond();
652   } else if ( abort_fsm_state == Rx_Abort_reeval ) {
653     abort_fsm_state = No_pending_abort_reeval;
654     Rx_abort_eval_respond();
655   } else if ( abort_fsm_state == CCA_Abort_reeval ) {
656     abort_fsm_state = No_pending_abort_reeval;
657     CCA_abort_eval_respond();
658   } else {
659     bs_trace_error_line("The abort timer was left running.. somebody forgot to cleanup..\n");
660   }
661 }
662 
663 NSI_HW_EVENT(Timer_RADIO_abort_reeval, nhw_radio_timer_abort_reeval_triggered, 999 /* Purposely the last (all other events must have been evaluated before) */);
664 
665 /**
666  * Handle all possible responses to a Tx request from the Phy
667  */
handle_Tx_response(int ret)668 static void handle_Tx_response(int ret){
669   if (ret == -1){
670     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"The phy disconnected us during a Tx\n");
671     hwll_disconnect_phy_and_exit();
672   } else if ( ret == P2G4_MSG_TX_END  ) {
673     bs_time_t end_time = hwll_dev_time_from_phy(tx_status.tx_resp.end_time);
674     phy_sync_ctrl_set_last_phy_sync_time( end_time );
675     //The main machine was already pre-programmed at the Tx Start, no need to do anything else now
676   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
677     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
678     abort_fsm_state = Tx_Abort_reeval;
679     nhwra_set_Timer_abort_reeval(next_recheck_time);
680   }
681 }
682 
683 /**
684  * Set the Phy abort structure to the next time we will want to either abort or have a recheck
685  * And store in next_recheck_time the next recheck time
686  */
update_abort_struct(p2G4_abort_t * abort,bs_time_t * next_recheck_time)687 static void update_abort_struct(p2G4_abort_t *abort, bs_time_t *next_recheck_time){
688   //We will want to recheck next time anything may decide to stop the radio, that can be SW or HW
689   //The only logical way to do so is to set it to the next timer whatever it may be as many can trigger SW interrupts
690   *next_recheck_time = nsi_hws_get_next_event_time();
691   abort->recheck_time = hwll_phy_time_from_dev(*next_recheck_time);
692 
693   //We either have decided already we want to abort so we do it right now
694   //or we have not decided yet
695   if ( aborting_set == 1 ) {
696     aborting_set = 0; //By returning nsi_hws_get_time(), we are aborting right now
697     abort->abort_time = hwll_phy_time_from_dev(nsi_hws_get_time());
698   } else {
699     abort->abort_time = TIME_NEVER;
700   }
701 }
702 
703 /**
704  * We have reached the time in which we wanted to reevaluate if we would abort or not
705  * so we answer to the Phy with our decision
706  */
Tx_abort_eval_respond(void)707 static void Tx_abort_eval_respond(void) {
708   //The abort must have been evaluated by now so we can respond to the waiting phy
709   p2G4_abort_t *abort = &tx_status.tx_req.abort;
710 
711   update_abort_struct(abort, &next_recheck_time);
712 
713   int ret = p2G4_dev_provide_new_tx_abort_nc_b(abort);
714 
715   handle_Tx_response(ret);
716 }
717 
718 /*
719  * Actually start the Tx in this microsecond (+ the Tx chain delay in the Phy)
720  */
start_Tx(void)721 static void start_Tx(void) {
722 
723   radio_state = RAD_TX;
724   NRF_RADIO_regs.STATE = RAD_TX;
725 
726   nhwra_check_packet_conf();
727 
728   //TOLOW: Add support for other packet formats and bitrates
729   uint8_t preamble_len = 0;
730   uint8_t address_len = 0;
731   uint8_t header_len = 0;
732   uint payload_len = 0;
733   uint8_t crc_len = nhwra_get_crc_length();
734 
735   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
736     preamble_len = 1; //1 byte
737     address_len = 4;
738     header_len  = 2;
739     bits_per_us = 1;
740   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
741     preamble_len = 2; //2 bytes
742     address_len = 4;
743     header_len  = 2;
744     bits_per_us = 2;
745   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
746     preamble_len = 4;
747     address_len = 1;
748     header_len  = 1;
749     bits_per_us = 0.25;
750   }
751 
752   payload_len = nhwra_tx_copy_payload(tx_buf);
753 
754   /* This code should be generalized to support any CRC configuration (CRCCNF, CRCINIT AND CRCPOLY)
755    * When doing so, we should still calculate the ble and 154 crc's with their optimized table implementations
756    * Here we just assume the CRC is configured as it should given the modulation */
757   uint32_t crc_init = NRF_RADIO_regs.CRCINIT & RADIO_CRCINIT_CRCINIT_Msk;
758   if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit)
759      || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) ) {
760     append_crc_ble(tx_buf, header_len + payload_len, crc_init);
761   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
762     //15.4 does not CRC the length (header) field
763     append_crc_154(&tx_buf[header_len], payload_len, crc_init);
764   }
765 
766   bs_time_t packet_duration; //From preamble to CRC
767   packet_duration  = preamble_len*8 + address_len*8;
768   packet_duration += header_len*8 + payload_len*8 + crc_len*8;
769   packet_duration /= bits_per_us;
770   uint packet_size = header_len + payload_len + crc_len;
771 
772   nhwra_prep_tx_request(&tx_status.tx_req, packet_size, packet_duration);
773 
774   update_abort_struct(&tx_status.tx_req.abort, &next_recheck_time);
775 
776   //Request the Tx from the Phy:
777   int ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req, tx_buf,  &tx_status.tx_resp);
778   handle_Tx_response(ret);
779 
780   tx_status.ADDRESS_end_time = nsi_hws_get_time() + (bs_time_t)((preamble_len*8 + address_len*8)/bits_per_us) - nhwra_timings_get_TX_chain_delay();
781   tx_status.PAYLOAD_end_time = tx_status.ADDRESS_end_time + (bs_time_t)(8*(header_len + payload_len)/bits_per_us);
782   tx_status.CRC_end_time = tx_status.PAYLOAD_end_time + (bs_time_t)(crc_len*8/bits_per_us);
783 
784   radio_sub_state = TX_WAIT_FOR_ADDRESS_END;
785   nhwra_set_Timer_RADIO(tx_status.ADDRESS_end_time);
786 }
787 
788 
Rx_handle_end_response(bs_time_t end_time)789 static void Rx_handle_end_response(bs_time_t end_time) {
790 
791   if (rx_status.rx_resp.status != P2G4_RXSTATUS_HEADER_ERROR) {
792       rx_status.CRC_End_Time = end_time + nhwra_timings_get_Rx_chain_delay();
793   } //Otherwise we do not really now how the Nordic RADIO behaves depending on
794   //where the biterrors are and so forth. So let's always behave like if the
795   //packet lenght was received correctly, and just report a CRC error at the
796   //end of the CRC
797 
798   if ( rx_status.rx_resp.status == P2G4_RXSTATUS_OK ){
799     NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
800     rx_status.CRC_OK = 1;
801     NRF_RADIO_regs.CRCSTATUS = 1;
802   }
803 
804   nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
805 }
806 
807 
Rx_handle_address_end_response(bs_time_t address_time)808 static void Rx_handle_address_end_response(bs_time_t address_time) {
809 
810   rx_status.ADDRESS_End_Time = address_time + nhwra_timings_get_Rx_chain_delay();
811 
812   uint length = nhwra_get_payload_length(rx_buf);
813   uint max_length = nhwra_get_MAXLEN();
814 
815   if (length > max_length) {
816     // We reject the packet right away, setting the CRC error, and timers as expected
817     bs_trace_warning_time_line("NRF_RADIO: received a packet longer than the configured MAXLEN (%i>%i). Truncating it\n", length, max_length);
818     length  = max_length;
819     NRF_RADIO_regs.PDUSTAT = RADIO_PDUSTAT_PDUSTAT_Msk;
820     rx_status.packet_rejected = true;
821   } else {
822     NRF_RADIO_regs.PDUSTAT = 0;
823     rx_status.packet_rejected = false;
824   }
825 
826   //TODO: Discard Ieee802154_250Kbit frames with length == 0
827 
828   bs_time_t payload_end = 0;
829 
830   if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit)
831       || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit)) {
832     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((2+length)*8/bits_per_us);
833   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
834     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((1+length)*8/bits_per_us);
835   } //Eventually this should be generalized with the packet configuration
836 
837   rx_status.PAYLOAD_End_Time = nhwra_timings_get_Rx_chain_delay() +
838                                hwll_dev_time_from_phy(payload_end);
839 
840   rx_status.CRC_End_Time = rx_status.PAYLOAD_End_Time + rx_status.CRC_duration; //Provisional value (if we are accepting the packet)
841 
842   //Copy the whole packet (S0, lenght, S1 & payload) excluding the CRC.
843   if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit)
844       || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit)) {
845     if (rx_status.rx_resp.packet_size >= 5) { /*At least the header and CRC, otherwise better to not try to copy it*/
846       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
847       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1] = rx_buf[1];
848       /* We cheat a bit and copy the whole packet already (The AAR block will look in Adv packets after 64 bits)*/
849       memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[2 + rx_status.S1Offset],
850           &rx_buf[2] , length);
851     }
852   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
853     if (rx_status.rx_resp.packet_size >= 3) { /*At least the header and CRC, otherwise better to not try to copy it*/
854             ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
855             memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset],
856                 &rx_buf[1] , length);
857           }
858   } //Eventually this should be generalized with the packet configuration
859 
860   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
861     //The real HW only copies the LQI value after the payload in this mode
862     //Note that doing it this early is a cheat
863     double RSSI = p2G4_RSSI_value_to_dBm(rx_status.rx_resp.rssi.RSSI);
864     uint8_t LQI = nhwra_dBm_to_modem_LQIformat(RSSI);
865     //Eventually this should be generalized with the packet configuration:
866     ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset + length] = LQI;
867   }
868 
869 }
870 
871 /**
872  * Handle all possible responses from the phy to a Rx request
873  */
handle_Rx_response(int ret)874 static void handle_Rx_response(int ret){
875   if ( ret == -1 ){
876     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"Communication with the phy closed during Rx\n");
877     hwll_disconnect_phy_and_exit();
878 
879   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
880 
881     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
882     abort_fsm_state = Rx_Abort_reeval;
883     nhwra_set_Timer_abort_reeval( BS_MAX(next_recheck_time,nsi_hws_get_time()) );
884 
885   } else if ( ( ret == P2G4_MSG_RXV2_ADDRESSFOUND ) && ( radio_state == RAD_RX /*if we havent aborted*/ ) ) {
886 
887     bs_time_t address_time = hwll_dev_time_from_phy(rx_status.rx_resp.rx_time_stamp); //this is the end of the sync word in air time
888     phy_sync_ctrl_set_last_phy_sync_time(address_time);
889     Rx_handle_address_end_response(address_time);
890     radio_sub_state = RX_WAIT_FOR_ADDRESS_END;
891     nhwra_set_Timer_RADIO(rx_status.ADDRESS_End_Time);
892 
893   } else if ( ( ret == P2G4_MSG_RXV2_END ) && ( radio_state == RAD_RX /*if we havent aborted*/ ) ) {
894 
895     bs_time_t end_time = hwll_dev_time_from_phy(rx_status.rx_resp.end_time);
896     phy_sync_ctrl_set_last_phy_sync_time(end_time);
897     Rx_handle_end_response(end_time);
898   }
899 }
900 
901 /**
902  * We have reached the time in which we wanted to reevaluate if we would abort or not
903  * so we answer to the phy with our decision
904  */
Rx_abort_eval_respond(void)905 static void Rx_abort_eval_respond(void) {
906   //The abort must have been evaluated by now so we can respond to the waiting phy
907   p2G4_abort_t *abort = &rx_status.rx_req.abort;
908   update_abort_struct(abort, &next_recheck_time);
909 
910   int ret = p2G4_dev_provide_new_rxv2_abort_nc_b(abort);
911 
912   handle_Rx_response(ret);
913 }
914 
915 /*
916  * Actually start the Rx in this microsecond
917  */
start_Rx(void)918 static void start_Rx(void) {
919   #define RX_N_ADDR 8 /* How many addresses we can search in parallel */
920   p2G4_address_t rx_addresses[RX_N_ADDR];
921 
922   nhwra_check_packet_conf();
923 
924   radio_state = RAD_RX;
925   NRF_RADIO_regs.STATE = RAD_RX;
926   NRF_RADIO_regs.CRCSTATUS = 0;
927 
928   if ( NRF_RADIO_regs.PCNF0 & ( RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos ) ){
929     rx_status.S1Offset = 1; /*1 byte offset in RAM (S1 length > 8 not supported)*/
930   } else {
931     rx_status.S1Offset = 0;
932   }
933 
934   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
935     bits_per_us = 1;
936   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
937     bits_per_us = 2;
938   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
939     bits_per_us = 0.25;
940   }
941   rx_status.CRC_duration = nhwra_get_crc_length()*8/bits_per_us;
942 
943   rx_status.CRC_OK = false;
944   rx_status.rx_resp.status = P2G4_RXSTATUS_NOSYNC;
945 
946   nhwra_prep_rx_request(&rx_status.rx_req, rx_addresses);
947 
948   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
949 
950   //attempt to receive
951   int ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req,
952       rx_addresses,
953       &rx_status.rx_resp,
954       &rx_pkt_buffer_ptr,
955       _NRF_MAX_PACKET_SIZE);
956 
957   radio_sub_state = SUB_STATE_INVALID;
958   nhwra_set_Timer_RADIO(TIME_NEVER);
959 
960   handle_Rx_response(ret);
961 }
962 
963 /**
964  * This function is called at the time when the Packet address would have been
965  * completely received
966  * (at the time of the end of the last bit of the packet address)
967  * To continue processing the reception (the Phy was left waiting for a response)
968  *
969  * Note that libPhyCom has already copied the whole packet into the input buffer
970  */
Rx_Addr_received(void)971 static void Rx_Addr_received(void) {
972 
973   bool accept_packet = !rx_status.packet_rejected;
974 
975   if ( rssi_sampling_on ){
976     NRF_RADIO_regs.RSSISAMPLE = nhwra_RSSI_value_to_modem_format(p2G4_RSSI_value_to_dBm(rx_status.rx_resp.rssi.RSSI));
977     nhw_RADIO_signal_EVENTS_RSSIEND(0);
978   }
979 
980   NRF_RADIO_regs.RXMATCH = 0; //The only we support so far
981 
982   if (NRF_RADIO_regs.DACNF & 0xFF) { /*If any of the addresses for device address match is enabled*/
983     /*
984      * NOTE: we cheat and we already check the advertisement addresses and
985      * raise the event, even though we should wait for 16 + 48 bits more
986      *
987      * If this is a problem, add a new timer and Rx state and delay raising the event
988      * until then
989      */
990     nhw_radio_device_address_match(rx_buf);
991   }
992 
993   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
994   int ret = p2G4_dev_rxv2_cont_after_addr_nc_b(accept_packet, &rx_status.rx_req.abort);
995 
996   if ( accept_packet ){
997     handle_Rx_response(ret);
998   } else {
999     //We said we don't want to continue => there will be no response (ret==0 always). We just close the reception like if the phy finished on its own even though we finished it
1000 
1001     //We do what would correspond to Rx_handle_end_response() as it won't get called
1002     NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
1003     nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
1004   }
1005 }
1006 
1007 /**
1008  * Check if the address in the received (advertisement) packet
1009  * matches one configured in the DAP/DAB registers as set by DACNF
1010  *
1011  * If it does, it sets appropriately the DAI register,
1012  * in any case, it generates the DEVMATCH and DEVMISS signals accordingly
1013  *
1014  * Note that, as specified in the infocenter documentation,
1015  * the address is assumed to be the first 48 bits after the 2 byte header
1016  * and the TxAddr bit to be 7th bit in 1st header byte as per the BT Core spec.
1017  */
nhw_radio_device_address_match(uint8_t rx_buf[])1018 static void nhw_radio_device_address_match(uint8_t rx_buf[]) {
1019   bool match_found = false;
1020   bool nomatch;
1021   int TxAdd;
1022 
1023   for (int i = 0 ; i < 8; i++) {
1024     if (((NRF_RADIO_regs.DACNF >> i) & 1) == 0 ) {
1025       continue;
1026     }
1027 
1028     TxAdd = (NRF_RADIO_regs.DACNF >> (i + 8)) & 1;
1029 
1030     if (TxAdd != ((rx_buf[0] >> 6) & 1) ) {
1031       continue;
1032     }
1033 
1034     nomatch = (*(uint32_t *)(rx_buf + 2) != NRF_RADIO_regs.DAB[i]);
1035     uint32_t DAP = NRF_RADIO_regs.DAP[i] & UINT16_MAX;
1036     nomatch |= (*(uint16_t *)(rx_buf + 6) != DAP);
1037 
1038     if (nomatch) {
1039       continue;
1040     }
1041 
1042     match_found = true;
1043     NRF_RADIO_regs.DAI = i;
1044     break;
1045   }
1046 
1047   if (match_found) {
1048     nhw_RADIO_signal_EVENTS_DEVMATCH(0);
1049   } else {
1050     nhw_RADIO_signal_EVENTS_DEVMISS(0);
1051   }
1052 }
1053 
CCA_handle_end_response(void)1054 static void CCA_handle_end_response(void) {
1055   //Depending on mode, set status and registers
1056   //raising CCAIDLE, CCABUSY or EDEND will happen in the correct time in the main machine
1057 
1058   if (cca_status.CCA_notED) { //End a CCA procedure
1059     uint CCAMode = (NRF_RADIO_regs.CCACTRL & RADIO_CCACTRL_CCAMODE_Msk) >> RADIO_CCACTRL_CCAMODE_Pos;
1060 
1061     if ((CCAMode == RADIO_CCACTRL_CCAMODE_EdMode)
1062         || (CCAMode == RADIO_CCACTRL_CCAMODE_EdModeTest1)) {
1063       cca_status.is_busy = cca_status.cca_resp.rssi_overthreshold;
1064     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierMode) {
1065       cca_status.is_busy = cca_status.cca_resp.mod_found;
1066     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierAndEdMode) {
1067       cca_status.is_busy = cca_status.cca_resp.mod_found
1068           && cca_status.cca_resp.rssi_overthreshold;
1069     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierOrEdMode) {
1070       cca_status.is_busy = cca_status.cca_resp.mod_found
1071           || cca_status.cca_resp.rssi_overthreshold;
1072     } else {
1073       bs_trace_error_time_line("%s, CCAMODE=%i suppport not yet implemented\n",
1074           __func__, CCAMode);
1075     }
1076   } else { // Ending an ED procedure
1077     double RSSI = p2G4_RSSI_value_to_dBm(cca_status.cca_resp.RSSI_max);
1078     NRF_RADIO_regs.EDSAMPLE = nhwra_dBm_to_modem_LQIformat(RSSI);
1079   }
1080 }
1081 
1082 /**
1083  * Handle all possible responses to a CCA request from the Phy
1084  */
handle_CCA_response(int ret)1085 static void handle_CCA_response(int ret){
1086   if (ret == -1){
1087     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"The Phy disconnected us during a CCA procedure\n");
1088     hwll_disconnect_phy_and_exit();
1089   } else if ( ret == P2G4_MSG_CCA_END  ) {
1090     bs_time_t end_time = hwll_dev_time_from_phy(cca_status.cca_resp.end_time);
1091     phy_sync_ctrl_set_last_phy_sync_time( end_time );
1092     cca_status.CCA_end_time = end_time;
1093     if (radio_state == RAD_CCA_ED) { /*if we haven't aborted*/
1094       nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1095     }
1096     CCA_handle_end_response();
1097   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
1098     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
1099     abort_fsm_state = CCA_Abort_reeval;
1100     nhwra_set_Timer_abort_reeval(next_recheck_time);
1101   }
1102 }
1103 
1104 /**
1105  * We have reached the time in which we wanted to reevaluate if we would abort or not
1106  * so we answer to the Phy with our decision
1107  */
CCA_abort_eval_respond(void)1108 static void CCA_abort_eval_respond(void) {
1109   //The abort must have been evaluated by now so we can respond to the waiting phy
1110   p2G4_abort_t *abort = &cca_status.cca_req.abort;
1111 
1112   update_abort_struct(abort, &next_recheck_time);
1113 
1114   int ret = p2G4_dev_provide_new_cca_abort_nc_b(abort);
1115 
1116   handle_CCA_response(ret);
1117 }
1118 
1119 /**
1120  * Start CCA or ED procedure right now.
1121  * input: CCA_not_ED = 1 for CCA, 0 for ED
1122  */
start_CCA_ED(bool CCA_not_ED)1123 static void start_CCA_ED(bool CCA_not_ED){
1124 
1125   radio_state = RAD_CCA_ED;
1126 
1127   cca_status.CCA_notED = CCA_not_ED;
1128   cca_status.is_busy = false;
1129 
1130   nhwra_prep_cca_request(&cca_status.cca_req, CCA_not_ED);
1131 
1132   update_abort_struct(&cca_status.cca_req.abort, &next_recheck_time);
1133 
1134   //Expected end time; note that it may be shorter if detect over threshold is set
1135   cca_status.CCA_end_time = nsi_hws_get_time() + cca_status.cca_req.scan_duration;
1136   nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1137 
1138   //Request the CCA from the Phy:
1139   int ret = p2G4_dev_req_cca_nc_b(&cca_status.cca_req, &cca_status.cca_resp);
1140   handle_CCA_response(ret);
1141 }
1142