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.a: A RSSISTART task will be fully instantaneous (instead of taking ~0.25 micros)
23  * Note6.b: A RSSISTART task will (by now) use the latest RSSI sample from the Phy and not request a new one.
24  *          If the stack is triggering this task to do a RSSI measurement without an actual packet reception
25  *          the measurement will be wrong.
26  *          Doing an immediate measurement requires an up to date libPhyCom and Phy. To avoid forcing users to update just for this, we postpone this feature.
27  *          This feature should be enabled as soon as a Phy update is required for another reason.
28  * Note6.c: The spec says the radio must be actively receiving when receiving a RSSISTART task, but
29  *        not what happens otherwise. The model just warns and uses the last value.
30  *
31  * 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)
32  *
33  * Note8: During idle nothing is sent to the air
34  *
35  * Note9: Double buffering of registers is not implemented. Changing the register during the packet Tx/Rx will cause trouble
36  *        It should be (at least): PACKETPTR @ START
37  *                                 MODE @ TXEN | RXEN
38  *                                 CRC config @ START
39  *
40  * Note10: Regarding MAXLEN:
41  *           if CRCINC==1, the CRC LEN is deducted from the length field, before MAXLEN is checked.
42  *           This seems to be also the real HW behavior
43  *         Bit errors in the length field reception, or even bit errors in the CI field reception (for Coded phy)
44  *         are not accounted for when handling the length field in this model. The model will always
45  *         act on the transmitted length field.
46  *
47  * Note11: Only the BLE & 15.4 CRC polynomials are supported
48  *         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
49  *         On transmission we generate the correct CRC for correctness of the channel dump traces (and Ellisys traces)
50  * Note11b:The CRC configuration is directly deduced from the modulation, only BLE and 154 CRCs are supported so far
51  *
52  * Note12: * CCA or ED procedures cannot be performed while the RADIO is performing an actual packet reception (they are exclusive)
53  *         * In CCA Mode2 & 3, this model (due to the Phy) does not search for a SFD, or for a correlation peak
54  *           instead it searches for a compatible modulation of sufficient power (which is in line with what the 802.15.4
55  *           standard specifies)
56  *
57  * Note13: Nothing related to AoA/AoD features (CTE, DFE) is implemented
58  *
59  * Note14: Several 52833 radio state change events are not yet implemented
60  *         (EVENTS_MHRMATCH & EVENTS_CTEPRESENT)
61  *
62  * Note16: No antenna switching
63  *
64  * Note17: Interrupts are modeled as pulses to the NVIC, not level interrupts as they are in reality
65  *
66  * Note18: EVENTS_SYNC:
67  *         a) It is not generated at the exact correct time:
68  *         In this model it is generated at the end of the address (or SFD)
69  *         while according to the infocenter spec this should come at the end of the preamble,
70  *         and only if in coded and 15.4 mode.
71  *         In reality it seems to come somewhere during the preamble for 15.4 and coded phy,
72  *         and somewhere during the address for 1&2Mbps BLE.
73  *         In any case this seems to be a debug signal, and a quite imprecise one,
74  *         so the assumption is that nobody uses it for anything timing critical.
75  *         b) it is only generated when there is a full address match. While in real HW this is not required
76  *         (so false positives happen in real HW)
77  *
78  * Note19: EVENTS_PHYEND
79  *         It is not generated at the exact correct time. In the model it is generated at the
80  *         exact same time as END. While according to the spec, it should be generated with the last
81  *         bit on *air* (That is the Tx chain delay later for Tx, and RxChainDelay earlier for Rx)
82  *
83  * Note20: The LQI value is based on a single measurement at the end of the SFD.
84  *         While the real HW samples it in 3 places during the payload, and the middle one selected.
85  *
86  * Note21: Timings:
87  *          * Radio ramp down time for 2Mbps BLE is 2 microseconds too long.
88  *          * Many timings are simplified, and some events which take slightly different amounts of time to occur
89  *            are produced at the same time as others, or so. Check NRF_RADIO_timings.c for some more notes.
90  *
91  * Note21.b: CodedPhy timings:
92  *          * For CodedPhy the spec lacks radio timings, so the values used are mostly rounded
93  *            versions of the ones the Zephyr controller used
94  *          * At this point (for simplicity) The Rx chain delay for S=2 and S=8 are modeled as being equal.
95  *          * It is quite unclear if the CRCOK/ERROR and PAYLOAD events are delayed by the equivalent
96  *            of the conv. decoder taking 2 extra input bits or not, and if the ADDRESS event has an
97  *            equivalent delay or not.
98  *            At this point the model does not add a different delay to these, so this events
99  *            timings should be considered a rough guess. CRCOK/ERROR is generated at the same time
100  *            and the END event based on the end of TERM2.
101  *
102  * Note22: EVENTS_FRAMESTART
103  *          * It is generated for all modulation types, this seems to be how the HW behaves even if the spec
104  *            seems to mildly imply it is only for 15.4
105  *          * In Tx: The spec seems unclear about the FRAMESTART being generated or not (after SHR).
106  *            Drawings imply it is, the text that it does not. The HW does. The model does generate it after SHR.
107  *          * In Rx: In the model it is generated at the SHR/SFD end (not PHR), meaning, at the same time as the ADDRESS EVENT
108  *            The spec seems to contradict itself here. But seems in real HW it is generated at the end of the PHR.
109  *
110  * Note23: For nRF52/53: Powering off/on is not properly modeled (it is mostly ignored)
111  *         (In the real setting POWER to 0, resets most registers. That is not the case in the models)
112  *
113  * Note24: Only PCNF1.ENDIAN == Little is supported (What BT and 802.15.4 use)
114  *
115  * Note25: The RATEBOOST event in real HW seems to be generated: only for Rx, only if the FEC2 block has S=2,
116  *         and roughly at the end of TERM1. The models generates it at that point and only in that case.
117  *         (It's timing is probably a bit off compared to real HW)
118  *
119  *
120  *
121  * Implementation Specification:
122  *   A diagram of the main state machine can be found in docs/RADIO_states.svg
123  *   That main state machine is driven by a timer (Timer_RADIO) which results in calls to nhw_radio_timer_triggered()
124  *   and the tasks which cause transitions and/or the timer to be set to a new value.
125  *
126  *   Apart from this main state machine there is a small state machine for handling the automatic TIFS re-enabling.
127  *   See TIFS_state, Timer_TIFS, nhw_RADIO_fake_task_TRXEN_TIFS, and maybe_prepare_TIFS()
128  *   This TIFS machine piggybacks on the main machine and its timer.
129  *
130  *   And apart from this, there is an "abort" state machine, which is used to handle SW or another peripheral
131  *   triggering a TASK which requires us to stop a transaction with the Phy midway.
132  *   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
133  *   will want to stop it midway. So we tell the Phy when we start, when we expect to end, but also, when we
134  *   want the Phy to recheck with us if the transaction needs to be aborted midway.
135  *   This recheck time is set to the time anything may decide to stop. Which for simplicity is whenever *anything* may run.
136  *   That is, whenever any timer is scheduled. As this includes other peripherals which may trigger tasks thru the PPI,
137  *   or SW doing so after an interrupt.
138  *   If at any point, a TASK that stops a transaction comes while that transaction is ongoing, the abort state machine will flag it,
139  *   and the next time we need to respond to the Phy we will tell that we are stopping.
140  *
141  *   Apart from these, there is the interaction with the Phy (check ext_2G4_libPhyComv1/docs):
142  *   There is 3 different procedures for this (Tx, Rx & CCA) which fundamentally work in the same way.
143  *   At start_Rx/Tx/CCA_ED() (which is called at the micros when the actual Tx/Rx/CCA/ED measurement starts),
144  *   the Phy is told we want to start, and immediately we block until we get a response from the Phy.
145  *   (1) Here the response may be that:
146  *     * The Phy finished the procedure, in which case we just pre-program the main state machine timer,
147  *       and set registers and other state accordingly. (as we are done interacting with the Phy for this operation)
148  *       OR
149  *     * The Phy asks us to reevaluate if we want to abort. In this case, we hold responding to the Phy
150  *       and instead set the Timer_RADIO_abort_reeval to the time in which we need to respond to the Phy
151  *       and let time pass until that microsecond is ended.
152  *       At that point in time:
153  *          * If SW (or whatever else may trigger a TASK) has caused the procedure to end, we tell the Phy
154  *            we are aborting right now
155  *          * If nothing stopped it yet, we respond with a new abort reevaluation time in the future to the Phy,
156  *            and continue from (1).
157  *       The idea is that Timer_RADIO_abort_reeval is a separate timer that runs in parallel to the main Timer_RADIO and any other
158  *       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
159  *       anything else has affected the RADIO state in a way that requires us to stop the interaction with the Phy or not.
160  *   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
161  *   as to raise or not the CCABUSY/IDLE signals.
162  *   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
163  *   notification when the address has been received. At this point (address end) we pre-check some of the packet content (address for BLE adv,
164  *   length, etc) and already set some status registers and make some decisions about if we should proceed with the packet or not.
165  *
166  *   The CCA and ED procedures are so similar that they are handled with the same CCA_ED state in the main state machine,
167  *   most of the same CCA_ED code, and the same CCA procedure to the Phy.
168  *
169  *   For BLE Coded Phy, transmissions and receptions are actually handled as 2 piggybacking ones:
170  *   One for the FEC1 block and another for the FEC2.
171  *   * In transmission, during start_Tx() both FEC1 and FEC2 tx_req and tx_req_fec1 structures are
172  *   filled in, and the transmission for the FEC1 is started. When the FEC2 transmission start time
173  *   has been reached (the micros after the FEC1 has ended), the main state state machine will call
174  *   start_Tx_FEC2() which will update the FEC2 tx_req, and start it. From there it will continue
175  *   like a normal transmission until the CRC end.
176  *   * In receptions similarly start_Rx() will prefill both rx_req structures. But: the 2nd rx_req
177  *   will be updated after the FEC1 CI is received at start_Rx_FEC2(), and when the FEC1 ends.
178  *   Unlike a Tx, a reception can take several code paths depending on the possibility of sync'ing,
179  *   having an erroneous FEC1 or FEC2 packet. See docs/Rx_Phy_paths.svg for more info.
180  *
181  *   The main state machine has a few conditions to transition differently for Coded Phy and normal
182  *   packets.
183  */
184 
185 #include <string.h>
186 #include <stdbool.h>
187 #include <stdint.h>
188 #include "bs_types.h"
189 #include "bs_tracing.h"
190 #include "bs_utils.h"
191 #include "bs_pc_2G4.h"
192 #include "bs_pc_2G4_utils.h"
193 #include "bs_rand.h"
194 #include "NHW_common_types.h"
195 #include "NHW_config.h"
196 #include "NHW_peri_types.h"
197 #include "NHW_RADIO.h"
198 #include "NHW_RADIO_signals.h"
199 #include "NHW_RADIO_utils.h"
200 #include "NHW_RADIO_timings.h"
201 #include "NHW_RADIO_bitcounter.h"
202 #include "NHW_RADIO_priv.h"
203 #include "nsi_hw_scheduler.h"
204 #include "NHW_AES_CCM.h"
205 #include "irq_ctrl.h"
206 #include "NRF_HWLowL.h"
207 #include "crc.h"
208 #include "nsi_tasks.h"
209 #include "nsi_hws_models_if.h"
210 #include "weak_stubs.h"
211 
212 #if NHW_RADIO_TOTAL_INST > 1
213 #error "This model only supports 1 instance so far"
214 #endif
215 
216 NRF_RADIO_Type NRF_RADIO_regs;
217 
218 static bs_time_t Timer_RADIO = TIME_NEVER; //main radio timer
219 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)
220 
221 static TIFS_state_t TIFS_state = TIFS_DISABLE;
222 static bool TIFS_ToTxNotRx = false; //Are we in a TIFS automatically starting a Tx from a Rx (true), or Rx from Tx (false)
223 static bs_time_t Timer_TIFS = TIME_NEVER;
224 static bool from_hw_tifs = false; /* Unfortunate hack due to the SW racing the HW to clear SHORTS*/
225 
226 static RADIO_Rx_status_t rx_status;
227 static RADIO_Tx_status_t tx_status;
228 static RADIO_CCA_status_t cca_status;
229 
230 static double bits_per_us; //Bits per us for the ongoing Tx or Rx
231 
232 static bs_time_t next_recheck_time; // when we asked the phy to recheck (in our own time) next time
233 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)
234 static int aborting_set = 0; //If set, we will abort the current Tx/Rx/CCA at the next abort reevaluation
235 
236 static nrfra_state_t radio_state;
237 static nrfra_sub_state_t radio_sub_state;
238 
239 static uint8_t tx_buf[_NRF_MAX_PACKET_SIZE]; //starting from the header, and including CRC
240 static uint8_t rx_buf[_NRF_MAX_PACKET_SIZE]; // "
241 static uint8_t *rx_pkt_buffer_ptr = (uint8_t*)&rx_buf;
242 
243 #if !NHW_RADIO_IS_54
244 static bool radio_POWER = false;
245 #endif
246 
247 static double cheat_rx_power_offset;
248 
249 static void start_Tx(void);
250 static void start_Tx_FEC2(void);
251 static void start_Rx(void);
252 static void start_Rx_FEC2(void);
253 static void start_CCA_ED(bool CCA_not_ED);
254 static void Rx_Addr_received(void);
255 static void Tx_abort_eval_respond(void);
256 static void Rx_abort_eval_respond(void);
257 #if 0 //Note6.b
258 static p2G4_rssi_power_t Rx_abort_imm_RSSI(void);
259 #endif
260 static void CCA_abort_eval_respond(void);
261 static void nhw_radio_device_address_match(uint8_t rx_buf[]);
262 
radio_set_registers_defaults(void)263 static void radio_set_registers_defaults(void) {
264   //Registers' reset values:
265   NRF_RADIO_regs.FREQUENCY = 0x00000002;
266   NRF_RADIO_regs.SFD = 0xA7;
267   NRF_RADIO_regs.CCACTRL = 0x052D0000;
268   NRF_RADIO_regs.CTEINLINECONF = 0x00002800;
269   NRF_RADIO_regs.DFECTRL1 = 0x00023282;
270   for (int i = 0; i < 8; i++)
271     NRF_RADIO_regs.PSEL.DFEGPIO[i] = 0xFFFFFFFF;
272   NRF_RADIO_regs.DFEPACKET.MAXCNT = 0x00001000;
273 
274 #if defined(NRF54L15) && !defined(RADIO_DATAWHITEIV_DATAWHITEIV_Msk)
275   NRF_RADIO_regs.DATAWHITE = 0x00890040;
276 #else
277   NRF_RADIO_regs.DATAWHITEIV = 0x00000040;
278 #endif
279 
280 #if !NHW_RADIO_IS_54
281   NRF_RADIO_regs.MODECNF0 = 0x00000200;
282   NRF_RADIO_regs.POWER = 1;
283 #else
284   NRF_RADIO_regs.EDCTRL = 0x20000000;
285   NRF_RADIO_regs.TXPOWER = 0x00000013;
286 #endif
287 }
288 
radio_reset(void)289 static void radio_reset(void) {
290   memset(&NRF_RADIO_regs, 0, sizeof(NRF_RADIO_regs));
291   radio_state = RAD_DISABLED;
292   radio_sub_state = SUB_STATE_INVALID;
293   Timer_RADIO = TIME_NEVER;
294 
295   TIFS_state = TIFS_DISABLE;
296   TIFS_ToTxNotRx = false;
297   Timer_TIFS = TIME_NEVER;
298 
299   radio_set_registers_defaults();
300 
301   nhwra_signalif_reset();
302 }
303 
nhw_radio_init(void)304 static void nhw_radio_init(void) {
305   nrfra_timings_init();
306   radio_reset();
307 #if !NHW_RADIO_IS_54
308   radio_POWER = false;
309 #endif
310   bits_per_us = 1;
311 }
312 
313 NSI_TASK(nhw_radio_init, HW_INIT, 100);
314 
nhw_radio_get_bpus(void)315 double nhw_radio_get_bpus(void) {
316   return bits_per_us;
317 }
318 
nhwra_set_Timer_RADIO(bs_time_t t)319 static inline void nhwra_set_Timer_RADIO(bs_time_t t){
320   Timer_RADIO = t;
321   nsi_hws_find_next_event();
322 }
323 
nhwra_set_Timer_abort_reeval(bs_time_t t)324 static inline void nhwra_set_Timer_abort_reeval(bs_time_t t){
325   Timer_RADIO_abort_reeval = t;
326   nsi_hws_find_next_event();
327 }
328 
nhw_RADIO_TASK_TXEN(void)329 void nhw_RADIO_TASK_TXEN(void) {
330   if ( ( radio_state != RAD_DISABLED )
331       && ( radio_state != RAD_TXIDLE )
332       && ( radio_state != RAD_RXIDLE ) ){
333     bs_trace_warning_line_time(
334         "NRF_RADIO: TXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
335         radio_state);
336     return;
337   }
338   radio_state = RAD_TXRU;
339   NRF_RADIO_regs.STATE = RAD_TXRU;
340 
341   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(1, from_hw_tifs));
342 }
343 
nhw_RADIO_TASK_RXEN(void)344 void nhw_RADIO_TASK_RXEN(void) {
345   if ( ( radio_state != RAD_DISABLED )
346       && ( radio_state != RAD_TXIDLE )
347       && ( radio_state != RAD_RXIDLE ) ){
348     bs_trace_warning_line_time(
349         "NRF_RADIO: RXEN received when the radio was not DISABLED or TX/RXIDLE but in state %i. It will be ignored. Expect problems\n",
350         radio_state);
351     return;
352   }
353   TIFS_state = TIFS_DISABLE;
354   radio_state = RAD_RXRU;
355   NRF_RADIO_regs.STATE = RAD_RXRU;
356   nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_rampup_time(0, from_hw_tifs));
357 }
358 
abort_if_needed(void)359 static void abort_if_needed(void) {
360   if ( ( abort_fsm_state == Tx_Abort_reeval )
361       || ( abort_fsm_state == Rx_Abort_reeval )
362       || ( abort_fsm_state == CCA_Abort_reeval ) ){
363     //If the phy is waiting for a response from us, we need to tell it, that we are aborting whatever it was doing
364     aborting_set = 1;
365   }
366   /* Note: In Rx, we may be
367    *   waiting to respond to the Phy to an abort reevaluation request abort_fsm_state == Rx_Abort_reeval
368    *   or waiting to reach the address end time to respond to the Phy if we accepted the packet or not
369    * but not both
370    */
371   if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ){
372     //we answer immediately to the phy rejecting the packet
373     p2G4_dev_rxv2_cont_after_addr_nc_b(false, NULL);
374     radio_sub_state = SUB_STATE_INVALID;
375   }
376 }
377 
nhw_RADIO_TASK_START(void)378 void nhw_RADIO_TASK_START(void) {
379   if ( radio_state == RAD_TXIDLE ) {
380     bs_time_t Tx_start_time = nsi_hws_get_time() + nhwra_timings_get_TX_chain_delay();
381     radio_state = RAD_TXSTARTING;
382     NRF_RADIO_regs.STATE = RAD_TX;
383     nhwra_set_Timer_RADIO(Tx_start_time);
384   } else if ( radio_state == RAD_RXIDLE ) {
385     start_Rx();
386   } else {
387     bs_trace_warning_line_time(
388         "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",
389         radio_state);
390   }
391 }
392 
nhw_RADIO_TASK_SOFTRESET(void)393 void nhw_RADIO_TASK_SOFTRESET(void) {
394   if (radio_state != RAD_DISABLED) {
395     bs_trace_warning_line_time(
396         "NRF_RADIO: TASK_SOFTRESET should only be used in disabled state."
397         "Current state %i\n", radio_state);
398   }
399 
400 #if defined(RADIO_TASKS_SOFTRESET_TASKS_SOFTRESET_Msk)
401   //Reset all registers after interrupts (first is MODE), but not the PACKETPTR.
402   //Note: DFE PACKETPTR should also not be reset(?), but is not yet implemented
403   memset((void *)&NRF_RADIO_regs.MODE, 0, offsetof(NRF_RADIO_Type, PACKETPTR) - offsetof(NRF_RADIO_Type, MODE));
404   memset((void *)&NRF_RADIO_regs.CSTONES, 0, sizeof(NRF_RADIO_Type) - offsetof(NRF_RADIO_Type, CSTONES));
405   radio_set_registers_defaults();
406 #endif
407 }
408 
nhw_RADIO_TASK_CCASTART(void)409 void nhw_RADIO_TASK_CCASTART(void) {
410   if ((radio_state != RAD_RXIDLE)){
411     bs_trace_warning_line_time(
412         "NRF_RADIO: CCASTART received when the radio was not RXIDLE but in state %i. "
413         "It will be ignored. Expect problems\n",
414         radio_state);
415     return;
416   }
417   start_CCA_ED(1);
418 }
419 
nhw_RADIO_TASK_CCASTOP(void)420 void nhw_RADIO_TASK_CCASTOP(void) {
421   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED )) {
422     abort_if_needed();
423     radio_state = RAD_RXIDLE;
424     NRF_RADIO_regs.STATE = RAD_RXIDLE;
425     nhwra_set_Timer_RADIO(TIME_NEVER);
426     nhw_RADIO_signal_EVENTS_CCASTOPPED(0);
427   } else {
428     bs_trace_info_line_time(3,
429         "NRF_RADIO: TASK_CCASTOP received while the radio was not on a CCA procedure (was %i, %i). "
430         "It will be ignored\n",
431         radio_state, cca_status.CCA_notED);
432   }
433 }
434 
nhw_RADIO_TASK_EDSTART(void)435 void nhw_RADIO_TASK_EDSTART(void) {
436   if ((radio_state != RAD_RXIDLE)){
437     bs_trace_warning_line_time(
438         "NRF_RADIO: EDSTART received when the radio was not RXIDLE but in state %i. "
439         "It will be ignored. Expect problems\n",
440         radio_state);
441     return;
442   }
443   start_CCA_ED(0);
444 }
445 
nhw_RADIO_TASK_EDSTOP(void)446 void nhw_RADIO_TASK_EDSTOP(void) {
447   if (( radio_state == RAD_CCA_ED ) && ( cca_status.CCA_notED == 0)) {
448     abort_if_needed();
449     radio_state = RAD_RXIDLE;
450     NRF_RADIO_regs.STATE = RAD_RXIDLE;
451     nhwra_set_Timer_RADIO(TIME_NEVER);
452     nhw_RADIO_signal_EVENTS_EDSTOPPED(0);
453   } else {
454     bs_trace_info_line_time(3,
455         "NRF_RADIO: TASK_EDSTOP received while the radio was not on a ED procedure (was %i, %i). "
456         "It will be ignored\n",
457         radio_state, cca_status.CCA_notED);
458   }
459 }
460 
nhw_RADIO_TASK_STOP(void)461 void nhw_RADIO_TASK_STOP(void) {
462   nhw_radio_stop_bit_counter();
463 
464   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
465     if (radio_state == RAD_TX) {
466       abort_if_needed();
467     }
468     radio_state = RAD_TXIDLE;
469     NRF_RADIO_regs.STATE = RAD_TXIDLE;
470     nhwra_set_Timer_RADIO(TIME_NEVER);
471   } else if ( radio_state == RAD_RX ){
472     abort_if_needed();
473     radio_state = RAD_RXIDLE;
474     NRF_RADIO_regs.STATE = RAD_RXIDLE;
475     nhwra_set_Timer_RADIO(TIME_NEVER);
476   } else if ( radio_state == RAD_CCA_ED ){
477     //The documentation is not clear about what happens if we get a STOP during a CCA or ED procedure,
478     //but it seems for CCA it can cause a bit of a mess depending on CCA mode.
479     //the behavior here is that we stop just like if it was an active Rx, and do *not* trigger a CCASTOPPED or EDSTOPPED event
480     bs_trace_warning_line_time(
481         "NRF_RADIO: TASK_STOP received while the radio was performing a CCA or ED procedure. "
482         "In this models we stop the procedure, but this can cause a mess in real HW\n");
483     abort_if_needed();
484     radio_state = RAD_RXIDLE;
485     NRF_RADIO_regs.STATE = RAD_RXIDLE;
486     nhwra_set_Timer_RADIO(TIME_NEVER);
487   } else {
488     bs_trace_warning_line_time(
489         "NRF_RADIO: TASK_STOP received while the radio was not on either TX or RX but in state %i. "
490         "It will be ignored\n",
491         radio_state);
492   }
493 }
494 
nhw_RADIO_TASK_DISABLE(void)495 void nhw_RADIO_TASK_DISABLE(void) {
496   nhw_radio_stop_bit_counter();
497 
498   if ((radio_state == RAD_TX) || (radio_state == RAD_TXSTARTING)) {
499     if (radio_state == RAD_TX) {
500       abort_if_needed();
501     }
502     radio_state = RAD_TXIDLE; //Momentary (will be changed in the if below)
503     NRF_RADIO_regs.STATE = RAD_TXIDLE;
504   } else if ( radio_state == RAD_RX ){
505     abort_if_needed();
506     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
507     NRF_RADIO_regs.STATE = RAD_RXIDLE;
508   } else if ( radio_state == RAD_CCA_ED ){
509     //The documentation is not clear about what happens if we get a disable during a CCA  or ED procedure,
510     //the assumption here is that we stop just like if it was an active Rx, but do not trigger a CCASTOPPED or EDSTOPPED event
511     abort_if_needed();
512     radio_state = RAD_RXIDLE; //Momentary (will be changed in the if below)
513     NRF_RADIO_regs.STATE = RAD_RXIDLE;
514   }
515 
516   if (TIFS_state != TIFS_DISABLE) {
517     TIFS_state = TIFS_DISABLE;
518     nhwra_set_Timer_RADIO(TIME_NEVER);
519     Timer_TIFS = TIME_NEVER;
520   }
521 
522   if ( ( radio_state == RAD_TXRU ) || ( radio_state == RAD_TXIDLE ) ) {
523     radio_state = RAD_TXDISABLE;
524     NRF_RADIO_regs.STATE = RAD_TXDISABLE;
525     TIFS_state = TIFS_DISABLE;
526     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_TX_rampdown_time());
527   } else if ( ( radio_state == RAD_RXRU ) || ( radio_state == RAD_RXIDLE ) ) {
528     radio_state = RAD_RXDISABLE;
529     NRF_RADIO_regs.STATE = RAD_RXDISABLE;
530     TIFS_state = TIFS_DISABLE;
531     nhwra_set_Timer_RADIO(nsi_hws_get_time() + nhwra_timings_get_RX_rampdown_time());
532   } else if ( radio_state == RAD_DISABLED ) {
533     //It seems the radio will also signal a DISABLED event even if it was already disabled
534     nhw_radio_stop_bit_counter();
535     nhw_RADIO_signal_EVENTS_DISABLED(0);
536   }
537 }
538 
nhw_RADIO_TASK_RSSISTART(void)539 void nhw_RADIO_TASK_RSSISTART(void) {
540   if ((radio_state != RAD_RX) && (radio_state != RAD_RXIDLE)) {
541     bs_trace_warning_time_line("TASK_RSSISTART received while the radio was not in Rx. "
542                                "The spec does not allow this. We just use the last Rx value in the model\n");
543   }
544 
545   // When we receive this, wrt. the Phy communication either:
546   // a) we have not started anything (radio_state != RAD_RX) (phy time is in the past in the previous transaction end)
547   // b) Phy is waiting during an abort reevaluation before address match
548   // c) we have got the addr and the phy is waiting for a response  to it (normal case, where the stack triggers RSSISTART via ADDRESS short)
549   // d) the phy is waiting for us, in the middle or an abort reevaluation after addr.
550   //   d.1) because stack triggered RSSISTART in SW after ADDRESS (time == phy time == address end time)
551   //   d.2) because stack triggered RSSISTART in SW anywhere later in packet reception (time == phy time)
552   // e) or we have finished a Phy Rx all together but the RADIO is still in Rx (we already got the CRC end from the Phy but we are still processing the packet in the RADIO, due to Rx prop. delay)
553   //
554   // Best approach:
555   // if a)
556   //      if real RADIO reports an old measured value then best is to use our latest
557   //      otherwise to do a plain RSSI measurement now
558   //        Note: It seems it is possible to do a RSSI measurement after ramp-up with the RADIO in RXIDLE. So we'd want to do a normal RSSI measurement in this case. But then we'd need to distinguish it from c) and e)
559   // if b) => do an immediate RSSI measurement during abort reeval (this case we don't yet handle well now)
560   // if c) best is to use the RSSI measurement from the addr => last Rx measurement
561   // if d.1) => same as c)
562   // if d.2) best to do is an immediate RSSI measurement during abort, but last rx measurement is going to be almost the same
563   //   For d.1) an immediate RSSI measurement would produce the same result (just a bit of Phy comm overhead)
564   // if e) => use last Rx measurement
565 
566   p2G4_rssi_power_t RSSI_value;
567 #if 0 //Note6.b
568   if (abort_fsm_state == Rx_Abort_reeval) {
569     //Respond with an immediate RSSI request during abort, get that response and let nhw_radio_timer_abort_reeval_triggered() handle it further
570     RSSI_value = Rx_abort_imm_RSSI();
571   } else
572 #endif
573   {
574     RSSI_value = rx_status.rx_resp.rssi.RSSI;
575   }
576 
577   /* See Note6 */
578   NRF_RADIO_regs.RSSISAMPLE = nhwra_RSSI_value_to_modem_format(
579                                 p2G4_RSSI_value_to_dBm(RSSI_value)
580                                 + cheat_rx_power_offset
581                               );
582 #if !NHW_RADIO_IS_54
583   nhw_RADIO_signal_EVENTS_RSSIEND(0);
584 #endif
585 }
586 
nhw_RADIO_TASK_RSSISTOP(void)587 void nhw_RADIO_TASK_RSSISTOP(void) {
588   /* Nothing to be done */
589 }
590 
591 #if !NHW_RADIO_IS_54
nhw_RADIO_regw_sideeffects_POWER(void)592 void nhw_RADIO_regw_sideeffects_POWER(void) {
593   if ( NRF_RADIO_regs.POWER == 0 ){
594     radio_POWER = false;
595   } else {
596     if ( radio_POWER == false ){
597       radio_POWER = true;
598       abort_if_needed();
599       radio_reset();
600       nsi_hws_find_next_event();
601     }
602   }
603 }
604 #endif
605 
606 /**
607  * This is a fake task meant to start a HW timer for the TX->RX or RX->TX TIFS
608  */
nhw_RADIO_fake_task_TRXEN_TIFS(void)609 void nhw_RADIO_fake_task_TRXEN_TIFS(void) {
610   if ( TIFS_state == TIFS_WAITING_FOR_DISABLE ) {
611     TIFS_state = TIFS_TRIGGERING_TRX_EN;
612     nhwra_set_Timer_RADIO(Timer_TIFS);
613     if ( Timer_RADIO < nsi_hws_get_time() ){
614       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");
615     }
616   }
617 }
618 
619 /**
620  * If the HW automatic TIFS switch is enabled, prepare the internals for an automatic switch
621  * (when a fake_task_TRXEN_TIFS is automatically triggered after a disable due to a shortcut)
622  * otherwise do nothing
623  *
624  * Input Tx_Not_Rx: Are we finishing a Tx (true) or an Rx (false)
625  */
maybe_prepare_TIFS(bool Tx_Not_Rx)626 void maybe_prepare_TIFS(bool Tx_Not_Rx){
627   bs_time_t delta;
628   if ( !nhwra_is_HW_TIFS_enabled() ) {
629     TIFS_state = TIFS_DISABLE;
630     return;
631   }
632   if ( NRF_RADIO_regs.SHORTS & RADIO_SHORTS_DISABLED_TXEN_Msk ){
633     TIFS_ToTxNotRx = true;
634   } else {
635     TIFS_ToTxNotRx = false;
636   }
637 
638   if ( Tx_Not_Rx ){ //End of Tx
639     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*/
640   } else { //End of Rx
641     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;
642   }
643   Timer_TIFS = nsi_hws_get_time() + delta;
644   TIFS_state = TIFS_WAITING_FOR_DISABLE; /* In Timer_TIFS we will trigger a TxEN or RxEN */
645 }
646 
maybe_signal_event_RATEBOOST(void)647 static void maybe_signal_event_RATEBOOST(void) {
648   if (rx_status.CI == 1) {
649     nhw_RADIO_signal_EVENTS_RATEBOOST(0);
650   }
651 }
652 
653 /**
654  * The main radio timer (Timer_RADIO) has just triggered,
655  * continue whatever activity we are on
656  * (typically do something at the end/start of a state, set the new state
657  * and schedule further the next state change)
658  */
nhw_radio_timer_triggered(void)659 static void nhw_radio_timer_triggered(void) {
660   if ( radio_state == RAD_TXRU ){
661     radio_state = RAD_TXIDLE;
662     NRF_RADIO_regs.STATE = RAD_TXIDLE;
663     nhwra_set_Timer_RADIO(TIME_NEVER);
664     nhw_RADIO_signal_EVENTS_READY(0);
665     nhw_RADIO_signal_EVENTS_TXREADY(0);
666   } else if ( radio_state == RAD_RXRU ){
667     radio_state = RAD_RXIDLE;
668     NRF_RADIO_regs.STATE = RAD_RXIDLE;
669     nhwra_set_Timer_RADIO(TIME_NEVER);
670     nhw_RADIO_signal_EVENTS_READY(0);
671     nhw_RADIO_signal_EVENTS_RXREADY(0);
672   } else if ( radio_state == RAD_TXSTARTING ){
673     nhwra_set_Timer_RADIO(TIME_NEVER);
674     start_Tx();
675   } else if ( radio_state == RAD_TX ){
676     if ( radio_sub_state == TX_WAIT_FOR_ADDRESS_END ){
677       if (tx_status.codedphy) {
678         radio_sub_state = TX_WAIT_FOR_FEC1_END;
679         nhwra_set_Timer_RADIO(tx_status.FEC2_start_time);
680       } else {
681         radio_sub_state = TX_WAIT_FOR_PAYLOAD_END;
682         nhwra_set_Timer_RADIO(tx_status.PAYLOAD_end_time);
683       }
684       nhw_RADIO_signal_EVENTS_ADDRESS(0);
685       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
686     } else if ( radio_sub_state == TX_WAIT_FOR_FEC1_END ) {
687       start_Tx_FEC2();
688       radio_sub_state = TX_WAIT_FOR_PAYLOAD_END;
689       nhwra_set_Timer_RADIO(tx_status.PAYLOAD_end_time);
690     } else if ( radio_sub_state == TX_WAIT_FOR_PAYLOAD_END ) {
691       radio_sub_state = TX_WAIT_FOR_CRC_END;
692       nhwra_set_Timer_RADIO(tx_status.CRC_end_time);
693       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
694     } else if ( radio_sub_state == TX_WAIT_FOR_CRC_END ) {
695       radio_sub_state = SUB_STATE_INVALID;
696       radio_state = RAD_TXIDLE;
697       NRF_RADIO_regs.STATE = RAD_TXIDLE;
698       nhwra_set_Timer_RADIO(TIME_NEVER);
699       nhw_radio_stop_bit_counter();
700       nhw_RADIO_signal_EVENTS_END(0);
701       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
702       maybe_prepare_TIFS(true);
703     }  else { //SUB_STATE_INVALID
704       bs_trace_error_time_line("programming error\n");
705     }
706   } else if ( radio_state == RAD_RX ){
707     if ( radio_sub_state == RX_WAIT_FOR_ADDRESS_END ) {
708       nhw_RADIO_signal_EVENTS_SYNC(0); //See note on EVENTS_SYNC
709       nhw_RADIO_signal_EVENTS_ADDRESS(0);
710       nhw_RADIO_signal_EVENTS_FRAMESTART(0); //See note on FRAMESTART
711       nhwra_set_Timer_RADIO(TIME_NEVER); //Provisionally clear the RADIO timer for the Rx cont.
712       Rx_Addr_received();
713       if (rx_status.codedphy) {
714         radio_sub_state = RX_WAIT_FOR_FEC1_END;
715         // The timer will be set once we get the Phy FEC1 end response
716       } else {
717         radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
718         nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
719       }
720     } else if ( radio_sub_state == RX_WAIT_FOR_FEC1_END ) {
721       maybe_signal_event_RATEBOOST();
722       /* The next state transition will be programmed when we get the Phy response for the FEC2 */
723       nhwra_set_Timer_RADIO(TIME_NEVER);
724       start_Rx_FEC2();
725     } else if ( radio_sub_state == RX_WAIT_FOR_PAYLOAD_END ) {
726       radio_sub_state = RX_WAIT_FOR_CRC_END;
727       nhwra_set_Timer_RADIO(rx_status.CRC_End_Time);
728       nhw_RADIO_signal_EVENTS_PAYLOAD(0);
729     } else if ( radio_sub_state == RX_WAIT_FOR_CRC_END ) {
730 #if !NHW_RADIO_IS_54
731       //The 54 CCM is triggered through the DPPI like in reality
732       nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
733 #endif
734       radio_sub_state = SUB_STATE_INVALID;
735       radio_state = RAD_RXIDLE;
736       NRF_RADIO_regs.STATE = RAD_RXIDLE;
737       nhwra_set_Timer_RADIO(TIME_NEVER);
738       if ( rx_status.CRC_OK ) {
739         nhw_RADIO_signal_EVENTS_CRCOK(0);
740       } else {
741         nhw_RADIO_signal_EVENTS_CRCERROR(0);
742       }
743       nhw_radio_stop_bit_counter();
744       nhw_RADIO_signal_EVENTS_PHYEND(0); //See note on EVENTS_PHYEND
745       nhw_RADIO_signal_EVENTS_END(0);
746       maybe_prepare_TIFS(false);
747     } else { //SUB_STATE_INVALID
748       bs_trace_error_time_line("programming error\n");
749     }
750   } else if ( radio_state == RAD_CCA_ED ){
751     radio_state = RAD_RXIDLE;
752     NRF_RADIO_regs.STATE = RAD_RXIDLE;
753     nhwra_set_Timer_RADIO(TIME_NEVER);
754     if (cca_status.CCA_notED) { //CCA procedure ended
755       if (cca_status.is_busy) {
756         nhw_RADIO_signal_EVENTS_CCABUSY(0);
757       } else {
758         nhw_RADIO_signal_EVENTS_CCAIDLE(0);
759       }
760     } else { //ED procedure ended
761       nhw_RADIO_signal_EVENTS_EDEND(0);
762     }
763   } else if ( radio_state == RAD_TXDISABLE ){
764     radio_state = RAD_DISABLED;
765     NRF_RADIO_regs.STATE = RAD_DISABLED;
766     nhwra_set_Timer_RADIO(TIME_NEVER);
767     nhw_radio_stop_bit_counter();
768     nhw_RADIO_signal_EVENTS_DISABLED(0);
769   } else if ( radio_state == RAD_RXDISABLE ){
770     radio_state = RAD_DISABLED;
771     NRF_RADIO_regs.STATE = RAD_DISABLED;
772     nhwra_set_Timer_RADIO(TIME_NEVER);
773     nhw_radio_stop_bit_counter();
774     nhw_RADIO_signal_EVENTS_DISABLED(0);
775   } else {
776     if ( ( radio_state == RAD_DISABLED ) && ( TIFS_state == TIFS_TRIGGERING_TRX_EN ) ) {
777       if ( Timer_RADIO != Timer_TIFS ){
778         bs_trace_warning_line_time("NRF_RADIO: TIFS Ups 3\n");
779       }
780       TIFS_state = TIFS_DISABLE;
781       nhwra_set_Timer_RADIO(TIME_NEVER);
782       from_hw_tifs = true;
783       if ( TIFS_ToTxNotRx ) {
784         nhw_RADIO_TASK_TXEN();
785       } else {
786         nhw_RADIO_TASK_RXEN();
787       }
788       from_hw_tifs = false;
789     } else {
790       bs_trace_error_line_time(
791           "NRF_RADIO: this should not have happened (radio_state =%i)\n",
792           radio_state);
793     }
794   }
795 }
796 
797 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*/);
798 
799 /**
800  * The abort reevaluation timer has just triggered,
801  * => we can now respond to the Phy with our abort decision
802  */
nhw_radio_timer_abort_reeval_triggered(void)803 static void nhw_radio_timer_abort_reeval_triggered(void) {
804   nhwra_set_Timer_abort_reeval(TIME_NEVER);
805 
806   if ( abort_fsm_state == Tx_Abort_reeval ){
807     abort_fsm_state = No_pending_abort_reeval;
808     Tx_abort_eval_respond();
809   } else if ( abort_fsm_state == Rx_Abort_reeval ) {
810     abort_fsm_state = No_pending_abort_reeval;
811     Rx_abort_eval_respond();
812   } else if ( abort_fsm_state == CCA_Abort_reeval ) {
813     abort_fsm_state = No_pending_abort_reeval;
814     CCA_abort_eval_respond();
815   } else {
816     bs_trace_error_line("The abort timer was left running.. somebody forgot to cleanup..\n");
817   }
818 }
819 
820 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) */);
821 
822 /**
823  * Handle all possible responses to a Tx request from the Phy
824  */
handle_Tx_response(int ret)825 static void handle_Tx_response(int ret){
826   if (ret == -1){
827     bs_trace_raw_manual_time(3, nsi_hws_get_time(),"The phy disconnected us during a Tx\n");
828     hwll_disconnect_phy_and_exit();
829   } else if (ret == P2G4_MSG_TX_END) {
830     bs_time_t end_time = hwll_dev_time_from_phy(tx_status.tx_resp.end_time);
831     phy_sync_ctrl_set_last_phy_sync_time(end_time);
832     //The main machine was already pre-programmed at the Tx Start, no need to do anything else now
833   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
834     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
835     abort_fsm_state = Tx_Abort_reeval;
836     nhwra_set_Timer_abort_reeval(next_recheck_time);
837   }
838 }
839 
840 /**
841  * Set the Phy abort structure to the next time we will want to either abort or have a recheck
842  * And store in next_recheck_time the next recheck time
843  */
update_abort_struct(p2G4_abort_t * abort,bs_time_t * next_recheck_time)844 static void update_abort_struct(p2G4_abort_t *abort, bs_time_t *next_recheck_time){
845   //We will want to recheck next time anything may decide to stop the radio, that can be SW or HW
846   //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
847   *next_recheck_time = nsi_hws_get_next_event_time();
848   abort->recheck_time = hwll_phy_time_from_dev(*next_recheck_time);
849 
850   //We either have decided already we want to abort so we do it right now
851   //or we have not decided yet
852   if ( aborting_set == 1 ) {
853     aborting_set = 0; //By returning nsi_hws_get_time(), we are aborting right now
854     abort->abort_time = hwll_phy_time_from_dev(nsi_hws_get_time());
855   } else {
856     abort->abort_time = TIME_NEVER;
857   }
858 }
859 
860 /**
861  * We have reached the time in which we wanted to reevaluate if we would abort or not
862  * so we answer to the Phy with our decision
863  */
Tx_abort_eval_respond(void)864 static void Tx_abort_eval_respond(void) {
865   //The abort must have been evaluated by now so we can respond to the waiting phy
866   p2G4_abort_t *abort = &tx_status.tx_req.abort;
867 
868   update_abort_struct(abort, &next_recheck_time);
869 
870   int ret = p2G4_dev_provide_new_tx_abort_nc_b(abort);
871 
872   handle_Tx_response(ret);
873 }
874 
875 /*
876  * Actually start the Tx in this microsecond (+ the Tx chain delay in the Phy)
877  * (For coded phy, starts the FEC1 Tx itself, and prepares the FEC2 to be started later by start_Tx_FEC2() )
878  */
start_Tx(void)879 static void start_Tx(void) {
880 
881   radio_state = RAD_TX;
882   NRF_RADIO_regs.STATE = RAD_TX;
883 
884   nhwra_check_packet_conf();
885 
886   //TOLOW: Add support for other packet formats and bitrates
887   uint8_t preamble_len = 0;
888   uint8_t address_len = 0;
889   uint8_t header_len = 0;
890   uint payload_len = 0;
891   uint8_t crc_len = nhwra_get_crc_length();
892   uint8_t CI = 0;
893   uint8_t main_packet_coding_rate = 0;
894 
895   tx_status.codedphy = false;
896 
897   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
898     preamble_len = 1; //1 byte
899     address_len = 4;
900     header_len  = 2;
901     bits_per_us = 1;
902   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
903     preamble_len = 2; //2 bytes
904     address_len = 4;
905     header_len  = 2;
906     bits_per_us = 2;
907   } else if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit)
908             || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR500Kbit)) {
909     tx_status.codedphy = true;
910     address_len = 4;
911     header_len  = 2;
912     if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit) {
913       bits_per_us = 0.125;
914       CI = 0; //0b00
915       main_packet_coding_rate = 8;
916     } else { /* RADIO_MODE_MODE_Ble_LR500Kbit */
917       bits_per_us = 0.5;
918       CI = 1; //0b01
919       main_packet_coding_rate = 2;
920     }
921   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
922     preamble_len = 4;
923     address_len = 1;
924     header_len  = 1;
925     bits_per_us = 0.25;
926   }
927 
928   payload_len = nhwra_tx_copy_payload(tx_buf);
929 
930   /* This code should be generalized to support any CRC configuration (CRCCNF, CRCINIT AND CRCPOLY)
931    * When doing so, we should still calculate the ble and 154 crc's with their optimized table implementations
932    * Here we just assume the CRC is configured as it should given the modulation */
933   uint32_t crc_init = NRF_RADIO_regs.CRCINIT & RADIO_CRCINIT_CRCINIT_Msk;
934   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
935     append_crc_ble(tx_buf, header_len + payload_len, crc_init);
936   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
937     //15.4 does not CRC the length (header) field
938     append_crc_154(&tx_buf[header_len], payload_len, crc_init);
939   }
940 
941   uint main_packet_size; //Main "packet" size (the payload sent thru the phy)
942   bs_time_t packet_duration = 0; //Main packet duration (from preamble to CRC except for codedPhy which is just the FEC2)
943 
944   if (!tx_status.codedphy) {
945     packet_duration = preamble_len*8 + address_len*8;
946   } else {
947     packet_duration = 3; //TERM2
948   }
949   packet_duration += header_len*8 + payload_len*8 + crc_len*8;
950   packet_duration /= bits_per_us;
951   main_packet_size = header_len + payload_len + crc_len;
952 
953   bs_time_t payload_start_time;
954   bs_time_t main_packet_start_time;
955 
956   if (tx_status.codedphy) {
957     tx_status.ADDRESS_end_time = nsi_hws_get_time() + (bs_time_t)(80 + 256 - nhwra_timings_get_TX_chain_delay());
958     payload_start_time = tx_status.ADDRESS_end_time + 16 + 24;/* CI = 16us; TERM1= 24us */
959 
960     bs_time_t fec1_duration = 80 + 256 + 16 + 24;
961 
962     nhwra_prep_tx_request(&tx_status.tx_req_fec1, 1, fec1_duration, hwll_phy_time_from_dev(nsi_hws_get_time()), 8);
963     update_abort_struct(&tx_status.tx_req_fec1.abort, &next_recheck_time);
964     main_packet_start_time = tx_status.tx_req_fec1.end_tx_time + 1;
965     tx_status.FEC2_start_time = main_packet_start_time; /* in air */
966   } else {
967     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();
968     payload_start_time = tx_status.ADDRESS_end_time;
969     main_packet_start_time = hwll_phy_time_from_dev(nsi_hws_get_time());
970   }
971   tx_status.PAYLOAD_end_time = payload_start_time + (bs_time_t)(8*(header_len + payload_len)/bits_per_us);
972   tx_status.CRC_end_time = tx_status.PAYLOAD_end_time + (bs_time_t)(crc_len*8/bits_per_us);
973 
974   nhwra_prep_tx_request(&tx_status.tx_req, main_packet_size, packet_duration,
975                         main_packet_start_time, main_packet_coding_rate);
976   update_abort_struct(&tx_status.tx_req.abort, &next_recheck_time);
977 
978   int ret;
979   if (tx_status.codedphy) {
980     //Request the FEC1 Tx from the Phy:
981     ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req_fec1, &CI, &tx_status.tx_resp);
982   } else { /* not codedphy */
983     //Request the Tx from the Phy:
984     ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req, tx_buf, &tx_status.tx_resp);
985   }
986   handle_Tx_response(ret);
987 
988   radio_sub_state = TX_WAIT_FOR_ADDRESS_END;
989   nhwra_set_Timer_RADIO(tx_status.ADDRESS_end_time);
990 }
991 
start_Tx_FEC2(void)992 static void start_Tx_FEC2(void) {
993   int ret;
994   update_abort_struct(&tx_status.tx_req.abort, &next_recheck_time);
995   tx_status.tx_req.phy_address = 0; /* An invalid address */
996   ret = p2G4_dev_req_txv2_nc_b(&tx_status.tx_req, tx_buf, &tx_status.tx_resp);
997   handle_Tx_response(ret);
998 }
999 
Rx_handle_CI_reception(void)1000 static void Rx_handle_CI_reception(void) {
1001   rx_status.CI = rx_buf[0] & 0x3;
1002 
1003   if ((rx_status.rx_resp.packet_size < 1) || (rx_status.CI > 1)) {
1004     bs_trace_warning_time_line("%s: Received supposed BLE CodedPhy FEC1 without CI or corrupted CI (%i, %i)\n",
1005         __func__, rx_status.rx_resp.packet_size, rx_status.CI);
1006   }
1007 
1008   NRF_RADIO_regs.PDUSTAT |= (rx_status.CI << RADIO_PDUSTAT_CISTAT_Pos) & RADIO_PDUSTAT_CISTAT_Msk;
1009 
1010   if (rx_status.rx_resp.status != P2G4_RXSTATUS_OK) {
1011     /* Error during CI decoding, we don't know how many bits, and if it would have recovered.
1012      * So, we just do a 50% drop of having each CI bit corrupted or not */
1013     int error;
1014     error = bs_random_Bern(RAND_PROB_1/2);
1015     NRF_RADIO_regs.PDUSTAT ^= error << (RADIO_PDUSTAT_CISTAT_Pos + 1); /* The don't-care bit in CI */
1016 
1017     error = bs_random_Bern(RAND_PROB_1/2);
1018     if (error) {
1019       NRF_RADIO_regs.PDUSTAT ^= 1 << (RADIO_PDUSTAT_CISTAT_Pos);
1020       rx_status.CI ^= 1;
1021       rx_status.CI_error = true;
1022     }
1023   }
1024 }
1025 
Rx_handle_end_response(bs_time_t end_time)1026 static void Rx_handle_end_response(bs_time_t end_time) {
1027 
1028   if (rx_status.inFEC1 == true) { /* End of CodedPhy packet FEC1 */
1029     Rx_handle_CI_reception();
1030 
1031   } else { //Normal packet or end of FEC2
1032     if (rx_status.rx_resp.status != P2G4_RXSTATUS_HEADER_ERROR) {
1033         rx_status.CRC_End_Time = end_time + nhwra_timings_get_Rx_chain_delay();
1034     } //Otherwise we do not really now how the Nordic RADIO behaves depending on
1035     //where the biterrors are and so forth. So let's always behave like if the
1036     //packet lenght was received correctly, and just report a CRC error at the
1037     //end of the CRC
1038 
1039     if ( rx_status.rx_resp.status == P2G4_RXSTATUS_OK ){
1040       NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
1041       rx_status.CRC_OK = 1;
1042       NRF_RADIO_regs.CRCSTATUS = 1;
1043     }
1044   }
1045 }
1046 
1047 
Rx_handle_address_end_response(bs_time_t address_time)1048 static void Rx_handle_address_end_response(bs_time_t address_time) {
1049 
1050   rx_status.ADDRESS_End_Time = address_time + nhwra_timings_get_Rx_chain_delay();
1051 
1052   if ((rx_status.codedphy == true) && (rx_status.inFEC1)) {
1053     rx_status.FEC2_start_time = address_time + 16 + 24 + 1; /* It will be updated on the FEC1 Rx end */
1054     rx_status.packet_rejected = false; //We always accept the FEC1 part
1055 
1056     //Let's set a very provisional packet end time, in case the Tx aborts between FEC1 and FEC2:
1057     rx_status.PAYLOAD_End_Time = rx_status.FEC2_start_time + 2*8/bits_per_us
1058                                 + nhwra_timings_get_Rx_chain_delay(); /* An empty packet */
1059     rx_status.CRC_End_Time = rx_status.PAYLOAD_End_Time + rx_status.CRC_duration;
1060     return;
1061   }
1062   //Otherwise, FEC2 or not Coded Phy
1063 
1064   uint length = nhwra_get_payload_length(rx_buf);
1065   uint max_length = nhwra_get_MAXLEN();
1066 
1067   if (length > max_length) {
1068     // We reject the packet right away, setting the CRC error, and timers as expected
1069     bs_trace_warning_time_line("NRF_RADIO: received a packet longer than the configured MAXLEN (%i>%i). Truncating it\n", length, max_length);
1070     length  = max_length;
1071     NRF_RADIO_regs.PDUSTAT |= RADIO_PDUSTAT_PDUSTAT_Msk;
1072     rx_status.packet_rejected = true;
1073   } else {
1074     rx_status.packet_rejected = false;
1075   }
1076   if (rx_status.CI_error) {
1077     /* Let's just stop the Phy reception here, as continuing does not give us anything anymore */
1078     rx_status.packet_rejected = true;
1079   }
1080 
1081   //TODO: Discard Ieee802154_250Kbit frames with length == 0
1082 
1083   bs_time_t payload_end = 0;
1084 
1085   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
1086     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((2+length)*8/bits_per_us);
1087   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1088     payload_end = rx_status.rx_resp.rx_time_stamp + (bs_time_t)((1+length)*8/bits_per_us);
1089   } //Eventually this should be generalized with the packet configuration
1090 
1091   rx_status.PAYLOAD_End_Time = nhwra_timings_get_Rx_chain_delay() +
1092                                hwll_dev_time_from_phy(payload_end);
1093 
1094   int TERM2_duration = 0; /* See Note21.b */
1095   if (rx_status.codedphy) {
1096     if (rx_status.CI == 1) {
1097       TERM2_duration = 6; //S=2 * 3bits
1098     } else {
1099       TERM2_duration = 24;//S=8 * 3bits
1100     }
1101   }
1102 
1103   rx_status.CRC_End_Time = rx_status.PAYLOAD_End_Time + rx_status.CRC_duration + TERM2_duration; //Provisional value (if we are accepting the packet)
1104 
1105   //Copy the whole packet (S0, lenght, S1 & payload) excluding the CRC.
1106   if (nhwra_is_ble_mode(NRF_RADIO_regs.MODE)) {
1107     if (rx_status.rx_resp.packet_size >= 5) { /*At least the header and CRC, otherwise better to not try to copy it*/
1108       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
1109       ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1] = rx_buf[1];
1110       /* We cheat a bit and copy the whole packet already (The AAR block will look in Adv packets after 64 bits)*/
1111       memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[2 + rx_status.S1Offset],
1112           &rx_buf[2] , length);
1113     }
1114   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1115     if (rx_status.rx_resp.packet_size >= 3) { /*At least the header and CRC, otherwise better to not try to copy it*/
1116             ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[0] = rx_buf[0];
1117             memcpy(&((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset],
1118                 &rx_buf[1] , length);
1119           }
1120   } //Eventually this should be generalized with the packet configuration
1121 
1122   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1123     //The real HW only copies the LQI value after the payload in this mode
1124     //Note that doing it this early is a cheat
1125     double RSSI = p2G4_RSSI_value_to_dBm(rx_status.rx_resp.rssi.RSSI) + cheat_rx_power_offset;
1126     uint8_t LQI = nhwra_dBm_to_modem_LQIformat(RSSI);
1127     //Eventually this should be generalized with the packet configuration:
1128     ((uint8_t*)NRF_RADIO_regs.PACKETPTR)[1 + rx_status.S1Offset + length] = LQI;
1129   }
1130 
1131 }
1132 
1133 /**
1134  * Handle all possible responses from the phy to a Rx request
1135  */
handle_Rx_response(int ret)1136 static void handle_Rx_response(int ret){
1137   if (ret == -1) {
1138     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"Communication with the phy closed during Rx\n");
1139     hwll_disconnect_phy_and_exit();
1140 
1141   } else if (ret == P2G4_MSG_ABORTREEVAL) {
1142 
1143     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
1144     abort_fsm_state = Rx_Abort_reeval;
1145     nhwra_set_Timer_abort_reeval( BS_MAX(next_recheck_time,nsi_hws_get_time()) );
1146 
1147   } else  if ((ret == P2G4_MSG_RXV2_ADDRESSFOUND) && (radio_state == RAD_RX /*if we havent aborted*/)) {
1148 
1149     bs_time_t addres_time = hwll_dev_time_from_phy(rx_status.rx_resp.rx_time_stamp); //this is the end of the sync word in air time
1150     phy_sync_ctrl_set_last_phy_sync_time(addres_time);
1151     Rx_handle_address_end_response(addres_time);
1152 
1153     if ((rx_status.codedphy == false) || (rx_status.inFEC1)) {
1154       radio_sub_state = RX_WAIT_FOR_ADDRESS_END;
1155       nhwra_set_Timer_RADIO(rx_status.ADDRESS_End_Time);
1156     } else { //FEC2
1157       radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
1158       nhwra_set_Timer_RADIO(TIME_NEVER);
1159       Rx_Addr_received();
1160       nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
1161     }
1162   } else if ((ret == P2G4_MSG_RXV2_END) && (radio_state == RAD_RX /*if we havent aborted*/)) {
1163 
1164     bs_time_t end_time = hwll_dev_time_from_phy(rx_status.rx_resp.end_time);
1165     phy_sync_ctrl_set_last_phy_sync_time(end_time);
1166 
1167     /* P2G4_RXSTATUS_NOSYNC during a simple packet or CodedPhy FEC1 cannot really happen
1168      * As that would mean we have run out of the "infinite" scan time.
1169      * It can happen though at the start of the CodedPhy FEC2, if the transmitter aborted
1170      * before starting the FEC2 */
1171     if (rx_status.rx_resp.status == P2G4_RXSTATUS_NOSYNC) {
1172       if ((rx_status.codedphy == false) || (rx_status.inFEC1 == true)) {
1173         bs_trace_error_time_line("Unexpected not-handled path\n");
1174       }
1175       /* Otherwise we just wait for the RX_WAIT_FOR_PAYLOAD_END handling in the main RADIO FSM
1176        * A provisional PAYLOAD_End_Time was set earlier */
1177       radio_sub_state = RX_WAIT_FOR_PAYLOAD_END;
1178       nhwra_set_Timer_RADIO(rx_status.PAYLOAD_End_Time);
1179       return;
1180     } else {
1181       Rx_handle_end_response(end_time);
1182 
1183       if (rx_status.inFEC1 == true) {
1184         /* To avoid issues with possible rounding errors in the phy<->dev timing conversion,
1185          * we ensure the FEC2 Rx will start in the next us in Phy time */
1186         rx_status.rx_req.start_time = rx_status.rx_resp.end_time + 1;
1187         nhwra_set_Timer_RADIO(rx_status.FEC2_start_time);
1188       }
1189     }
1190   }
1191 }
1192 
1193 #if 0 //Note6.b
1194 /*
1195  * Request an immediate RSSI measurement during an Rx abort reevaluation
1196  * with the same Rx parameters as the currently ongoing reception
1197  * (the abort reevaluation will be left pending)
1198  */
1199 static p2G4_rssi_power_t Rx_abort_imm_RSSI(void) {
1200   p2G4_rssi_t rssi_req;
1201   p2G4_rssi_done_t rssi_resp;
1202   p2G4_rxv2_t *rx_req = &rx_status.rx_req;
1203 
1204   //Right now, though as an immediate request this is actually ignored by the Phy:
1205   rssi_req.meas_time = hwll_phy_time_from_dev(nsi_hws_get_time());
1206   rssi_req.antenna_gain = rx_req->antenna_gain;
1207   memcpy(&rssi_req.radio_params, &rx_req->radio_params, sizeof(p2G4_radioparams_t));
1208 
1209   int ret = p2G4_dev_req_imm_RSSI_nc_b(&rssi_req, &rssi_resp);
1210 
1211   if (ret == -1) {
1212     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"Communication with the phy closed during Rx abort reeval\n");
1213     hwll_disconnect_phy_and_exit();
1214   }
1215 
1216   return rssi_resp.RSSI;
1217 }
1218 #endif
1219 
1220 /**
1221  * We have reached the time in which we wanted to reevaluate if we would abort or not
1222  * so we answer to the phy with our decision
1223  */
Rx_abort_eval_respond(void)1224 static void Rx_abort_eval_respond(void) {
1225   //The abort must have been evaluated by now so we can respond to the waiting phy
1226   p2G4_abort_t *abort = &rx_status.rx_req.abort;
1227   update_abort_struct(abort, &next_recheck_time);
1228 
1229   int ret = p2G4_dev_provide_new_rxv2_abort_nc_b(abort);
1230 
1231   handle_Rx_response(ret);
1232 }
1233 
1234 /*
1235  * Actually start the Rx in this microsecond
1236  */
start_Rx(void)1237 static void start_Rx(void) {
1238   #define RX_N_ADDR 8 /* How many addresses we can search in parallel */
1239   p2G4_address_t rx_addresses[RX_N_ADDR];
1240 
1241   nhwra_check_packet_conf();
1242 
1243   radio_state = RAD_RX;
1244   NRF_RADIO_regs.STATE = RAD_RX;
1245   NRF_RADIO_regs.CRCSTATUS = 0;
1246   NRF_RADIO_regs.PDUSTAT = 0;
1247 
1248   if ( NRF_RADIO_regs.PCNF0 & ( RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos ) ){
1249     rx_status.S1Offset = 1; /*1 byte offset in RAM (S1 length > 8 not supported)*/
1250   } else {
1251     rx_status.S1Offset = 0;
1252   }
1253 
1254   rx_status.codedphy = false;
1255   rx_status.inFEC1 = false;
1256   rx_status.CI_error = false;
1257   rx_status.CI = 0;
1258 
1259   if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_1Mbit) {
1260     bits_per_us = 1;
1261   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_2Mbit) {
1262     bits_per_us = 2;
1263   } else if ((NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR125Kbit)
1264       || (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ble_LR500Kbit)) {
1265     bits_per_us = 0.125; /* For FEC1 part */
1266     rx_status.codedphy = true;
1267     rx_status.inFEC1 = true;
1268   } else if (NRF_RADIO_regs.MODE == RADIO_MODE_MODE_Ieee802154_250Kbit) {
1269     bits_per_us = 0.25;
1270   }
1271   rx_status.CRC_duration = nhwra_get_crc_length()*8/bits_per_us;
1272   rx_status.CRC_OK = false;
1273   rx_status.rx_resp.status = P2G4_RXSTATUS_NOSYNC;
1274 
1275   if (rx_status.codedphy) {
1276     nhwra_prep_rx_request_FEC1(&rx_status.rx_req_fec1, rx_addresses);
1277     update_abort_struct(&rx_status.rx_req_fec1.abort, &next_recheck_time);
1278   }
1279   nhwra_prep_rx_request(&rx_status.rx_req, rx_addresses);
1280   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1281 
1282   //attempt to receive
1283   int ret;
1284   if (rx_status.codedphy) {
1285     ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req_fec1, rx_addresses,
1286                                  &rx_status.rx_resp, &rx_pkt_buffer_ptr,
1287                                  _NRF_MAX_PACKET_SIZE);
1288   } else {
1289     ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req, rx_addresses,
1290                                  &rx_status.rx_resp,&rx_pkt_buffer_ptr,
1291                                  _NRF_MAX_PACKET_SIZE);
1292   }
1293 
1294   radio_sub_state = SUB_STATE_INVALID;
1295   nhwra_set_Timer_RADIO(TIME_NEVER);
1296 
1297   handle_Rx_response(ret);
1298 }
1299 
1300 /*
1301  * Start the Rx for a CodedPhy FEC2 packet part in this microsecond
1302  */
start_Rx_FEC2(void)1303 static void start_Rx_FEC2(void) {
1304 
1305   rx_status.inFEC1 = false;
1306 
1307   if (rx_status.CI == 0) {
1308     rx_status.rx_req.coding_rate = 8;
1309     //error_calc_rate & header_duration preset in nhwra_prep_rx_request() are already correct
1310   } else { //0b01
1311     bits_per_us = 0.5;
1312     rx_status.rx_req.coding_rate = 2;
1313     rx_status.rx_req.error_calc_rate = 500000;
1314     rx_status.rx_req.header_duration = 2*8*2 /* 2 bytes at 500kbps */;
1315   }
1316   /* rx_req.start_time was set based on the FEC1 end */
1317   rx_status.rx_req.pream_and_addr_duration = 0;
1318   rx_status.rx_req.n_addr = 0;
1319   rx_status.rx_req.scan_duration = 1;
1320   rx_status.rx_req.prelocked_tx = true;
1321 
1322   rx_status.CRC_duration = nhwra_get_crc_length()*8/bits_per_us;
1323 
1324   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1325 
1326   int ret;
1327 
1328   ret = p2G4_dev_req_rxv2_nc_b(&rx_status.rx_req, NULL,
1329                                &rx_status.rx_resp, &rx_pkt_buffer_ptr,
1330                                _NRF_MAX_PACKET_SIZE);
1331 
1332   handle_Rx_response(ret);
1333 }
1334 
1335 /**
1336  * This function is called at the time when the Packet address* would have been
1337  * completely received for simple packets, AND at the beginning of the FEC2
1338  * for CodedPhy packets.
1339  * (* at the time of the end of the last bit of the packet address)
1340  * To continue processing the reception (the Phy was left waiting for a response)
1341  *
1342  * Note that libPhyCom has already copied the whole packet into the input buffer
1343  */
Rx_Addr_received(void)1344 static void Rx_Addr_received(void) {
1345 
1346   bool accept_packet = !rx_status.packet_rejected;
1347 
1348   if (rx_status.codedphy == false || rx_status.inFEC1 == false) {
1349     NRF_RADIO_regs.RXMATCH = 0; //The only we support so far
1350 
1351     if (NRF_RADIO_regs.DACNF & 0xFF) { /*If any of the addresses for device address match is enabled*/
1352       /*
1353        * NOTE: we cheat and we already check the advertisement addresses and
1354        * raise the event, even though we should wait for 16 + 48 bits more
1355        *
1356        * If this is a problem, add a new timer and Rx state and delay raising the event
1357        * until then
1358        */
1359       nhw_radio_device_address_match(rx_buf);
1360     }
1361   }
1362 
1363   update_abort_struct(&rx_status.rx_req.abort, &next_recheck_time);
1364   int ret = p2G4_dev_rxv2_cont_after_addr_nc_b(accept_packet, &rx_status.rx_req.abort);
1365 
1366   if ( accept_packet ){ /* Always true for CodedPhy FEC1 */
1367     handle_Rx_response(ret);
1368   } else {
1369     //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
1370 
1371     //We do what would correspond to Rx_handle_end_response() as it won't get called
1372     NRF_RADIO_regs.RXCRC = nhwra_get_rx_crc_value(rx_buf, rx_status.rx_resp.packet_size);
1373 #if !NHW_RADIO_IS_54
1374     //The 54 CCM is triggered through the DPPI like in reality
1375     nhw_ccm_radio_received_packet(!rx_status.CRC_OK);
1376 #endif
1377   }
1378 }
1379 
1380 /**
1381  * Check if the address in the received (advertisement) packet
1382  * matches one configured in the DAP/DAB registers as set by DACNF
1383  *
1384  * If it does, it sets appropriately the DAI register,
1385  * in any case, it generates the DEVMATCH and DEVMISS signals accordingly
1386  *
1387  * Note that, as specified in the infocenter documentation,
1388  * the address is assumed to be the first 48 bits after the 2 byte header
1389  * and the TxAddr bit to be 7th bit in 1st header byte as per the BT Core spec.
1390  */
nhw_radio_device_address_match(uint8_t rx_buf[])1391 static void nhw_radio_device_address_match(uint8_t rx_buf[]) {
1392   bool match_found = false;
1393   bool nomatch;
1394   int TxAdd;
1395 
1396   for (int i = 0 ; i < 8; i++) {
1397     if (((NRF_RADIO_regs.DACNF >> i) & 1) == 0 ) {
1398       continue;
1399     }
1400 
1401     TxAdd = (NRF_RADIO_regs.DACNF >> (i + 8)) & 1;
1402 
1403     if (TxAdd != ((rx_buf[0] >> 6) & 1) ) {
1404       continue;
1405     }
1406 
1407     nomatch = (*(uint32_t *)(rx_buf + 2) != NRF_RADIO_regs.DAB[i]);
1408     uint32_t DAP = NRF_RADIO_regs.DAP[i] & UINT16_MAX;
1409     nomatch |= (*(uint16_t *)(rx_buf + 6) != DAP);
1410 
1411     if (nomatch) {
1412       continue;
1413     }
1414 
1415     match_found = true;
1416     NRF_RADIO_regs.DAI = i;
1417     break;
1418   }
1419 
1420   if (match_found) {
1421     nhw_RADIO_signal_EVENTS_DEVMATCH(0);
1422   } else {
1423     nhw_RADIO_signal_EVENTS_DEVMISS(0);
1424   }
1425 }
1426 
CCA_handle_end_response(void)1427 static void CCA_handle_end_response(void) {
1428   //Depending on mode, set status and registers
1429   //raising CCAIDLE, CCABUSY or EDEND will happen in the correct time in the main machine
1430 
1431   if (cca_status.CCA_notED) { //End a CCA procedure
1432     uint CCAMode = (NRF_RADIO_regs.CCACTRL & RADIO_CCACTRL_CCAMODE_Msk) >> RADIO_CCACTRL_CCAMODE_Pos;
1433 
1434     if ((CCAMode == RADIO_CCACTRL_CCAMODE_EdMode)
1435         || (CCAMode == RADIO_CCACTRL_CCAMODE_EdModeTest1)) {
1436       cca_status.is_busy = cca_status.cca_resp.rssi_overthreshold;
1437     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierMode) {
1438       cca_status.is_busy = cca_status.cca_resp.mod_found;
1439     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierAndEdMode) {
1440       cca_status.is_busy = cca_status.cca_resp.mod_found
1441           && cca_status.cca_resp.rssi_overthreshold;
1442     } else if (CCAMode == RADIO_CCACTRL_CCAMODE_CarrierOrEdMode) {
1443       cca_status.is_busy = cca_status.cca_resp.mod_found
1444           || cca_status.cca_resp.rssi_overthreshold;
1445     } else {
1446       bs_trace_error_time_line("%s, CCAMODE=%i suppport not yet implemented\n",
1447           __func__, CCAMode);
1448     }
1449   } else { // Ending an ED procedure
1450     double RSSI = p2G4_RSSI_value_to_dBm(cca_status.cca_resp.RSSI_max) + cheat_rx_power_offset;
1451     NRF_RADIO_regs.EDSAMPLE = nhwra_dBm_to_modem_LQIformat(RSSI);
1452   }
1453 }
1454 
1455 /**
1456  * Handle all possible responses to a CCA request from the Phy
1457  */
handle_CCA_response(int ret)1458 static void handle_CCA_response(int ret){
1459   if (ret == -1){
1460     bs_trace_raw_manual_time(3,nsi_hws_get_time(),"The Phy disconnected us during a CCA procedure\n");
1461     hwll_disconnect_phy_and_exit();
1462   } else if ( ret == P2G4_MSG_CCA_END  ) {
1463     bs_time_t end_time = hwll_dev_time_from_phy(cca_status.cca_resp.end_time);
1464     phy_sync_ctrl_set_last_phy_sync_time( end_time );
1465     cca_status.CCA_end_time = end_time;
1466     if (radio_state == RAD_CCA_ED) { /*if we haven't aborted*/
1467       nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1468     }
1469     CCA_handle_end_response();
1470   } else if ( ret == P2G4_MSG_ABORTREEVAL ) {
1471     phy_sync_ctrl_set_last_phy_sync_time( next_recheck_time );
1472     abort_fsm_state = CCA_Abort_reeval;
1473     nhwra_set_Timer_abort_reeval(next_recheck_time);
1474   }
1475 }
1476 
1477 /**
1478  * We have reached the time in which we wanted to reevaluate if we would abort or not
1479  * so we answer to the Phy with our decision
1480  */
CCA_abort_eval_respond(void)1481 static void CCA_abort_eval_respond(void) {
1482   //The abort must have been evaluated by now so we can respond to the waiting phy
1483   p2G4_abort_t *abort = &cca_status.cca_req.abort;
1484 
1485   update_abort_struct(abort, &next_recheck_time);
1486 
1487   int ret = p2G4_dev_provide_new_cca_abort_nc_b(abort);
1488 
1489   handle_CCA_response(ret);
1490 }
1491 
1492 /**
1493  * Start CCA or ED procedure right now.
1494  * input: CCA_not_ED = 1 for CCA, 0 for ED
1495  */
start_CCA_ED(bool CCA_not_ED)1496 static void start_CCA_ED(bool CCA_not_ED){
1497 
1498   radio_state = RAD_CCA_ED;
1499 
1500   cca_status.CCA_notED = CCA_not_ED;
1501   cca_status.is_busy = false;
1502 
1503   nhwra_prep_cca_request(&cca_status.cca_req, CCA_not_ED, cheat_rx_power_offset);
1504 
1505   update_abort_struct(&cca_status.cca_req.abort, &next_recheck_time);
1506 
1507   //Expected end time; note that it may be shorter if detect over threshold is set
1508   cca_status.CCA_end_time = nsi_hws_get_time() + cca_status.cca_req.scan_duration;
1509   nhwra_set_Timer_RADIO(cca_status.CCA_end_time);
1510 
1511   //Request the CCA from the Phy:
1512   int ret = p2G4_dev_req_cca_nc_b(&cca_status.cca_req, &cca_status.cca_resp);
1513   handle_CCA_response(ret);
1514 }
1515 
hw_radio_testcheat_set_rx_power_gain(double power_offset)1516 void hw_radio_testcheat_set_rx_power_gain(double power_offset){
1517   cheat_rx_power_offset = power_offset;
1518 }
1519