1 | /** @file wlan_mac_time_util.c |
---|
2 | * @brief Time Utilities |
---|
3 | * |
---|
4 | * This contains code common to both CPU_LOW and CPU_HIGH that allows them |
---|
5 | * to interact with the MAC Time and User IO cores. |
---|
6 | * |
---|
7 | * @copyright Copyright 2013-2019, Mango Communications. All rights reserved. |
---|
8 | * Distributed under the Mango Communications Reference Design License |
---|
9 | * See LICENSE.txt included in the design archive or |
---|
10 | * at http://mangocomm.com/802.11/license |
---|
11 | * |
---|
12 | * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) |
---|
13 | */ |
---|
14 | |
---|
15 | /***************************** Include Files *********************************/ |
---|
16 | |
---|
17 | // Xilinx / Standard library includes |
---|
18 | #include <xparameters.h> |
---|
19 | #include <xil_io.h> |
---|
20 | |
---|
21 | // WLAN include files |
---|
22 | #include "wlan_mac_common.h" |
---|
23 | #include "wlan_mac_mailbox_util.h" |
---|
24 | #include "wlan_platform_common.h" |
---|
25 | |
---|
26 | #include "include/w3_mac_time_util.h" |
---|
27 | |
---|
28 | |
---|
29 | /*********************** Global Variable Definitions *************************/ |
---|
30 | |
---|
31 | |
---|
32 | /*************************** Variable Definitions ****************************/ |
---|
33 | |
---|
34 | |
---|
35 | /*************************** Functions Prototypes ****************************/ |
---|
36 | |
---|
37 | |
---|
38 | /******************************** Functions **********************************/ |
---|
39 | |
---|
40 | |
---|
41 | /*****************************************************************************/ |
---|
42 | /** |
---|
43 | * @brief Get MAC Time |
---|
44 | * |
---|
45 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
46 | * and can be updated (ie the MAC time). This function returns the value of the |
---|
47 | * counter at the time the function is called and is used throughout the framework |
---|
48 | * as a timestamp. The MAC time can be updated by the set_mac_time_usec() and |
---|
49 | * apply_mac_time_delta_usec() methods. |
---|
50 | * |
---|
51 | * @param None |
---|
52 | * @return u64 - Current number of microseconds of MAC time. |
---|
53 | */ |
---|
54 | u64 get_mac_time_usec() { |
---|
55 | //The MAC time core register interface is only 32-bit, so the 64-bit time |
---|
56 | // is read from two 32-bit registers and reconstructed here. |
---|
57 | |
---|
58 | u32 time_high_u32; |
---|
59 | u32 time_low_u32; |
---|
60 | u64 time_u64; |
---|
61 | |
---|
62 | time_high_u32 = Xil_In32(WLAN_MAC_TIME_REG_MAC_TIME_MSB); |
---|
63 | time_low_u32 = Xil_In32(WLAN_MAC_TIME_REG_MAC_TIME_LSB); |
---|
64 | |
---|
65 | // Catch very rare race when 32-LSB of 64-bit value wraps between the two 32-bit reads |
---|
66 | if((time_high_u32 & 0x1) != (Xil_In32(WLAN_MAC_TIME_REG_MAC_TIME_MSB) & 0x1)) { |
---|
67 | //32-LSB wrapped - start over |
---|
68 | time_high_u32 = Xil_In32(WLAN_MAC_TIME_REG_MAC_TIME_MSB); |
---|
69 | time_low_u32 = Xil_In32(WLAN_MAC_TIME_REG_MAC_TIME_LSB); |
---|
70 | } |
---|
71 | |
---|
72 | time_u64 = (((u64)time_high_u32) << 32) + ((u64)time_low_u32); |
---|
73 | |
---|
74 | return time_u64; |
---|
75 | } |
---|
76 | |
---|
77 | |
---|
78 | |
---|
79 | /*****************************************************************************/ |
---|
80 | /** |
---|
81 | * @brief Get System Timestamp (Microsecond Counter) |
---|
82 | * |
---|
83 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
84 | * and can not be updated (ie the system time). This function returns the value of |
---|
85 | * the counter at the time the function is called and is used throughout the framework |
---|
86 | * as a timestamp. The system time can not be updated and reflects the number of |
---|
87 | * microseconds that has past since the hardware booted. |
---|
88 | * |
---|
89 | * @param None |
---|
90 | * @return u64 - Current number of microseconds that have elapsed |
---|
91 | * since the hardware has booted. |
---|
92 | */ |
---|
93 | u64 get_system_time_usec() { |
---|
94 | // The MAC time core register interface is only 32-bit, so the 64-bit time |
---|
95 | // is read from two 32-bit registers and reconstructed here. |
---|
96 | |
---|
97 | u32 time_high_u32; |
---|
98 | u32 time_low_u32; |
---|
99 | u64 time_u64; |
---|
100 | |
---|
101 | time_high_u32 = Xil_In32(WLAN_MAC_TIME_REG_SYSTEM_TIME_MSB); |
---|
102 | time_low_u32 = Xil_In32(WLAN_MAC_TIME_REG_SYSTEM_TIME_LSB); |
---|
103 | |
---|
104 | // Catch very rare race when 32-LSB of 64-bit value wraps between the two 32-bit reads |
---|
105 | if((time_high_u32 & 0x1) != (Xil_In32(WLAN_MAC_TIME_REG_SYSTEM_TIME_MSB) & 0x1) ) { |
---|
106 | // 32-LSB wrapped - start over |
---|
107 | time_high_u32 = Xil_In32(WLAN_MAC_TIME_REG_SYSTEM_TIME_MSB); |
---|
108 | time_low_u32 = Xil_In32(WLAN_MAC_TIME_REG_SYSTEM_TIME_LSB); |
---|
109 | } |
---|
110 | |
---|
111 | time_u64 = (((u64)time_high_u32)<<32) + ((u64)time_low_u32); |
---|
112 | |
---|
113 | return time_u64; |
---|
114 | } |
---|
115 | |
---|
116 | |
---|
117 | #if WLAN_COMPILE_FOR_CPU_HIGH |
---|
118 | // CPU_HIGH implementations |
---|
119 | |
---|
120 | /*****************************************************************************/ |
---|
121 | /** |
---|
122 | * @brief Set MAC time |
---|
123 | * |
---|
124 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
125 | * and can be updated (ie the MAC time). This function sets the counter value. |
---|
126 | * Some 802.11 handshakes require updating the MAC time to match a partner node's |
---|
127 | * MAC time value (reception of a beacon, for example) |
---|
128 | * |
---|
129 | * @param new_time - u64 number of microseconds for the new MAC time of the node |
---|
130 | * @return None |
---|
131 | */ |
---|
132 | void set_mac_time_usec(u64 new_time) { |
---|
133 | |
---|
134 | wlan_ipc_msg_t ipc_msg_to_low; |
---|
135 | u64 ipc_msg_to_low_payload = new_time; |
---|
136 | |
---|
137 | // Send message to CPU Low |
---|
138 | ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_SET_MAC_TIME); |
---|
139 | ipc_msg_to_low.arg0 = 0; //Indicates that the payload is absolute |
---|
140 | ipc_msg_to_low.num_payload_words = 2; |
---|
141 | ipc_msg_to_low.payload_ptr = (u32*)(&(ipc_msg_to_low_payload)); |
---|
142 | |
---|
143 | write_mailbox_msg(&ipc_msg_to_low); |
---|
144 | |
---|
145 | } |
---|
146 | |
---|
147 | |
---|
148 | |
---|
149 | /*****************************************************************************/ |
---|
150 | /** |
---|
151 | * @brief Apply time delta to MAC time |
---|
152 | * |
---|
153 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
154 | * and can be updated (ie the MAC time). This function updates the counter value |
---|
155 | * by time_delta microseconds (note that the time delta is an s64 and can be positive |
---|
156 | * or negative). Some 802.11 handshakes require updating the MAC time to match a |
---|
157 | * partner node's MAC time value (reception of a beacon, for example) |
---|
158 | * |
---|
159 | * @param time_delta - s64 number of microseconds to change the MAC time of the node |
---|
160 | * @return None |
---|
161 | */ |
---|
162 | void apply_mac_time_delta_usec(s64 time_delta) { |
---|
163 | |
---|
164 | wlan_ipc_msg_t ipc_msg_to_low; |
---|
165 | s64 ipc_msg_to_low_payload = time_delta; |
---|
166 | |
---|
167 | // Send message to CPU Low |
---|
168 | ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_SET_MAC_TIME); |
---|
169 | ipc_msg_to_low.arg0 = 1; //Indicates that the payload is a delta |
---|
170 | ipc_msg_to_low.num_payload_words = 2; |
---|
171 | ipc_msg_to_low.payload_ptr = (u32*)(&(ipc_msg_to_low_payload)); |
---|
172 | |
---|
173 | write_mailbox_msg(&ipc_msg_to_low); |
---|
174 | } |
---|
175 | |
---|
176 | #else |
---|
177 | // CPU_LOW implementations |
---|
178 | |
---|
179 | /*****************************************************************************/ |
---|
180 | /** |
---|
181 | * @brief Set MAC time |
---|
182 | * |
---|
183 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
184 | * and can be updated (ie the MAC time). This function sets the counter value. |
---|
185 | * Some 802.11 handshakes require updating the MAC time to match a partner node's |
---|
186 | * MAC time value (reception of a beacon, for example) |
---|
187 | * |
---|
188 | * @param new_time - u64 number of microseconds for the new MAC time of the node |
---|
189 | * @return None |
---|
190 | */ |
---|
191 | void set_mac_time_usec(u64 new_time) { |
---|
192 | |
---|
193 | Xil_Out32(WLAN_MAC_TIME_REG_NEW_MAC_TIME_MSB, (u32)(new_time >> 32)); |
---|
194 | Xil_Out32(WLAN_MAC_TIME_REG_NEW_MAC_TIME_LSB, (u32)(new_time & 0xFFFFFFFF)); |
---|
195 | |
---|
196 | Xil_Out32(WLAN_MAC_TIME_REG_CONTROL, (Xil_In32(WLAN_MAC_TIME_REG_CONTROL) & ~WLAN_MAC_TIME_CTRL_REG_UPDATE_MAC_TIME)); |
---|
197 | Xil_Out32(WLAN_MAC_TIME_REG_CONTROL, (Xil_In32(WLAN_MAC_TIME_REG_CONTROL) | WLAN_MAC_TIME_CTRL_REG_UPDATE_MAC_TIME)); |
---|
198 | Xil_Out32(WLAN_MAC_TIME_REG_CONTROL, (Xil_In32(WLAN_MAC_TIME_REG_CONTROL) & ~WLAN_MAC_TIME_CTRL_REG_UPDATE_MAC_TIME)); |
---|
199 | } |
---|
200 | |
---|
201 | |
---|
202 | |
---|
203 | /*****************************************************************************/ |
---|
204 | /** |
---|
205 | * @brief Apply time delta to MAC time |
---|
206 | * |
---|
207 | * The Reference Design includes a 64-bit counter that increments every microsecond |
---|
208 | * and can be updated (ie the MAC time). This function updates the counter value |
---|
209 | * by time_delta microseconds (note that the time delta is an s64 and can be positive |
---|
210 | * or negative). Some 802.11 handshakes require updating the MAC time to match a |
---|
211 | * partner node's MAC time value (reception of a beacon, for example) |
---|
212 | * |
---|
213 | * @param time_delta - s64 number of microseconds to change the MAC time of the node |
---|
214 | * @return None |
---|
215 | */ |
---|
216 | void apply_mac_time_delta_usec(s64 time_delta) { |
---|
217 | |
---|
218 | u64 new_mac_time; |
---|
219 | |
---|
220 | // Compute the new MAC time based on the current MAC time and the time delta |
---|
221 | // The extra 1 usec is empirically determined. This compensates for the time it |
---|
222 | // takes to read, modify, then write the new MAC time back to the core. |
---|
223 | new_mac_time = get_mac_time_usec() + time_delta + 1; |
---|
224 | |
---|
225 | // Update the time in the MAC Time HW core |
---|
226 | set_mac_time_usec(new_mac_time); |
---|
227 | } |
---|
228 | #endif |
---|
229 | |
---|
230 | |
---|
231 | /*****************************************************************************/ |
---|
232 | /** |
---|
233 | * @brief Sleep delay (in microseconds) |
---|
234 | * |
---|
235 | * Function will delay execution for the specified amount of time. |
---|
236 | * |
---|
237 | * NOTE: This function is based on the system timestamp so it will not be affected |
---|
238 | * by updates to the MAC time. |
---|
239 | * |
---|
240 | * @param delay - Time to sleep in microseconds (u64) |
---|
241 | * @return None |
---|
242 | */ |
---|
243 | void wlan_usleep(u64 delay) { |
---|
244 | u64 timestamp = get_system_time_usec(); |
---|
245 | while (get_system_time_usec() < (timestamp + delay)) {} |
---|
246 | return; |
---|
247 | } |
---|
248 | |
---|
249 | |
---|
250 | |
---|