OpenWIFI是一套基于FPGA SDR的开源WIFI协议平台。
OpenWIFI的优势在于可以根据需要充分的定制,都够便利地实现一般WIFI网卡不具备的功能,所以在很多基于WIFI的研究中,会选择在OpenWIFI的平台上做, 比较常见的就是利用CSI进行环境探测, 另外就是把OpenWIFI作为WIFI探测设备使用,通过OpenWIFI可以直接提取IQ流,更可以直接在FPGA内计算
为了让OpenWIFI有更好的性能,我选择尝试将OpenWIFI从AD9361移植到ADRV9002上。这里实际用的硬件是一块搭载XZCU15EG芯片的开发板(天嵌通途TQ15EG)+ADRV9002的官方验证板ADRV9002BBZC。开发板是参考ZCU102设计的,它的FMC0和ZCU102完全兼容。
这其中FPGA的部分相对简单,ADI提供的ADRV9002和AD9361的IP核接口比较相似,可以按照同样的方式连接。
主要工作:驱动上(sdr.c)将AD9361的操作替换为ADRV9002 2. ADRV9002和AD9361的增益表不同,ADRV9002通过PIN输出的AGC值的格式和AD9361也不一样,需要重新对齐。
采样率的问题,OpenWIFI原本是将20M调制后的IQ再插值到40M,AD9361使用40M采样率,用内置的FIR做差值滤波,实际出来的带宽还是20M。
这里可以选择不做插值改为20M采样率,或保持40M采样率并用ADRV9002的FIR做差值滤波。
因为器件改变了,原本根据AD9361配置的一些如信号强度(RSSI)门限也需要调整,比如退避和OFDM检测都有相关的门限值, 理论上如果能够对齐ADRV9002和AD9361的增益,这些值应该不用改,但后来发现还是需要重新调整的。
除了信号强度门限值,一些计数器的超时值也需要调整。
首先需要搭建起OpenWIFI IP+ADRV9002的HDL工程,原本OpenWIFI是在ADI提供的HDL工程上增加OpenWIFI的内容,所以这里也按照相同的步骤。
openwifi使用的Vivado版本是2021,对应的ADI的HDL版本是2021_R1。
选择和要迁移的硬件最相似的工程,也就是zcu102_fmcs2
,Xilinx ZCU102 board + FMCOMMS2/3/4,先按照官方的步骤构建这个平台的工程,然后再往其他硬件上迁移。
首先先把ZCU102迁移到新的开发板,然后用同样的FMCS2测试一下。
在工程里Project device
修改芯片型号,这里是xczu15eg-ffvb1156-2-i
,然后根据提示更新IP核。
两个板子的FMC0的FMC是兼容的,所以FMC部分的约束文件不用动,而其他的也只是一些DEBUG用的信号,这里仅仅将openwifi输出的几个LED改成新开发板上的LED。
openwifi里提供的设备树是把ADI的Kuipler镜像里的对应工程的设备树反编译出来后又修改的,不适合直接使用,这里还是自己重新生成设备树比较好。
从Vivado工程生成设备树的方法:https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842279/Build+Device+Tree+Blob,不过我习惯用Petalinux里创建的设备树,因为Petalinux能顺便把我们后面要用到的uboot也打包出来。
生成基础的设备树之后,还要把AD9361和openwifi的信息添加进去,AD9361的设备树可以在ADI的内核里找到:arch/arm64/boot/dts/xilinx/adi-fmcomms2.dtsi
,可以include或者干脆全粘贴进去。
#include <dt-bindings/interrupt-controller/irq.h>
#define fmc_spi spi0
/ {
clocks {
ad9361_clkin: clock@0 {
compatible = "fixed-clock";
clock-frequency = <40000000>;
clock-output-names = "ad9361_ext_refclk";
#clock-cells = <0>;
};
};
};
&fmc_spi {
adc0_ad9361: ad9361-phy@0 {
compatible = "adi,ad9361";
reg = <0>;
/* SPI Setup */
spi-cpha;
spi-max-frequency = <10000000>;
/* Clocks */
clocks = <&ad9361_clkin 0>;
clock-names = "ad9361_ext_refclk";
clock-output-names = "rx_sampl_clk", "tx_sampl_clk";
#clock-cells = <1>;
//adi,debug-mode-enable;
/* Digital Interface Control */
/* adi,digital-interface-tune-skip-mode:
* 0 = TUNE RX&TX
* 1 = SKIP TX
* 2 = SKIP ALL
*/
adi,digital-interface-tune-skip-mode = <0>;
adi,pp-tx-swap-enable;
adi,pp-rx-swap-enable;
adi,rx-frame-pulse-mode-enable;
adi,lvds-mode-enable;
adi,lvds-bias-mV = <150>;
adi,lvds-rx-onchip-termination-enable;
adi,rx-data-delay = <4>;
adi,tx-fb-clock-delay = <7>;
//adi,fdd-rx-rate-2tx-enable;
adi,dcxo-coarse-and-fine-tune = <8 5920>;
//adi,xo-disable-use-ext-refclk-enable;
/* Mode Setup */
adi,2rx-2tx-mode-enable;
//adi,split-gain-table-mode-enable;
/* ENSM Mode */
adi,frequency-division-duplex-mode-enable;
//adi,ensm-enable-pin-pulse-mode-enable;
//adi,ensm-enable-txnrx-control-enable;
/* adi,rx-rf-port-input-select:
* 0 = (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced
* 1 = (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced
* 2 = (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced
*
* 3 = RX1A_N and RX2A_N enabled; unbalanced
* 4 = RX1A_P and RX2A_P enabled; unbalanced
* 5 = RX1B_N and RX2B_N enabled; unbalanced
* 6 = RX1B_P and RX2B_P enabled; unbalanced
* 7 = RX1C_N and RX2C_N enabled; unbalanced
* 8 = RX1C_P and RX2C_P enabled; unbalanced
*/
adi,rx-rf-port-input-select = <0>; /* (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced */
/* adi,tx-rf-port-input-select:
* 0 TX1A, TX2A
* 1 TX1B, TX2B
*/
adi,tx-rf-port-input-select = <0>; /* TX1A, TX2A */
//adi,update-tx-gain-in-alert-enable;
adi,tx-attenuation-mdB = <10000>;
adi,tx-lo-powerdown-managed-enable;
adi,rf-rx-bandwidth-hz = <18000000>;
adi,rf-tx-bandwidth-hz = <18000000>;
adi,rx-synthesizer-frequency-hz = /bits/ 64 <2400000000>;
adi,tx-synthesizer-frequency-hz = /bits/ 64 <2450000000>;
/* BBPLL ADC R2CLK R1CLK CLKRF RSAMPL */
adi,rx-path-clock-frequencies = <983040000 245760000 122880000 61440000 30720000 30720000>;
/* BBPLL DAC T2CLK T1CLK CLKTF TSAMPL */
adi,tx-path-clock-frequencies = <983040000 122880000 122880000 61440000 30720000 30720000>;
/* Gain Control */
//adi,gaintable-name = "ad9361_std_gaintable";
/* adi,gc-rx[1|2]-mode:
* 0 = RF_GAIN_MGC
* 1 = RF_GAIN_FASTATTACK_AGC
* 2 = RF_GAIN_SLOWATTACK_AGC
* 3 = RF_GAIN_HYBRID_AGC
*/
adi,gc-rx1-mode = <2>;
adi,gc-rx2-mode = <2>;
adi,gc-adc-ovr-sample-size = <4>; /* sum 4 samples */
adi,gc-adc-small-overload-thresh = <47>; /* sum of squares */
adi,gc-adc-large-overload-thresh = <58>; /* sum of squares */
adi,gc-lmt-overload-high-thresh = <800>; /* mV */
adi,gc-lmt-overload-low-thresh = <704>; /* mV */
adi,gc-dec-pow-measurement-duration = <8192>; /* 0..524288 Samples */
adi,gc-low-power-thresh = <24>; /* 0..-64 dBFS vals are set pos */
//adi,gc-dig-gain-enable;
//adi,gc-max-dig-gain = <15>;
/* Manual Gain Control Setup */
//adi,mgc-rx1-ctrl-inp-enable; /* uncomment to use ctrl inputs */
//adi,mgc-rx2-ctrl-inp-enable; /* uncomment to use ctrl inputs */
adi,mgc-inc-gain-step = <2>;
adi,mgc-dec-gain-step = <2>;
/* adi,mgc-split-table-ctrl-inp-gain-mode:
* (relevant if adi,split-gain-table-mode-enable is set)
* 0 = AGC determine this
* 1 = only in LPF
* 2 = only in LMT
*/
adi,mgc-split-table-ctrl-inp-gain-mode = <0>;
/* Automatic Gain Control Setup */
adi,agc-attack-delay-extra-margin-us= <1>; /* us */
adi,agc-outer-thresh-high = <5>; /* -dBFS */
adi,agc-outer-thresh-high-dec-steps = <2>; /* 0..15 */
adi,agc-inner-thresh-high = <10>; /* -dBFS */
adi,agc-inner-thresh-high-dec-steps = <1>; /* 0..7 */
adi,agc-inner-thresh-low = <12>; /* -dBFS */
adi,agc-inner-thresh-low-inc-steps = <1>; /* 0..7 */
adi,agc-outer-thresh-low = <18>; /* -dBFS */
adi,agc-outer-thresh-low-inc-steps = <2>; /* 0..15 */
adi,agc-adc-small-overload-exceed-counter = <10>; /* 0..15 */
adi,agc-adc-large-overload-exceed-counter = <10>; /* 0..15 */
adi,agc-adc-large-overload-inc-steps = <2>; /* 0..15 */
//adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable;
adi,agc-lmt-overload-large-exceed-counter = <10>; /* 0..15 */
adi,agc-lmt-overload-small-exceed-counter = <10>; /* 0..15 */
adi,agc-lmt-overload-large-inc-steps = <2>; /* 0..7 */
//adi,agc-dig-saturation-exceed-counter = <3>; /* 0..15 */
//adi,agc-dig-gain-step-size = <4>; /* 1..8 */
//adi,agc-sync-for-gain-counter-enable;
adi,agc-gain-update-interval-us = <1000>; /* 1ms */
//adi,agc-immed-gain-change-if-large-adc-overload-enable;
//adi,agc-immed-gain-change-if-large-lmt-overload-enable;
/* Fast AGC */
adi,fagc-dec-pow-measurement-duration = <64>; /* 64 Samples */
//adi,fagc-allow-agc-gain-increase-enable;
adi,fagc-lp-thresh-increment-steps = <1>;
adi,fagc-lp-thresh-increment-time = <5>;
adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt = <8>;
adi,fagc-final-overrange-count = <3>;
//adi,fagc-gain-increase-after-gain-lock-enable;
adi,fagc-gain-index-type-after-exit-rx-mode = <0>;
adi,fagc-lmt-final-settling-steps = <1>;
adi,fagc-lock-level = <10>;
adi,fagc-lock-level-gain-increase-upper-limit = <5>;
adi,fagc-lock-level-lmt-gain-increase-enable;
adi,fagc-lpf-final-settling-steps = <1>;
adi,fagc-optimized-gain-offset = <5>;
adi,fagc-power-measurement-duration-in-state5 = <64>;
adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable;
adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll = <10>;
adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable;
adi,fagc-rst-gla-if-en-agc-pulled-high-mode = <0>;
adi,fagc-rst-gla-large-adc-overload-enable;
adi,fagc-rst-gla-large-lmt-overload-enable;
adi,fagc-rst-gla-stronger-sig-thresh-above-ll = <10>;
adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable;
adi,fagc-state-wait-time-ns = <260>;
adi,fagc-use-last-lock-level-for-set-gain-enable;
/* RSSI */
/* adi,rssi-restart-mode:
* 0 = AGC_IN_FAST_ATTACK_MODE_LOCKS_THE_GAIN,
* 1 = EN_AGC_PIN_IS_PULLED_HIGH,
* 2 = ENTERS_RX_MODE,
* 3 = GAIN_CHANGE_OCCURS,
* 4 = SPI_WRITE_TO_REGISTER,
* 5 = GAIN_CHANGE_OCCURS_OR_EN_AGC_PIN_PULLED_HIGH,
*/
adi,rssi-restart-mode = <3>;
//adi,rssi-unit-is-rx-samples-enable;
adi,rssi-delay = <1>; /* 1us */
adi,rssi-wait = <1>; /* 1us */
adi,rssi-duration = <1000>; /* 1ms */
/* Control Outputs */
adi,ctrl-outs-index = <0>;
adi,ctrl-outs-enable-mask = <0xFF>;
/* AuxADC Temp Sense Control */
adi,temp-sense-measurement-interval-ms = <1000>;
adi,temp-sense-offset-signed = <0xCE>;
adi,temp-sense-periodic-measurement-enable;
/* AuxDAC Control */
adi,aux-dac-manual-mode-enable;
adi,aux-dac1-default-value-mV = <0>;
//adi,aux-dac1-active-in-rx-enable;
//adi,aux-dac1-active-in-tx-enable;
//adi,aux-dac1-active-in-alert-enable;
adi,aux-dac1-rx-delay-us = <0>;
adi,aux-dac1-tx-delay-us = <0>;
adi,aux-dac2-default-value-mV = <0>;
//adi,aux-dac2-active-in-rx-enable;
//adi,aux-dac2-active-in-tx-enable;
//adi,aux-dac2-active-in-alert-enable;
adi,aux-dac2-rx-delay-us = <0>;
adi,aux-dac2-tx-delay-us = <0>;
};
};
&spi0 {
status = "okay";
};
// #include "adi-fmcomms2.dtsi"
&adc0_ad9361 {
en_agc-gpios = <&gpio 122 0>;
sync-gpios = <&gpio 123 0>;
reset-gpios = <&gpio 124 0>;
enable-gpios = <&gpio 125 0>;
txnrx-gpios = <&gpio 126 0>;
};
/ {
fpga-axi@0 {
interrupt-parent = <0x04>;
compatible = "simple-bus";
#address-cells = <0x01>;
#size-cells = <0x01>;
ranges = <0x00 0x00 0x00 0xffffffff>;
phandle = <0xac>;
// dma@9c400000 {
// compatible = "adi,axi-dmac-1.00.a";
// reg = <0x9c400000 0x10000>;
// #dma-cells = <0x01>;
// #clock-cells = <0x00>;
// interrupts = <0x00 0x6d 0x04>;
// clocks = <0x03 0x47>;
// phandle = <0x3c>;
// adi,channels {
// #size-cells = <0x00>;
// #address-cells = <0x01>;
// dma-channel@0 {
// reg = <0x00>;
// adi,source-bus-width = <0x40>;
// adi,source-bus-type = <0x02>;
// adi,destination-bus-width = <0x40>;
// adi,destination-bus-type = <0x00>;
// };
// };
// };
// dma@9c420000 {
// compatible = "adi,axi-dmac-1.00.a";
// reg = <0x9c420000 0x10000>;
// #dma-cells = <0x01>;
// #clock-cells = <0x00>;
// interrupts = <0x00 0x6c 0x04>;
// clocks = <0x03 0x47>;
// phandle = <0x3e>;
// adi,channels {
// #size-cells = <0x00>;
// #address-cells = <0x01>;
// dma-channel@0 {
// reg = <0x00>;
// adi,source-bus-width = <0x40>;
// adi,source-bus-type = <0x00>;
// adi,destination-bus-width = <0x40>;
// adi,destination-bus-type = <0x02>;
// };
// };
// };
sdr: sdr {
compatible ="sdr,sdr";
dmas = <&openwifi_ip_axi_dma_1 1
&openwifi_ip_axi_dma_0 0>;
dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
interrupt-names = "not_valid_anymore", "rx_pkt_intr", "tx_itrpt_useless", "tx_itrpt";
interrupts = <0 89 1 0 90 1 0 93 1 0 94 1>;
} ;
axidmatest_1: axidmatest@1 {
compatible ="xlnx,axi-dma-test-1.00.a";
dmas = <&openwifi_ip_axi_dma_1 0
&openwifi_ip_axi_dma_0 1>;
dma-names = "axidma0", "axidma1";
} ;
openwifi_ip_axi_bram_ctrl_0: axi_bram_ctrl@b0000000 {
clock-names = "s_axi_aclk";
clocks = <0x3 0x49>;
compatible = "xlnx,axi-bram-ctrl-4.1";
reg = <0x0 0xb0000000 0x0 0x80000>;
xlnx,bram-addr-width = <0x10>;
xlnx,bram-inst-mode = "EXTERNAL";
xlnx,ecc = <0x0>;
xlnx,ecc-onoff-reset-value = <0x0>;
xlnx,ecc-type = <0x0>;
xlnx,fault-inject = <0x0>;
xlnx,memory-depth = <0x10000>;
xlnx,rd-cmd-optimization = <0x1>;
xlnx,read-latency = <0x1>;
xlnx,s-axi-ctrl-addr-width = <0x20>;
xlnx,s-axi-ctrl-data-width = <0x20>;
xlnx,s-axi-id-width = <0x10>;
xlnx,s-axi-supports-narrow-burst = <0x1>;
xlnx,single-port-bram = <0x1>;
};
// tx_dma: dma@a0000000 {
// #dma-cells = <1>;
// clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
// clocks = <0x3 0x49>, <0x3 0x49>, <0x3 0x49>, <0x3 0x49>;
// compatible = "xlnx,axi-dma-1.00.a";
// interrupt-names = "mm2s_introut", "s2mm_introut";
// interrupts = <0 95 4 0 96 4>;
// reg = <0xA0000000 0x10000>;
// xlnx,addrwidth = <0x28>;
// xlnx,include-sg ;
// xlnx,sg-length-width = <0xe>;
// dma-channel@a0000000 {
// compatible = "xlnx,axi-dma-mm2s-channel";
// dma-channels = <0x1>;
// interrupts = <0 95 4>;
// xlnx,datawidth = <0x40>;
// xlnx,device-id = <0x0>;
// };
// dma-channel@A0000030 {
// compatible = "xlnx,axi-dma-s2mm-channel";
// dma-channels = <0x1>;
// interrupts = <0 96 4>;
// xlnx,datawidth = <0x40>;
// xlnx,device-id = <0x0>;
// };
// };
// rx_dma: dma@a0010000 {
// #dma-cells = <1>;
// clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
// clocks = <0x3 0x49>, <0x3 0x49>, <0x3 0x49>, <0x3 0x49>;
// compatible = "xlnx,axi-dma-1.00.a";
// //dma-coherent ;
// interrupt-names = "mm2s_introut", "s2mm_introut";
// interrupts = <0 91 4 0 92 4>;
// reg = <0xa0010000 0x10000>;
// xlnx,addrwidth = <0x28>;
// xlnx,include-sg ;
// xlnx,sg-length-width = <0xe>;
// dma-channel@a0010000 {
// compatible = "xlnx,axi-dma-mm2s-channel";
// dma-channels = <0x1>;
// interrupts = <0 91 4>;
// xlnx,datawidth = <0x40>;
// xlnx,device-id = <0x1>;
// };
// dma-channel@A0001030 {
// compatible = "xlnx,axi-dma-s2mm-channel";
// dma-channels = <0x1>;
// interrupts = <0 92 4>;
// xlnx,datawidth = <0x40>;
// xlnx,device-id = <0x1>;
// };
// };
tx_intf_0: tx_intf@a0060000 {
clock-names = "s00_axi_aclk", "s00_axis_aclk";//, "s01_axis_aclk", "m00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>;//, <0x3 0x49>, <0x3 0x49>;
compatible = "sdr,tx_intf";
interrupt-names = "tx_itrpt";
interrupts = <0 94 1>;
reg = <0xa0060000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
rx_intf_0: rx_intf@a0040000 {
clock-names = "s00_axi_aclk", "m00_axis_aclk";//, "s00_axis_aclk";
clocks = <0x3 0x49>, <0x3 0x49>;//, <0x3 0x49>;
compatible = "sdr,rx_intf";
interrupt-names = "not_valid_anymore", "rx_pkt_intr";
interrupts = <0 89 1 0 90 1>;
reg = <0xa0040000 0x10000>;
xlnx,s00-axi-addr-width = <0x7>;
xlnx,s00-axi-data-width = <0x20>;
};
openofdm_tx_0: openofdm_tx@a0030000 {
clock-names = "clk";
clocks = <0x3 0x49>;
compatible = "sdr,openofdm_tx";
reg = <0xa0030000 0x10000>;
};
openofdm_rx_0: openofdm_rx@a0020000 {
clock-names = "clk";
clocks = <0x3 0x49>;
compatible = "sdr,openofdm_rx";
reg = <0xa0020000 0x10000>;
};
xpu_0: xpu@a0070000 {
clock-names = "s00_axi_aclk";
clocks = <0x3 0x49>;
compatible = "sdr,xpu";
reg = <0xa0070000 0x10000>;
};
side_ch_0: side_ch@a0050000 {
clock-names = "s00_axi_aclk";
clocks = <0x3 0x49>;
compatible = "sdr,side_ch";
reg = <0xa0050000 0x10000>;
dmas = <&openwifi_ip_axi_dma_1 0
&openwifi_ip_axi_dma_0 1>;
dma-names = "rx_dma_mm2s", "tx_dma_s2mm";
};
cf-ad9361-lpc@99020000 {
compatible = "adi,axi-ad9361-6.00.a";
reg = <0x99020000 0x6000>;
// dmas = <0x3c 0x00>;
// dma-names = "rx";
spibus-connected = <0x3d>;
phandle = <0xad>;
};
cf-ad9361-dds-core-lpc@99024000 {
compatible = "adi,axi-ad9361-dds-6.00.a";
reg = <0x99024000 0x1000>;
clocks = <0x3d 0x0d>;arch/arm64/boot/dts/xilinx/adi-fmcomms2.dtsi
clock-names = "sampl_clk";
// dmas = <0x3e 0x00>;
// dma-names = "tx";
phandle = <0xae>;
};
// axi-sysid-0@85000000 {
// compatible = "adi,axi-sysid-1.00.a";
// reg = <0x85000000 0x10000>;
// phandle = <0xaf>;
// };
};
};
在ADI的内核下编译设备树。
我使用petalinux打包出来的UBOOT,替换掉Kuipler镜像里的。
但是我一直遇到一个问题,Petalinux直接打包出来的UBOOT,包括WIC镜像,放到SD卡上都不能直接启动,因为总是少一个boot.scr,这个文件在Petalinux工程的image/linux下可以找到。
替换openwif镜像里的uboot和设备树,启动之后按照openwifi的步骤启动。
接下来是到ADRV9002的迁移,比较简单的方法是先用ADI HDL里的ZCU102+ADRV9002的工程,然后改变器件型号、修改约束、添加openwifi。
按照ADI的步骤构建ZCU102+ADRV9002的Vivado工程:https://wiki.analog.com/resources/eval/user-guides/adrv9002/quickstart/zynqmp
把之前ZCU102+FMCS2的openwifi工程下的ip_repo拷贝一份,添加到工程的IP Repository里。
之后,从先前的openwifi的Vivado工程里,把Block Design的openwifi部分导出来:
write_bd_tcl -hier_blks [get_bd_cells /openwifi_ip] <some path>/exported_openwifi_block.tcl
然后再导入到ZCU102+ADRV9002的工程里:
source <some path>/exported_openwifi_block.tcl
到现在工程上的迁移完成了,之后的主要内容就是把原本AD9361接口连接到ADRV9001的IP上。
原工程的100M系统时钟实际上是从AD9361的CLK_I生成的,虽然rx_intf和tx_intf里把这个系统时钟和ADC的时钟视为两个时钟域,还增加了FIFO跨时钟域,实际上两个时钟还是一个时钟域。
因为收发的采样频率是一样的,因此
虽然在迁移的时候我希望让系统时钟来自PS,这样可以可以避免在配置ADRV9002的时候,影响其他模块的运行,但是测试了一下发现还是有问题,只能留待之后解决了。
在原工程里,AD9361的采样频率是40M,数据流是20M,接收和发送前分别做了2倍插值和抽样,在AD9361上配置FIR滤波器,将展宽的频率滤掉。这样应该是为了让频谱的两边更陡峭?
原本openwif为了降低TX的本振影响接收,因此只在需要TX的时候让本振工作,在驱动初始化完成之后,把控制AD9361的SPI移交给了PL,也就是xpu上的SPI接口,PL在有数据需要发送的时候,就通过SPI打开载频,不需要的时候就关掉。
因为ADRV9002提供了直接控制前端开关的PIN,所以不需要再像上面这样周折,这里可以修改xpu把里面指示TX的信号拉出来,SPI的接口就不用了。
当然这样也会引发一些问题,后面会涉及。
OpenWIFI里是通过设定AD9361上和ControlOutput相关的Pin输出AGC增益值,ADRV9002也提供了类似的方式,ADRV9002上有10个DGPIO,可以通过配置DGPIO输出当前AGC增益值。
还有一点,AD9361的AGC增益步进是1dB,ADRV9002的AGC增益步进是0.5dB,openwifi内计算RSSI数值的单位是0.5dB,这样需要修改一下xpu叠加信号RSSI数值和AGC增益数值部分。
除了sdr.c之外,其他的模块基本都不用修改,因为它们只和PL有关。
原来驱动工程的编译有些让人迷惑,仔细看了看我又把包含头文件的宏重新加进去。
软件部分只涉及初始的配置,Linux里面802.11传输层已经是一套很完整的系统了,这里的驱动主要是把上层来的MAC帧传进PL,让PL调制发送出去,另外就是频率切换也是由上层下发的,根据上层给的频段配置收发器,
这里就是把AD9361的API调用换成ADRV9002,
ADRV9002需要配置:
因为原本AD9361用了在发送时把20M的信号插值成40M,需要配置FIR,这里因为直接20M输出不再插值,就免去了FIR的配置。(ADRV9002因为支持多Profile,FIR配置也变得复杂了,不必自找麻烦)
使用AD9361时,可以直接从IIO的节点配置AGC模式为Fast Attack,而ADRV9002则是将超越门限的计数器配置为ByPass以工作为Fast Attack。
原本上层驱动配置完AD9361后,会让FPGA用SPI去控制AD9361 DAC的前端开关,因此配置ADRV9002的TX为PIN控制。
以及调试过程中发现一直收不到ACK,也是因为等待ACK的计数器超时,花了很长时间DEBUG才追溯到这儿,
最后还要试一下参数,因为器件变化,涉及增益和时序部分都会变化。
测试办法:用另一台正常运行openwifi的设备(LibreSDR)用openwifi提供的inject_80211程序互相发送帧,首先通过ILA看信号是不是正确解码,如果解码是对的,再看能不能收到对方ACK、收到ACK后能不能正确识别。
测试时需要调到一个2.4G/5.8G以外的频率。
./sdrctl dev sdr0 set reg rf 1 2200 # 接收频率2200MHz
./sdrctl dev sdr0 set reg rf 5 2200 # 发送频率2200MHz
./sdrctl dev sdr0 set reg drv_rx 7 3 # 驱动输出RX调试信息
./sdrctl dev sdr0 set reg drv_tx 7 3 # 驱动输出TX调试信息
能够成果接受和发送ACK是两个设备之间通信的关键,openwifi里的tx_intf在发送一个物理帧之后,还会向上层传递一个中断,也就是下面的openwifi_tx_interrupt
,后面信息的pass就表示刚刚发出去的这一帧有没有收到ACK,
[ 1127.928551] sdr,sdr openwifi_tx: 92B RC8 65M FC00b0 DI0000 ADDR6655443322ac/665544332260/6655443322ac flag40010099 QoSd9 SC2145_0 retr0 ack1 prio0 q0 wr10 rd9
[ 1127.943091] sdr,sdr openwifi_tx_interrupt: tx_result [nof_retx 1 pass 0] SC2145 prio0 q0 wr11 rd10 num_slot0 cw0 hwq len00000000 no_room_flag0 0
[nof_retx 1 pass 0]
这样是ACK没有收到。
如果从频谱仪上可以抓到发出来的ACK的波形,并且ADC的接口上也可以看到同样的波形的话,要么是没有解调出来,要么是解调出来了,但是已经超过了等待ACK的延时。
虽然是按照FDD模式配置的,两发两收是可以独立工作的,实际作为WiFI协议工作的时候仍然是时分的,rx_intf模块在发射信号的时候会把IQ置零,但这样不能阻止前端TX串进RX,所以RX刚打开的时候会有一小段直流,这段直流还没有研究是天线的问题还是ADC的问题,虽然不确定会不会影响到解调,但是这个过程会影响到AGC,因此后来我选择通过引脚去关掉ADC(和开关DAC一样的操作,当然二者在时间上是错开的)而不仅仅只是把数字信号置零。
因为ADRV9002能够直接用PIN控制前端开关,速度比AD9361要快一些,因此需要调一下控制发送部分的几个延时参数(XPU的寄存器10)
./sdrctl dev sdr0 set reg xpu 10 0x18040048
[ 7: 0] BB RF delay:
[14: 8] RF end ext time: 数据结束到TX关闭
[22:16]
之所以PIN的信号比数据快,主要是因为数据从基带里出来之后,还要过一些fifo后再转换到LVDS的接口,在ADRV9002内还有一段数据接口的延迟。严格地来的话,应该让PIN上的控制信号和数字信号同步。
这是调整OFDM解调的RSSI门限,
./sdrctl dev sdr0 set reg rx 2 $((16#0040005e))
最初以AP模式工作时,发现其他设备几乎无法连接,上层一直报IEEE 802.11: did not acknowledge authentication response
,最后用ILA看xpu里的tx_control模块的状态机,发现是ACK帧还没解出来的时候,等待ACK已经超时了,这个超时值也可以通过xpu的16、17号寄存器设置(bit30-16: timeout for PHY header detection):
reg_idx | meaning | comment |
---|---|---|
… | … | … |
16 | setting when wait for ACK in 2.4GHz | unit 0.1us. bit14-0: OFDM decoding timeout (after detect PHY header), bit30-16: timeout for PHY header detection, bit31: 0: FCS valid is not needed for ACK packet, 1: FCS valid is needed for ACK packet |
17 | setting when wait for ACK in 5GHz | unit 0.1us. bit14-0: OFDM decoding timeout (after detect PHY header), bit30-16: timeout for PHY header detection, bit31: 0: FCS valid is not needed for ACK packet, 1: FCS valid is needed for ACK packet |
xpu.c里配置的值:
xpu_api->XPU_REG_RECV_ACK_COUNT_TOP0_write( (1<<31) | (((51+2+2)*10 + 15)<<16) | 10 );//2.4GHz. extra 300 clocks are needed when rx core fall into fake ht detection phase (rx mcs 6M)
xpu_api->XPU_REG_RECV_ACK_COUNT_TOP1_write( (1<<31) | (((51+2+2)*10 + 15)<<16) | 10 );//5GHz. extra 300 clocks are needed when rx core fall into fake ht detection phase (rx mcs 6M)
调高延时,直到ACK能收到(pass 1
)。
[ 4968.183402] sdr,sdr openwifi_tx_interrupt: tx_result [nof_retx 1 pass 1] SC2145 prio0 q0 wr56 rd55 num_slot0 cw0 hwq len00000000 no_room_flag0 1
这部分其实是调SIFS(Short Interframe Space)。系统时钟和原工程是一样的100M,不知是哪个环节引起的偏差。
接收的SIFS有问题,发送的多半也逃不掉。
这部分调好之后,用手机可以连接热点了,但是连接距离十分有限,iperf测速也只有十几Mbps的样子,丢包很严重,和AD9361上还有差距。感觉是个比较棘手的问题。
因为rootfs直接用的zcu102的ADI Kuipler镜像,而内核因为修改过,rootfs里的modules需要全部重新编译,否则一些网络功能比如iptables是缺失的,这个因为比较繁琐就先略过了。
Zion National Park
reg_idx | meaning | comment |
---|---|---|
0 | reset | each bit is connected to rx_intf.v internal sub-module. 1 – reset; 0 – normal |
1 | trigger for ILA debug | bit4 and bit0. Please check slv_reg1 in rx_intf.v |
2 | enable/disable rx interrupt | 256(0x100):disable, 0:enable |
3 | get loopback I/Q from tx_intf | 256(0x100):from tx_intf, 0:from ad9361 ADC |
4 | baseband clock and IQ fifo in/out control | no use anymore – for old bb rf independent mode |
5 | control/config dma to cpu | check rx_intf.v slv_reg5 |
6 | abnormal packet length threshold | bit31-16 to store the threshold. if the packet length is not in the range of 14 to threshold, terminate the dma to cpu |
7 | source selection of rx dma to cpu | check rx_intf.v slv_reg7 |
8 | reserved | reserved |
9 | number of dma symbol to cpu | only valid in manual mode (slv_reg5[5]==1). normally the dma is set automatically by the received packet length |
10 | rx adc fifo reading control | check rx_intf.v slv_reg10 |
11 | rx digital I/Q gain | number of bit shift to left. default 4 in rx_intf.c: rx_intf_api->RX_INTF_REG_BB_GAIN_write(4)。 digital gain看来对后面解调影响很大,AD9361是12bit所以往高位移四位是正常的,但我想充分利用ADRV9002的16bit精度,把他设为0之后,同样的信号ofdm解调效果就差了很多,于是暂时又改回了4。(但是用MATLA的WLAN工具解调,无论移位基本不影响解调效果,所以我想openofdm可能还有改进的空间) |
上述部分就是整个迁移过程了,总共花了将近一个月的时间。我发现许多围绕openwifi的活动是把openwifi塞进更廉价硬件中,当然也无非是ZYNQ7000+AD936x的组合,像这样往更昂贵的SoC和射频芯片上迁移的反而比较少。openwifi主要目的也不是为了通信,在wifi感知、嗅探之类的地方用的比较多,换成更好的SoC和射频芯片的话,做感知、测量之类的话,计算性能和精度都会提升。
换成ADRV9002之后,至少Beacon的覆盖范围已经赶得上一般的家用Wi-Fi了,但是接收的效果却很不理想,跟手机设备通信也很不稳定,只能慢慢想办法调整了。
评估了一下迁移到Petalinux的工程量,openwifi这里如果802.11的API没变的话应该不会太麻烦。