[6319] | 1 | /** @file wlan_phy_util.c |
---|
| 2 | * @brief Physical Layer Utility |
---|
| 3 | * |
---|
| 4 | * This contains code for configuring low-level parameters in the PHY and hardware. |
---|
| 5 | * |
---|
| 6 | * @copyright Copyright 2013-2019, Mango Communications. All rights reserved. |
---|
| 7 | * Distributed under the Mango Communications Reference Design License |
---|
| 8 | * See LICENSE.txt included in the design archive or |
---|
| 9 | * at http://mangocomm.com/802.11/license |
---|
| 10 | * |
---|
| 11 | * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) |
---|
| 12 | */ |
---|
| 13 | /***************************** Include Files *********************************/ |
---|
| 14 | |
---|
| 15 | // Xilinx SDK includes |
---|
| 16 | #include "stdio.h" |
---|
| 17 | #include "stdarg.h" |
---|
| 18 | #include "xio.h" |
---|
| 19 | #include "string.h" |
---|
| 20 | #include "xparameters.h" |
---|
| 21 | |
---|
| 22 | // WLAN includes |
---|
| 23 | #include "wlan_platform_low.h" |
---|
| 24 | #include "wlan_platform_common.h" |
---|
| 25 | #include "wlan_mac_mailbox_util.h" |
---|
| 26 | #include "wlan_phy_util.h" |
---|
| 27 | #include "wlan_mac_low.h" |
---|
| 28 | #include "wlan_mac_common.h" |
---|
| 29 | #include "wlan_mac_pkt_buf_util.h" |
---|
| 30 | |
---|
| 31 | // LUT of number of ones in each byte (used to calculate PARITY in SIGNAL) |
---|
| 32 | const u8 ones_in_chars[256] = { |
---|
| 33 | 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0x00 - 0x0F |
---|
| 34 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 0x10 - 0x1F |
---|
| 35 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 0x20 - 0x2F |
---|
| 36 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0x30 - 0x3F |
---|
| 37 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 0x40 - 0x4F |
---|
| 38 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0x50 - 0x5F |
---|
| 39 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0x60 - 0x6F |
---|
| 40 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 0x70 - 0x7F |
---|
| 41 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 0x80 - 0x8F |
---|
| 42 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0x90 - 0x9F |
---|
| 43 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0xA0 - 0xAF |
---|
| 44 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 0xB0 - 0xBF |
---|
| 45 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 0xC0 - 0xCF |
---|
| 46 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 0xD0 - 0xDF |
---|
| 47 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 0xE0 - 0xEF |
---|
| 48 | 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; // 0xF0 - 0xFF |
---|
| 49 | |
---|
| 50 | |
---|
| 51 | // Common Platform Device Info |
---|
| 52 | extern platform_common_dev_info_t platform_common_dev_info; |
---|
| 53 | |
---|
| 54 | |
---|
| 55 | /*****************************************************************************/ |
---|
| 56 | /** |
---|
| 57 | * Calculates the PHY preamble (SIGNAL for 11a, L-SIG/HT-SIG for 11n) and writes |
---|
| 58 | * the preamble bytes to the specified packet buffer. The PHY preamble must be |
---|
| 59 | * written to the packet buffer for each transmission. |
---|
| 60 | * |
---|
| 61 | * @param pkt_buf - Packet buffer number |
---|
| 62 | * @param phy_mode - Sets waveform format; must be PHY_MODE_NON_HT (11a) or PHY_MODE_HTMF (11n) |
---|
| 63 | * @param mcs - MCS (modulation/coding scheme) index |
---|
| 64 | * @param length - Length of the packet being transmitted |
---|
| 65 | * |
---|
| 66 | * @return None |
---|
| 67 | * |
---|
| 68 | *****************************************************************************/ |
---|
| 69 | const u8 sig_rate_vals[8] = {0xB, 0xF, 0xA, 0xE, 0x9, 0xD, 0x8, 0xC}; |
---|
| 70 | |
---|
| 71 | void write_phy_preamble(u8 pkt_buf, u8 phy_mode, u8 mcs, u16 length) { |
---|
| 72 | |
---|
| 73 | u8* htsig_ptr; |
---|
| 74 | u16 lsig_length; |
---|
| 75 | u8* phy_hdr_ptr; |
---|
| 76 | |
---|
| 77 | phy_hdr_ptr = (u8*)(CALC_PKT_BUF_ADDR(platform_common_dev_info.tx_pkt_buf_baseaddr, pkt_buf) + PHY_TX_PKT_BUF_PHY_HDR_OFFSET); |
---|
| 78 | |
---|
| 79 | // RATE field values for SIGNAL/L-SIG in PHY preamble (IEEE 802.11-2012 18.3.4.2) |
---|
| 80 | // RATE field in SIGNAL/L-SIG is one of 8 4-bit values indicating modulation scheme and coding rate |
---|
| 81 | // For 11a (NONHT) transmissions we map mcs index to SIGNAL.RATE directly |
---|
| 82 | // For 11n (HTMF) transmissions the L-SIG.RATE field is always the lowest (BSPK 1/2) |
---|
| 83 | |
---|
| 84 | if((phy_mode & PHY_MODE_NONHT) == PHY_MODE_NONHT) { |
---|
| 85 | //11a mode - write SIGNAL (3 bytes) |
---|
| 86 | |
---|
| 87 | //Zero-out any stale header, also properly sets SERVICE and reserved bytes to 0 |
---|
| 88 | //Old: takes longer than necessary. We only really need the SERVICE bits 0. |
---|
| 89 | //bzero((u32*)phy_hdr_ptr, PHY_TX_PKT_BUF_PHY_HDR_SIZE); |
---|
| 90 | |
---|
| 91 | // Set SERVICE to 0. |
---|
| 92 | // Unfortunately, SERVICE spans a 32-bit boundary so we need two write 2 words. |
---|
| 93 | Xil_Out32((u32)(phy_hdr_ptr + 0), 0); |
---|
| 94 | Xil_Out32((u32)(phy_hdr_ptr + 4), 0); |
---|
| 95 | |
---|
| 96 | //Set SIGNAL with actual rate/length |
---|
| 97 | Xil_Out32((u32)phy_hdr_ptr, WLAN_TX_SIGNAL_CALC(sig_rate_vals[mcs], length)); |
---|
| 98 | |
---|
| 99 | } else if((phy_mode & PHY_MODE_HTMF) == PHY_MODE_HTMF) { |
---|
| 100 | //11n mode - write L-SIG (3 bytes) and HT-SIG (6 bytes) |
---|
| 101 | |
---|
| 102 | //Zero-out any stale header, also properly sets SERVICE, reserved bytes, and auto-filled bytes of HT-SIG to 0 |
---|
| 103 | //Old: takes longer than necessary. We only really need the SERVICE bits 0. |
---|
| 104 | //bzero((u32*)(TX_PKT_BUF_TO_ADDR(pkt_buf) + PHY_TX_PKT_BUF_PHY_HDR_OFFSET), PHY_TX_PKT_BUF_PHY_HDR_SIZE); |
---|
| 105 | |
---|
| 106 | // Set SERVICE to 0. |
---|
| 107 | Xil_Out32((u32)(phy_hdr_ptr + 8), 0); |
---|
| 108 | |
---|
| 109 | |
---|
| 110 | // L-SIG is same format as 11a SIGNAL, but with RATE always 6Mb and LENGTH |
---|
| 111 | // set such that LENGTH/6Mb matches duration of HT transmission |
---|
| 112 | // Using equation from IEEE 802.11-2012 9.23.4 |
---|
| 113 | // L-SIG.LENGTH = (3*ceil( (TXTIME - 6 - 20) / 4) - 3) |
---|
| 114 | // where TXTIME is actual duration of the HT transmission |
---|
| 115 | // The ceil((TXTIME - 6 - 20)/4) term represents the number of OFDM symbols after the L-SIG symbol |
---|
| 116 | // (-6-20) are (T_EXT-T_NONHT_PREAMBLE); (-3) accounts for service/tail |
---|
| 117 | |
---|
| 118 | // Calc (3*(num_payload_syms+num_ht_preamble_syms) = (3*(num_payload_syms+4)) |
---|
| 119 | |
---|
| 120 | lsig_length = 3*wlan_ofdm_calc_num_payload_syms(length, mcs, phy_mode) + 12 - 3; |
---|
| 121 | |
---|
| 122 | |
---|
| 123 | |
---|
| 124 | Xil_Out32((u32)phy_hdr_ptr, WLAN_TX_SIGNAL_CALC(sig_rate_vals[0], lsig_length)); |
---|
| 125 | |
---|
| 126 | //Assign pointer to first byte of HTSIG (PHY header base + 3 for sizeof(L-SIG)) |
---|
| 127 | htsig_ptr = (u8*)(phy_hdr_ptr + 3); |
---|
| 128 | |
---|
| 129 | //Set HTSIG bytes |
---|
| 130 | // PHY logic fills in bytes 4 and 5; ok to ignore here |
---|
| 131 | // Going byte-by-byte for now - maybe optimize to (unaligned) 32-bit write later |
---|
| 132 | htsig_ptr[0] = mcs & 0x3F; //MSB is channel bandwidth; 0=20MHz |
---|
| 133 | htsig_ptr[1] = length & 0xFF; |
---|
| 134 | htsig_ptr[2] = (length >> 8) & 0xFF; |
---|
| 135 | htsig_ptr[3] = 0x7; //smoothing=1, not-sounding=1, reserved=1, aggregation=STBC=FEC=short_GI=0 |
---|
| 136 | htsig_ptr[4] = 0x00; //NESS=0, CRC bytes must be 0 in pkt buf, PHY replaces with CRC value |
---|
| 137 | htsig_ptr[5] = 0x00; |
---|
| 138 | } |
---|
| 139 | |
---|
| 140 | |
---|
| 141 | return; |
---|
| 142 | } |
---|
| 143 | |
---|
| 144 | /*****************************************************************************/ |
---|
| 145 | /** |
---|
| 146 | * Calculates duration of an OFDM waveform. |
---|
| 147 | * |
---|
| 148 | * This function assumes every OFDM symbol is the same duration. Duration |
---|
| 149 | * calculation for short guard interval (SHORT_GI) waveforms is not supported. |
---|
| 150 | * |
---|
| 151 | * @param length - Length of MAC payload in bytes |
---|
| 152 | * @param mcs - MCS index |
---|
| 153 | * @param phy_mode - PHY waveform mode - either PHY_MODE_NONHT (11a/g) or PHY_MODE_HTMF (11n) |
---|
| 154 | * @param phy_samp_rate - PHY sampling rate - one of (PHY_10M, PHY_20M, PHY_40M) |
---|
| 155 | * |
---|
| 156 | * @return u16 - Duration of transmission in microseconds |
---|
| 157 | * (See IEEE 802.11-2012 18.4.3 and 20.4.3) |
---|
| 158 | *****************************************************************************/ |
---|
| 159 | inline u16 wlan_ofdm_calc_txtime(u16 length, u8 mcs, u8 phy_mode, phy_samp_rate_t phy_samp_rate) { |
---|
| 160 | |
---|
| 161 | u16 num_ht_preamble_syms, num_payload_syms; |
---|
| 162 | |
---|
| 163 | u16 t_preamble; |
---|
| 164 | u16 t_sym; |
---|
| 165 | |
---|
| 166 | // Note: the t_ext signal extension represent the value used in the standard, which in turn |
---|
| 167 | // is the value expected by other commercial WLAN devices. By default, the signal extensions |
---|
| 168 | // programmed into the PHY match this value. |
---|
| 169 | u16 t_ext = 6; |
---|
| 170 | |
---|
| 171 | // Set OFDM symbol duration in microseconds; only depends on PHY sampling rate |
---|
| 172 | switch(phy_samp_rate) { |
---|
| 173 | case PHY_40M: |
---|
| 174 | t_sym = 2; |
---|
| 175 | break; |
---|
| 176 | |
---|
| 177 | default: |
---|
| 178 | case PHY_20M: |
---|
| 179 | t_sym = 4; |
---|
| 180 | break; |
---|
| 181 | |
---|
| 182 | case PHY_10M: |
---|
| 183 | t_sym = 8; |
---|
| 184 | break; |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | // PHY preamble common to NONHT and HTMF waveforms consists of 5 OFDM symbols |
---|
| 188 | // 4 symbols for STF/LTF |
---|
| 189 | // 1 symbol for SIGNAL/L-SIG |
---|
| 190 | t_preamble = 5*t_sym; |
---|
| 191 | |
---|
| 192 | // Only HTMF waveforms have HT-SIG, HT-STF and HT-LTF symbols |
---|
| 193 | if(phy_mode == PHY_MODE_HTMF) { |
---|
| 194 | num_ht_preamble_syms = 4; |
---|
| 195 | } else { |
---|
| 196 | num_ht_preamble_syms = 0; |
---|
| 197 | } |
---|
| 198 | |
---|
| 199 | num_payload_syms = wlan_ofdm_calc_num_payload_syms(length, mcs, phy_mode); |
---|
| 200 | |
---|
| 201 | // Sum each duration and return |
---|
| 202 | return (t_preamble + (t_sym * (num_ht_preamble_syms + num_payload_syms)) + t_ext); |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | |
---|
| 206 | /*****************************************************************************/ |
---|
| 207 | /** |
---|
| 208 | * Calculates number of payload OFDM symbols in a packet. Implements |
---|
| 209 | * ceil(payload_length_bits / num_bits_per_ofdm_sym) |
---|
| 210 | * where num_bits_per_ofdm_sym is a function of the MCS index and PHY mode |
---|
| 211 | * |
---|
| 212 | * @param length - Length of MAC payload in bytes |
---|
| 213 | * @param mcs - MCS index |
---|
| 214 | * @param phy_mode - PHY waveform mode - either PHY_MODE_NONHT (11a/g) or PHY_MODE_HTMF (11n) |
---|
| 215 | * |
---|
| 216 | * @return u16 - Number of OFDM symbols |
---|
| 217 | *****************************************************************************/ |
---|
| 218 | inline u16 wlan_ofdm_calc_num_payload_syms(u16 length, u8 mcs, u8 phy_mode) { |
---|
| 219 | u16 num_payload_syms; |
---|
| 220 | u32 num_payload_bits; |
---|
| 221 | u16 n_dbps; |
---|
| 222 | |
---|
| 223 | // Payload consists of: |
---|
| 224 | // 16-bit SERVICE field |
---|
| 225 | // 'length' byte MAC payload |
---|
| 226 | // 6-bit TAIL field |
---|
| 227 | num_payload_bits = 16 + (8 * length) + 6; |
---|
| 228 | |
---|
| 229 | // Num payload syms is ceil(num_payload_bits / N_DATA_BITS_PER_SYM). The ceil() |
---|
| 230 | // operation implicitly accounts for any PAD bits in the waveform. The PHY inserts |
---|
| 231 | // PAD bits to fill the final OFDM symbol. A waveform always consists of an integer |
---|
| 232 | // number of OFDM symbols, so the actual number of PAD bits is irrelevant here |
---|
| 233 | n_dbps = wlan_mac_low_mcs_to_n_dbps(mcs, phy_mode); |
---|
| 234 | num_payload_syms = num_payload_bits / n_dbps; |
---|
| 235 | |
---|
| 236 | // Apply ceil() |
---|
| 237 | // Integer div above implies floor(); increment result if floor() changed result |
---|
| 238 | if( (n_dbps * num_payload_syms) != num_payload_bits ) { |
---|
| 239 | num_payload_syms++; |
---|
| 240 | } |
---|
| 241 | |
---|
| 242 | return num_payload_syms; |
---|
| 243 | } |
---|
| 244 | |
---|