154 | | '''TODO''' |
| 154 | Fundamentally, FDD changes NoMAC so that Tx and Rx events no longer occur in complete atomic contexts. The NoMAC code still has to operate serially while the Tx and Rx operations operate in parallels. We will need a few new global variables to help us track whether we are currently transmitting and/or receiving and, if so, which packet buffers are currently being used. Add the following to the top of {{{wlan_mac_nomac.c}}}: |
| 155 | |
| 156 | {{{#!c |
| 157 | typedef enum {TX_STATE_IDLE, TX_STATE_PENDING} tx_state_t; |
| 158 | typedef enum {RX_STATE_IDLE, RX_STATE_PENDING} rx_state_t; |
| 159 | |
| 160 | tx_state_t tx_state; |
| 161 | u8 pending_tx_pkt_buf; |
| 162 | rx_state_t rx_state; |
| 163 | u8 pending_rx_pkt_buf; |
| 164 | }}} |
| 165 | |
| 166 | Next, we will update the ```frame_transmit()``` function such that it immediately returns after submitting the packet to the MAC Tx Controller rather than wait for the PHY to finish. Replace ```frame_transmit()``` with the following: |
| 167 | |
| 168 | {{{#!c |
| 169 | int frame_transmit(u8 pkt_buf) { |
| 170 | // The pkt_buf, rate, and length arguments provided to this function specifically relate to |
| 171 | // the MPDU that the WLAN MAC LOW framework wants to send. |
| 172 | |
| 173 | u8 tx_gain; |
| 174 | |
| 175 | tx_state = TX_STATE_PENDING; |
| 176 | pending_tx_pkt_buf = pkt_buf; |
| 177 | |
| 178 | tx_frame_info_t * tx_frame_info = (tx_frame_info_t*) (TX_PKT_BUF_TO_ADDR(pkt_buf)); |
| 179 | u8 mpdu_tx_ant_mask = 0; |
| 180 | |
| 181 | // Extract waveform params from the tx_frame_info |
| 182 | u8 mcs = tx_frame_info->params.phy.mcs; |
| 183 | u8 phy_mode = (tx_frame_info->params.phy.phy_mode & (PHY_MODE_HTMF | PHY_MODE_NONHT)); |
| 184 | u16 length = tx_frame_info->length; |
| 185 | |
| 186 | // Write the PHY premable (SIGNAL or L-SIG/HT-SIG) to the packet buffer |
| 187 | write_phy_preamble(pkt_buf, phy_mode, mcs, length); |
| 188 | |
| 189 | // Set the antenna mode |
| 190 | switch(tx_frame_info->params.phy.antenna_mode) { |
| 191 | case TX_ANTMODE_SISO_ANTA: mpdu_tx_ant_mask |= 0x1; break; |
| 192 | case TX_ANTMODE_SISO_ANTB: mpdu_tx_ant_mask |= 0x2; break; |
| 193 | case TX_ANTMODE_SISO_ANTC: mpdu_tx_ant_mask |= 0x4; break; |
| 194 | case TX_ANTMODE_SISO_ANTD: mpdu_tx_ant_mask |= 0x8; break; |
| 195 | default: mpdu_tx_ant_mask = 0x1; break; // Default to RF_A |
| 196 | } |
| 197 | |
| 198 | // Fill in the number of attempts to transmit the packet |
| 199 | tx_frame_info->num_tx_attempts = 1; |
| 200 | |
| 201 | // Update tx_frame_info with current PHY sampling rate |
| 202 | tx_frame_info->phy_samp_rate = (u8)wlan_mac_low_get_phy_samp_rate(); |
| 203 | |
| 204 | // Convert the requested Tx power (dBm) to a Tx gain setting for the radio |
| 205 | tx_gain = wlan_mac_low_dbm_to_gain_target(tx_frame_info->params.phy.power); |
| 206 | |
| 207 | // Set the MAC HW control parameters |
| 208 | // args: (pktBuf, antMask, preTx_backoff_slots, preWait_postRxTimer1, preWait_postTxTimer1, postWait_postTxTimer2, phy_mode) |
| 209 | wlan_mac_tx_ctrl_A_params(pkt_buf, mpdu_tx_ant_mask, 0, 0, 0, 0, phy_mode); |
| 210 | |
| 211 | // Set Tx Gains - use same gain for all RF interfaces |
| 212 | wlan_mac_tx_ctrl_A_gains(tx_gain, tx_gain, tx_gain, tx_gain); |
| 213 | |
| 214 | // Before we mess with any PHY state, we need to make sure it isn't actively |
| 215 | // transmitting. For example, it may be sending an ACK when we get to this part of the code |
| 216 | while (wlan_mac_get_status() & WLAN_MAC_STATUS_MASK_TX_PHY_ACTIVE) {} |
| 217 | |
| 218 | // Submit the MPDU for transmission - this starts the MAC hardware's MPDU Tx state machine |
| 219 | wlan_mac_tx_ctrl_A_start(1); |
| 220 | wlan_mac_tx_ctrl_A_start(0); |
| 221 | |
| 222 | return TX_FRAME_INFO_RESULT_SUCCESS; |
| 223 | } |
| 224 | }}} |
| 225 | |
| 226 | Now we need a function that will "finish" the transmission with everything that used to take place after {{{wlan_mac_tx_ctrl_A_start()}}} from NoMAC's original {{{frame_transmit()}}}. This function should poll the status of the transmission and immediately quit if the transmission is still ongoing. Add the following function to {{{wlan_mac_nomac.c}}} and be sure to add a new declaration for it in the header file: |
| 227 | |
| 228 | {{{#!c |
| 229 | void finish_frame_transmit(){ |
| 230 | wlan_mac_low_tx_details_t low_tx_details = {0}; |
| 231 | u32 mac_tx_ctrl_status; |
| 232 | |
| 233 | tx_frame_info_t * tx_frame_info = (tx_frame_info_t*) (TX_PKT_BUF_TO_ADDR(pending_tx_pkt_buf)); |
| 234 | |
| 235 | // Extract waveform params from the tx_frame_info |
| 236 | u8 mcs = tx_frame_info->params.phy.mcs; |
| 237 | u8 phy_mode = (tx_frame_info->params.phy.phy_mode & (PHY_MODE_HTMF | PHY_MODE_NONHT)); |
| 238 | |
| 239 | // Get the MAC HW status |
| 240 | mac_tx_ctrl_status = wlan_mac_get_tx_ctrl_status(); |
| 241 | |
| 242 | if(mac_tx_ctrl_status & WLAN_MAC_TXCTRL_STATUS_MASK_TX_A_DONE){ |
| 243 | |
| 244 | // Fill in the Tx low details |
| 245 | low_tx_details.phy_params_mpdu.mcs = mcs; |
| 246 | low_tx_details.phy_params_mpdu.phy_mode = phy_mode; |
| 247 | low_tx_details.phy_params_mpdu.power = tx_frame_info->params.phy.power; |
| 248 | low_tx_details.phy_params_mpdu.antenna_mode = tx_frame_info->params.phy.antenna_mode; |
| 249 | low_tx_details.chan_num = wlan_mac_low_get_active_channel(); |
| 250 | low_tx_details.num_slots = 0; |
| 251 | low_tx_details.cw = 0; |
| 252 | low_tx_details.attempt_number = 1; |
| 253 | low_tx_details.tx_start_timestamp_mpdu = wlan_mac_low_get_tx_start_timestamp(); |
| 254 | low_tx_details.tx_start_timestamp_frac_mpdu = wlan_mac_low_get_tx_start_timestamp_frac(); |
| 255 | low_tx_details.tx_details_type = TX_DETAILS_MPDU; |
| 256 | |
| 257 | // Send IPC message containing the details about this low-level transmission |
| 258 | wlan_mac_low_send_low_tx_details(pending_tx_pkt_buf, &low_tx_details); |
| 259 | wlan_mac_low_finish_frame_transmit(pending_tx_pkt_buf); |
| 260 | tx_state = TX_STATE_IDLE; |
| 261 | } |
| 262 | } |
| 263 | }}} |
| 264 | |
| 265 | Next, we need to perform a similar dissection of the {{{frame_receive()}}} context to quit early before the reception has finished. Note: it is important to return the value {{{FRAME_RX_RET_SKIP_RX_STARTED_RESET}}} from this context to prevent the MAC Low Framework from resetting the PHY while the reception is still ongoing. Replace {{{frame_receive()}}} with the following: |
| 266 | |
| 267 | {{{#!c |
| 268 | u32 frame_receive(u8 rx_pkt_buf, phy_rx_details_t* phy_details){ |
| 269 | |
| 270 | void * pkt_buf_addr = (void *) RX_PKT_BUF_TO_ADDR(rx_pkt_buf); |
| 271 | rx_frame_info_t * rx_frame_info = (rx_frame_info_t *) pkt_buf_addr; |
| 272 | |
| 273 | rx_state = RX_STATE_PENDING; |
| 274 | pending_rx_pkt_buf = rx_pkt_buf; |
| 275 | |
| 276 | // Fill in the MPDU info fields for the reception. These values are known at RX_START. The other fields below |
| 277 | // must be written after RX_END |
| 278 | rx_frame_info->flags = 0; |
| 279 | rx_frame_info->phy_details = *phy_details; |
| 280 | rx_frame_info->channel = wlan_mac_low_get_active_channel(); |
| 281 | rx_frame_info->phy_samp_rate = (u8)wlan_mac_low_get_phy_samp_rate(); |
| 282 | rx_frame_info->timestamp = wlan_mac_low_get_rx_start_timestamp(); |
| 283 | rx_frame_info->timestamp_frac = wlan_mac_low_get_rx_start_timestamp_frac(); |
| 284 | |
| 285 | return FRAME_RX_RET_SKIP_RX_STARTED_RESET; |
| 286 | } |
| 287 | }}} |
| 288 | |
| 289 | Next, we need to finish the frame reception in another function. Like {{{finish_frame_transmit()}}} from above, this function should immediately quit if the PHY is still receiving a frame. Add the following function to {{{wlan_mac_nomac.c}}} and declare it in NoMAC's header: |
| 290 | |
| 291 | {{{ |
| 292 | #!c |
| 293 | void finish_frame_receive(){ |
| 294 | |
| 295 | void * pkt_buf_addr = (void *) RX_PKT_BUF_TO_ADDR(pending_rx_pkt_buf); |
| 296 | rx_frame_info_t * rx_frame_info = (rx_frame_info_t *) pkt_buf_addr; |
| 297 | |
| 298 | u32 mac_hw_status = wlan_mac_get_status(); |
| 299 | |
| 300 | if(mac_hw_status & (WLAN_MAC_STATUS_MASK_RX_PHY_ACTIVE | WLAN_MAC_STATUS_MASK_RX_PHY_WRITING_PAYLOAD)){ |
| 301 | // The PHY is not finished receiving. Jump back to main() |
| 302 | return; |
| 303 | } else { |
| 304 | |
| 305 | if(wlan_mac_hw_rx_finish() == 1){ |
| 306 | //FCS was good |
| 307 | rx_frame_info->flags |= RX_FRAME_INFO_FLAGS_FCS_GOOD; |
| 308 | } else { |
| 309 | //FCS was bad |
| 310 | rx_frame_info->flags &= ~RX_FRAME_INFO_FLAGS_FCS_GOOD; |
| 311 | } |
| 312 | |
| 313 | // Update the rest of the frame_info fields using post-Rx information |
| 314 | rx_frame_info->ant_mode = wlan_phy_rx_get_active_rx_ant(); |
| 315 | rx_frame_info->cfo_est = wlan_phy_rx_get_cfo_est(); |
| 316 | rx_frame_info->rf_gain = wlan_phy_rx_get_agc_RFG(rx_frame_info->ant_mode); |
| 317 | rx_frame_info->bb_gain = wlan_phy_rx_get_agc_BBG(rx_frame_info->ant_mode); |
| 318 | rx_frame_info->rx_power = wlan_mac_low_calculate_rx_power(wlan_phy_rx_get_pkt_rssi(rx_frame_info->ant_mode), wlan_phy_rx_get_agc_RFG(rx_frame_info->ant_mode)); |
| 319 | |
| 320 | // Increment the LEDs based on the FCS status |
| 321 | if(rx_frame_info->flags & RX_FRAME_INFO_FLAGS_FCS_GOOD){ |
| 322 | green_led_index = (green_led_index + 1) % NUM_LEDS; |
| 323 | userio_write_leds_green(USERIO_BASEADDR, (1 << green_led_index)); |
| 324 | } else { |
| 325 | red_led_index = (red_led_index + 1) % NUM_LEDS; |
| 326 | userio_write_leds_red(USERIO_BASEADDR, (1 << red_led_index)); |
| 327 | } |
| 328 | |
| 329 | rx_frame_info->rx_pkt_buf_state = RX_PKT_BUF_READY; |
| 330 | if (unlock_rx_pkt_buf(pending_rx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS) { |
| 331 | xil_printf("Error: unable to unlock RX pkt_buf %d\n", pending_rx_pkt_buf); |
| 332 | wlan_mac_low_send_exception(WLAN_ERROR_CODE_CPU_LOW_RX_MUTEX); |
| 333 | } else { |
| 334 | |
| 335 | wlan_mac_low_frame_ipc_send(); |
| 336 | |
| 337 | // Find a free packet buffer and begin receiving packets there (blocks until free buf is found) |
| 338 | wlan_mac_low_lock_empty_rx_pkt_buf(); |
| 339 | } |
| 340 | |
| 341 | wlan_mac_hw_clear_rx_started(); //FDD-NoMAC |
| 342 | rx_state = RX_STATE_IDLE; |
| 343 | } |
| 344 | } |
| 345 | }}} |
| 346 | |
| 347 | Finally, we will call our new functions for finishing Tx or Rx events from the primary while loop in {{{main()}}}. Replace the existing while loop with the following: |
| 348 | |
| 349 | {{{#!c |
| 350 | while(1){ |
| 351 | switch(rx_state){ |
| 352 | case RX_STATE_IDLE: |
| 353 | // Poll PHY RX start |
| 354 | wlan_mac_low_poll_frame_rx(); |
| 355 | break; |
| 356 | case RX_STATE_PENDING: |
| 357 | finish_frame_receive(); |
| 358 | break; |
| 359 | } |
| 360 | |
| 361 | switch(tx_state){ |
| 362 | case TX_STATE_IDLE: |
| 363 | // Poll IPC rx for a new tx packet |
| 364 | wlan_mac_low_poll_ipc_rx(); |
| 365 | break; |
| 366 | case TX_STATE_PENDING: |
| 367 | //Finish any ongoing transmission |
| 368 | finish_frame_transmit(); |
| 369 | break; |
| 370 | } |
| 371 | } |
| 372 | }}} |
| 373 | |
| 374 | ---- |
| 375 | |
| 376 | === 4. Evaluation with Experiments Framework === |