diff --git a/ci-scripts/xml_files/gnb_usrp_build.xml b/ci-scripts/xml_files/gnb_usrp_build.xml index f67098116b..db6450b259 100644 --- a/ci-scripts/xml_files/gnb_usrp_build.xml +++ b/ci-scripts/xml_files/gnb_usrp_build.xml @@ -40,7 +40,7 @@ TesteNB Build_eNB Build gNB (USRP) - --gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_t2 --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c + --gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_aal --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c True diff --git a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml index 15b8921a5a..f82c54e8de 100644 --- a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml +++ b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml @@ -62,7 +62,7 @@ true nr_ulsim 100 - -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -80,7 +80,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -98,7 +98,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -116,7 +116,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -134,7 +134,7 @@ true nr_ulsim 350 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -152,7 +152,7 @@ true nr_ulsim 550 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -170,7 +170,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -188,7 +188,7 @@ true nr_ulsim 650 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -206,7 +206,7 @@ true nr_ulsim 1100 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 diff --git a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml index 7feb93741a..9b7a84c51f 100644 --- a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml +++ b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml @@ -63,7 +63,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -81,7 +81,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -99,7 +99,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -117,7 +117,7 @@ true nr_dlsim 150 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -135,7 +135,7 @@ true nr_dlsim 250 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -153,7 +153,7 @@ true nr_dlsim 400 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -171,7 +171,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -189,7 +189,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -207,7 +207,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -225,7 +225,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -243,7 +243,7 @@ true nr_dlsim 300 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -261,6 +261,6 @@ true nr_dlsim 450 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 0b6f31d930..292e8d0575 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -45,7 +45,7 @@ BUILD_DOXYGEN=0 DISABLE_HARDWARE_DEPENDENCY="False" CMAKE_BUILD_TYPE="RelWithDebInfo" CMAKE_CMD="$CMAKE" -OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 ldpc_xdma websrv oai_iqplayer imscope imscope_record" +OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record" TARGET_LIST="" BUILD_TOOL_OPT="-j$(nproc)" diff --git a/doc/BUILD.md b/doc/BUILD.md index 4d629007b0..8298204859 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -185,7 +185,7 @@ Some libraries have further dependencies and might not build on every system: - `enbscope`, `uescope`, `nrscope`: libforms/X - `ldpc_cuda`: CUDA - `websrv`: npm and others -- `ldpc_t2`: DPDK with patch +- `ldpc_aal`: DPDK with patch # Running `cmake` directly diff --git a/doc/LDPC_OFFLOAD_SETUP.md b/doc/LDPC_OFFLOAD_SETUP.md new file mode 100644 index 0000000000..cabac2cf39 --- /dev/null +++ b/doc/LDPC_OFFLOAD_SETUP.md @@ -0,0 +1,344 @@ + + + + + +
+ + + + + + OAI LDPC offload (O-RAN AAL/DPDK BBDEV) +
+ +**Table of Contents** + +[[_TOC_]] + +This documentation describes the integration of LDPC coding for lookaside acceleration using O-RAN AAL/DPDK BBDEV in OAI, along with its usage. +For details on the implementation, please consult the [developer notes](../openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md). + +# Requirements + +In principle, any lookaside LDPC accelerator supporting the O-RAN AAL/DPDK BBDEV should work. +However, the current implementation has only been validated for the Xilinx T2, Intel ACC100, and Intel ACC200 (VRB1). +Therefore, your mileage may vary when using other BBDEV devices as there may be some hardware-specific changes required -- contributions are welcome! + +## DPDK Version Requirements + +The following DPDK versions are supported: +- For the Xilinx T2 card, DPDK20.11+ is supported. +- As for the Intel ACC100/ACC200, only DPDK22.11+ is supported. + +## Tested Devices/ DPDK versions + +### Xilinx T2 + +- DPDK20.11.9*. +- DPDK22.11.7*. +> Note: FPGA bitstream image and the corresponding patch file (e.g., `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` for DPDK20.11) from Accelercomm required. + +### Intel ACC100 + +- DPDK22.11.7*. +- DPDK23.11.3*. +- DPDK24.11.2. +> Note: [Patch]((https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd)) required for pre-DPDK24.11 versions when using the Intel ACC100. + +### Intel ACC200 (also known as VRB1) +- DPDK22.11.7. +- DPDK23.11.3. +- DPDK24.11.2. + +# System Setup +## DPDK installation + +> Important: +> - If you are using the Xilinx T2 card, you will need to apply the vendor-supplied patches before compiling DPDK. +> - If you are using the Intel ACC100, you will need to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver if you are using DPDK22.11 or DPDK23.11. + + +Refer to the guide [here](./ORAN_FHI7.2_Tutorial.md?ref_type=heads#dpdk-data-plane-development-kit) to install, and then validate your DPDK installation. + +
+ Notes on DPDK patching/installation for Xilinx T2. + +*Note: The following instructions apply to `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch`, compatible with DPDK 20.11.9. For older patches (e.g., `ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch`), refer to the T2 documentation in `2023.w48`.* + +```bash +# Get DPDK source code +git clone https://github.com/DPDK/dpdk-stable.git ~/dpdk-stable +cd ~/dpdk-stable +git checkout v20.11.9 +git apply ~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch +``` +Replace `~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` by patch file provided by +Accelercomm. + +If you would like to install DPDK to a custom directory, here is an example. +```bash +cd ~/dpdk-stable +# meson setup build +meson setup --prefix=/opt/dpdk-t2 build # for installation with non-default installation prefix +cd build +ninja +sudo ninja install +sudo ldconfig + +``` +
+ +## System configuration + +### Setting up Hugepages + +First, we must setup hugepages on the system. +In our setup, we setup 16 of the 1G hugepages. +Apart from 1G, 2MB hugepages works too, but make sure to allocate a sufficient number of them. + +``` +# sudo dpdk-hugepages.py -p 1G --setup 16G +``` + +### Locating the Accelerator + +Next, we check whether our system can detect our accelerator using `dpdk-devbind.py`. +You should see Baseband devices detected by DPDK, as follows: +``` +# sudo dpdk-devbind.py -s +... +Baseband devices using DPDK-compatible driver +============================================= +0000:f7:00.0 'Device 57c0' unused=vfio-pci +... +``` + +As you can see here, our Intel ACC200 has the address of `0000:f7:00.0`. +Depending on the accelerator you are using, the address may vary. + +### Loading VFIO-PCI and enabling SR-IOV +Following, make sure to load the `vfio-pci` kernel modules and ensure that SR-IOV is enabled. + +``` +# sudo modprobe vfio-pci enable_sriov=1 disable_idle_d3=1 +``` + +### Binding the Accelerator with `vfio-pci` + +Lastly, we bind our accelerator with the `vfio-pci` driver. +``` +# sudo dpdk-devbind.py --bind=vfio-pci 0000:f7:00.0 +``` + +> Note: For the Xilinx T2, we can use this device directly. +If you use an Intel vRAN accelerator, read on. + +### Additional Steps for Intel vRAN Accelerators + +> IMPORTANT NOTE: +> - Currently, we only support using the Virtual Functions (VFs) of the Intel vRAN accelerators, but not the Physical Function (PF). +> - One key advantage of using VFs is that this allows us to share the accelerator with other DU instances on the same machine, which is common in practice. + +If you are using an Intel vRAN accelerator, you will need to use the [pf_bb_config](https://github.com/intel/pf-bb-config) tool to configure the accelerator beforehand. + +#### pf_bb_config +For more details, please consult the `pf_bb_config` README. + +``` +# git clone https://github.com/intel/pf-bb-config +# cd ~/pf-bb-config +# ./build.sh +``` +This clones and builds the `pf_bb_config` binary. + +Next, we show an example for the Intel ACC200. +We use an existing configuration located at `./vrb1/vrb1_config_16vf.cfg`. + +Here, it is necessary to specify a VFIO token (in this case, we use the UUID `00112233-4455-6677-8899-aabbccddeeff`). +Note that in practice, a random UUID should be used. +``` +# sudo ./pf_bb_config VRB1 -v 00112233-4455-6677-8899-aabbccddeeff -c vrb1/vrb1_config_16vf.cfg +== pf_bb_config Version v25.01-0-g812e032 == +VRB1 PF [0000:f7:00.0] configuration complete! +Log file = /var/log/pf_bb_cfg_0000:f7:00.0.log +``` + +#### Creating VFs + +Finally, we create the VF(s) for our accelerator. +In this example, we only create one SR-IOV VF. +``` +# echo 1 | sudo tee /sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs +``` + +If you encounter any error when creating the VF(s), e.g., `tee: '/sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs': No such file or directory`, then try enabling SR-IOV again. +``` +# echo 1 | sudo tee /sys/module/vfio_pci/parameters/enable_sriov +``` + +After you have successfully created the VF, you should see an additional baseband device, in our case, it is `0000:f7:00.1`. +We will use this device with OAI later. +The newly created VF should also be using the same `vfio-pci` driver as the PF, if it is not, you will need to do a `dpdk-devbind.py` to bind it with `vfio-pci`. +``` +# sudo dpdk-devbind.py -s +... +Baseband devices using DPDK-compatible driver +============================================= +0000:f7:00.0 'Device 57c0' drv=vfio-pci unused= +0000:f7:00.1 'Device 57c0' drv=vfio-pci unused= +... +``` + +# Building OAI with ORAN-AAL +OTA deployment is precisely described in the following tutorial: +- [NR_SA_Tutorial_COTS_UE](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_COTS_UE.md) +Instead of section *3.2 Build OAI gNB* from the tutorial, run the following commands: + +```bash +# Get openairinterface5g source code +git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g +cd ~/openairinterface5g +git checkout develop + +# Install OAI dependencies +cd ~/openairinterface5g/cmake_targets +./build_oai -I + +# Build OAI gNB +cd ~/openairinterface5g +source oaienv +cd cmake_targets +./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_aal" -C +``` + +A shared object file `libldpc_aal.so` will be created during the compilation. +This object is conditionally compiled. +The selection of the library to compile is done using `--build-lib ldpc_aal`. + +> Note: The required DPDK poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to building OAI. + +# O-RAN AAL DPDK EAL parameters +To configure O-RAN AAL/DPDK BBDEV, you can set the following parameters via the command line of PHY simulators or nr-softmodem: + +> Note: the group parameter name has been renamed from `nrLDPC_coding_t2` to +> `nrLDPC_coding_aal` to better reflect that it is a generic AAL accelerator +> card. + +- `nrLDPC_coding_aal.dpdk_dev` - **mandatory** parameter, specifies the PCI address of our accelerator. It must follow the format `WWWW:XX:YY.Z`. + +- `nrLDPC_coding_aal.dpdk_core_list` - **mandatory** parameter, specifies the CPU cores assigned to DPDK . +Ensure that the CPU cores specified in `nrLDPC_coding_aal.dpdk_core_list` are available and not used by other processes to avoid conflicts. + +- `nrLDPC_coding_aal.dpdk_prefix` - optional parameter, DPDK shared data file prefix, by default set to *b6*. + +- `nrLDPC_coding_aal.vfio_vf_token` - optional parameter, VFIO token set for the VF, if applicable. + +- `nrLDPC_coding_aal.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased). + +- `nrLDPC_coding_aal.is_t2` - optional parameter, set this to 1 when using the Xilinx T2 card. + +**Note:** These parameters can also be provided in a configuration file. +Example for the ACC200: +``` +nrLDPC_coding_aal : { + dpdk_dev : "0000:f7:00.1"; + dpdk_core_list : "14-15"; + vfio_vf_token: "00112233-4455-6677-8899-aabbccddeeff"; +}; + +loader : { + ldpc : { + shlibversion : "_aal"; + }; +}; +``` + +# Running OAI with O-RAN AAL + +In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _aal` option. +Reminder, if you are using the Xilinx T2 card, make sure to set `--nrLDPC_coding_aal.is_t2 1`. + +## 5G PHY simulators + +### nr_ulsim + +Example command: +```bash +cd ~/openairinterface5g +source oaienv +cd cmake_targets/ran_build/build +sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +``` +### nr_dlsim + +Example command: +```bash +cd ~/openairinterface5g +source oaienv +cd cmake_targets/ran_build/build +sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +``` + +## OTA test + +### Running OAI gNB with USRP B210/FHI72 + +Example command: +```bash +cd ~/openairinterface5g +source oaienv +cd cmake_targets/ran_build/build +sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 14-15 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +``` + +# Known Issue(s) + +## BBDEV CPU Usage + +When running the E2E setup (this applies to both USRP and FHI72), BBDEV may not be using the list of CPU cores as specified by `nrLDPC_coding_aal.dpdk_core_list` accordingly. +This is an issue under investigation, and subject for future fixes. +In the meantime, we recommend allocating idle, and isolated CPU cores in the configuration for BBDEV. + +## Potential Low Throughput + +The current implementation has been tested to work in an end-to-end setup and is functional. +However, there are still opportunities for optimization, particularly in LDPC decoding performance, which is an area of ongoing improvement. +As such, downlink/uplink throughput may be suboptimal with the default configurations, but enhancements are actively being explored. + +To achieve better E2E performance with the current implementation, we recommend the following adjustments: +1. Increasing the number of LDPC decoding iterations of the L1, e.g., `max_ldpc_iterations` to 200. +2. Increasing the BLER targets of the MAC scheduler. + +Example configuration snippet: +``` +... +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 180; + pucch_TargetSNRx10 = 220; + dl_bler_target_upper = .35; + dl_bler_target_lower = .15; + ul_bler_target_upper = .35; + ul_bler_target_lower = .15; + pusch_FailureThres = 1000; + ul_max_mcs = 28; + +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 100; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + max_ldpc_iterations = 200; +} +); +... +``` diff --git a/doc/LDPC_T2_OFFLOAD_SETUP.md b/doc/LDPC_T2_OFFLOAD_SETUP.md deleted file mode 100644 index 24d36e39cf..0000000000 --- a/doc/LDPC_T2_OFFLOAD_SETUP.md +++ /dev/null @@ -1,158 +0,0 @@ - - - - - -
- - - - - - OAI T1/T2 LDPC offload -
- -**Table of Contents** - -[[_TOC_]] - -This documentation aims to provide a tutorial for AMD Xilinx T2 Telco card integration into OAI and its usage. - -# Requirements - - - bitstream image and PMD driver for the T2 card provided by AccelerComm - - DPDK 20.11.9 with patch from Accelercomm: version ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch - - tested on RHEL7.9, RHEL9.2, Ubuntu 22.04 - -# DPDK setup -## DPDK installation -*Note: Following instructions are valid for ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch version, which is compatible with DPDK 20.11.9. Installation steps, which should be followed for older versions of the patch file (for example ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch) are present in older version of this documentation, under the tag 2023.w48.* - -``` -# Get DPDK source code -git clone https://github.com/DPDK/dpdk-stable.git ~/dpdk-stable -cd ~/dpdk-stable -git checkout v20.11.9 -git apply ~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch -``` -Replace `~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` by patch file provided by -Accelercomm. -``` -cd ~/dpdk-stable -meson setup build -# meson setup --prefix=/opt/dpdk-t2 build for installation with non-default installation prefix -cd build -ninja -sudo ninja install -sudo ldconfig -``` -## DPDK configuration - - load required kernel module -``` -sudo modprobe vfio-pci -``` - - check presence of the card and its PCI addres on the host machine -``` -lspci | grep "Xilinx" -``` - - bind the card with vfio-pci driver -``` -sudo python3 ~/dpdk-stable/usertools/dpdk-devbind.py --bind=vfio-pci 41:00.0 -``` -Replace PCI address of the card *41:00.0* by address detected by *lspci | grep "Xilinx"* command - - hugepages setup (10 x 1GB hugepages) -``` -sudo python3 ~/dpdk-stable/usertools/dpdk-hugepages.py -p 1G --setup 10G -``` - -*Note: device binding and hugepages setup has to be done after every reboot of -the host machine* - -# Modifications in the OAI code -## DPDK lib and PMD path specification -If DPDK library was installed into custom path, you have to point to the right directory with `PKG_CONFIG_PATH`, prior to the OAI build. Sample command to set the DPDK path to `/opt/dpdk-t2/lib64/pkgconfig/`: -``` -export PKG_CONFIG_PATH=/opt/dpdk-t2/lib64/pkgconfig/:$PKG_CONFIG_PATH -``` - -# OAI Build -OTA deployment is precisely described in the following tutorial: -- [NR_SA_Tutorial_COTS_UE](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_COTS_UE.md) -Instead of section *3.2 Build OAI gNB* from the tutorial, run following commands: - -``` -# Get openairinterface5g source code -git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g -cd ~/openairinterface5g -git checkout develop - -# Install OAI dependencies -cd ~/openairinterface5g/cmake_targets -./build_oai -I - -# Build OAI gNB -cd ~/openairinterface5g -source oaienv -cd cmake_targets -./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_t2" -C -``` - -Shared object file *libldpc_t2.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using *--build-lib ldpc_t2*. - -*Required poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to the build of OAI* - -# Setup of T2-related DPDK EAL parameters -To configure T2-related DPDK Environment Abstraction Layer (EAL) parameters, you can set the following parameters via the command line of PHY simulators or softmodem: -- `nrLDPC_coding_t2.dpdk_dev` - **mandatory** parameter, specifies PCI address of the T2 card. PCI address of the T2 card can be detected by `lspci | grep "Xilinx"` command. -- `nrLDPC_coding_t2.dpdk_core_list` - **mandatory** parameter, specifies CPU cores assigned to DPDK for T2 processing. Ensure that the CPU cores specified in *nrLDPC_coding_t2.dpdk_core_list* are available and not used by other processes to avoid conflicts. -- `nrLDPC_coding_t2.dpdk_prefix` - DPDK shared data file prefix, by default set to *b6*. - -**Note:** These parameters can also be provided in a configuration file: -``` -nrLDPC_coding_t2 : { - dpdk_dev : "41:00.0"; - dpdk_core_list : "14-15"; -}; - -loader : { - ldpc : { - shlibversion : "_t2"; - }; -}; -``` - -# 5G PHY simulators - -## nr_ulsim test -Offload of the channel decoding to the T2 card is in nr_ulsim specified by *--loader.ldpc.shlibversion _t2* option. Example command for running nr_ulsim with LDPC decoding offload to the T2 card: -``` -cd ~/openairinterface5g -source oaienv -cd cmake_targets/ran_build/build -sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 -``` -## nr_dlsim test -Offload of the channel encoding to the AMD Xilinx T2 card is in nr_dlsim specified by *-c* option. Example command for running nr_dlsim with LDPC encoding offload to the T2 card: -``` -cd ~/openairinterface5g -source oaienv -cd cmake_targets/ran_build/build -sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 -``` - -# OTA test -Offload of the channel encoding and decoding to the AMD Xilinx T2 card is enabled by *--loader.ldpc.shlibversion _t2* option. - -## Run OAI gNB with USRP B210 -``` -cd ~/openairinterface5g -source oaienv -cd cmake_targets/ran_build/build -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 -``` - -# Limitations -## AMD Xilinx T2 card - - functionality of the LDPC encoding and decoding offload verified in OTA SISO setup with USRP N310 and Quectel RM500Q, blocking of the card reported for MIMO setup (2 layers) - -*Note: AMD Xilinx T1 Telco card is not supported anymore.* diff --git a/doc/README.md b/doc/README.md index 27c2688673..40f838750a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -64,7 +64,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https:// - [How to run with E2 agent](../openair2/E2AP/README.md) - [How to run the physical simulators](./physical-simulators.md) - [How to setup OAI with Nvidia Aerial and Foxconn](./Aerial_FAPI_Split_Tutorial.md) -- [How to setup OAI with AMD T2 Telco card](./LDPC_T2_OFFLOAD_SETUP.md) +- [How to setup OAI with LDPC accelerators (Xilinx T2/Intel ACCs)](./LDPC_OFFLOAD_SETUP.md) - [How to do a handover](./handover-tutorial.md) - [How to setup gNB frequency](./gNB_frequency_setup.md) diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index 0fae4e1574..d537a8a299 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -28,35 +28,35 @@ shlib_path libldpc.so `libldpc.so` has its decoder implemented in [nrLDPC_coding_segment_decoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c).\ Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c). -loading `libldpc_t2.so` instead of `libldpc.so`: +loading `libldpc_aal.so` instead of `libldpc.so`: -`make ldpc_t2` +`make ldpc_aal` -This command creates the `libldpc_t2.so` shared library. +This command creates the `libldpc_aal.so` shared library. ``` -Building C object CMakeFiles/ldpc_t2.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c.o -Linking C shared module libldpc_t2.so +Building C object CMakeFiles/ldpc_aal.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c.o +Linking C shared module libldpc_aal.so ``` -At runtime, to successfully use the T2 board, you need to install vendor specific drivers and tools.\ -Please refer to the dedicated documentation at [LDPC_T2_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_T2_OFFLOAD_SETUP.md). +At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools. +Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_OFFLOAD_SETUP.md). -`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1` +`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 01:00.0 --nrLDPC_coding_aal.dpdk_core_list 0-1` ``` ....................... [CONFIG] loader.ldpc.shlibversion set to default value "" [LIBCONFIG] loader.ldpc: 2/2 parameters successfully set, (1 to default value) -[CONFIG] shlibversion set to _t2 from command line +[CONFIG] shlibversion set to _aal from command line [CONFIG] loader.ldpc 1 options set from command line -shlib_path libldpc_t2.so -[LOADER] library libldpc_t2.so successfully loaded +shlib_path libldpc_aal.so +[LOADER] library libldpc_aal.so successfully loaded ........................ ``` -`libldpc_t2.so` has its decoder and its encoder implemented in [nrLDPC_coding_t2.c](file://../nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c). +`libldpc_aal.so` has its decoder and its encoder implemented in [nrLDPC_coding_aal.c](file://../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c). loading `libldpc_xdma.so` instead of `libldpc.so`: @@ -103,7 +103,7 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version> `libldpc.so` is completed. -`libldpc_t2.so` is completed. +`libldpc_aal.so` is completed. `libldpc_xdma.so` is completed. diff --git a/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt index e60ca479bb..356aa5f2a0 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt @@ -1,3 +1,3 @@ add_subdirectory(nrLDPC_coding_segment) add_subdirectory(nrLDPC_coding_xdma) -add_subdirectory(nrLDPC_coding_t2) +add_subdirectory(nrLDPC_coding_aal) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt new file mode 100644 index 0000000000..1bad804fba --- /dev/null +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt @@ -0,0 +1,36 @@ +########################################################## +# LDPC offload library - O-RAN AAL/DPDK BBDEV +########################################################## + +add_boolean_option(ENABLE_LDPC_AAL OFF "Build support for LDPC Offload using O-RAN AAL library" OFF) + +if (ENABLE_LDPC_AAL) + pkg_check_modules(LIBDPDK_AAL REQUIRED libdpdk>=20.11.9) + + find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_AAL_LIBRARY_DIRS}) + if (PMD_T2) + message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") + else() + if (LIBDPDK_AAL_VERSION VERSION_LESS "22.11") + message(FATAL_ERROR "for non-T2 BBDEV PMDs, we support the Intel ACC100 and ACC200 (VRB1), using DPDK 22.11 or later.") + endif() + endif() + + add_library(ldpc_aal MODULE nrLDPC_coding_aal.c) + + set_target_properties(ldpc_aal PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API") + set_target_properties(ldpc_aal PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + target_include_directories(ldpc_aal PRIVATE ${LIBDPDK_AAL_INCLUDE_DIRS}) + target_link_libraries(ldpc_aal PRIVATE ldpc_gen_HEADERS ${LIBDPDK_AAL_LDFLAGS}) + if (PMD_T2) + target_link_libraries(ldpc_aal PRIVATE ${PMD_T2}) + endif() + + add_dependencies(nr-softmodem ldpc_aal) + add_dependencies(nr-uesoftmodem ldpc_aal) + add_dependencies(nr_ulsim ldpc_aal) + add_dependencies(nr_ulschsim ldpc_aal) + add_dependencies(nr_dlsim ldpc_aal) + add_dependencies(nr_dlschsim ldpc_aal) + +endif() diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md new file mode 100644 index 0000000000..140fff5f1e --- /dev/null +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md @@ -0,0 +1,143 @@ +This document highlights some of the key aspects of the implementation for O-RAN AAL/DPDK BBDEV; for user documentation, please consult the [setup guide](../../../../../doc/LDPC_OFFLOAD_SETUP.md) instead. + +## Overview + +The current implementation supports the following BBDEV devices and respective DPDK versions: +- Xilinx T2 on DPDK20.11+ +- Intel ACC100* on DPDK22.11+ +- Intel ACC200 (VRB1) on DPDK22.11+ + +> Important notes: +> - For the Intel ACC100, for DPDK22.11 and DPDK23.11, users are required to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver. +> - If you intend to use this implementation in an FHI7.2 setup, only the F-release and beyond is supported. + +## Implementation Details + +Below, we highlight some of the important details in this implementation. + +### HARQ + +To maintain the HARQ buffers in the LDPC decoding process, the BBDEV either uses its internal memory (not visible to DPDK), or using external memory (i.e., DDR) managed by OAI. +For the Xilinx T2 and Intel ACC100, they use the internal memory while the Intel ACC200 uses the DDR memory. + +If internal memory is supported by the device, we will set `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE` and `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE`. + +The following two fields are important for HARQ: `harq_combined_input` and `harq_combined_output`. + +For the first transmission, there is no HARQ input. +Thus, the flag `RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE` is not set. +However, there will be a HARQ output, so we set the flag `RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE`. +We also set the corresponding fields for `harq_combined_output`. + +For the second transmission (or subsequent retransmissions), we need to provide a HARQ input at the same time, there will also be a HARQ output. +Therefore, both fields, `harq_combined_input` and `harq_combined_output` must be set accordingly. + +As HARQ requires the output of the previous transmission/ HARQ round as its input, we need to maintain a separate variable to hold the previous round's information. +To do so, we define `harq_buffers` (which is a pointer to an array of size `num_harq_codeblock` allocated in the heap later) in the `active_device` struct (see the following) in our implementation. +```C +struct active_device { + ... + uint32_t num_harq_codeblock; + /* Persistent data structure to keep track of HARQ-related information */ + // Note: This is used to store/keep track of the combined output information across iterations + struct rte_bbdev_op_data *harq_buffers; + ... +} active_dev; +``` + + +In the following, we elaborate on how the HARQ-related fields are set. + +#### Internal Memory + +When using the internal memory, we need to specify the *offset* of the hardware's memory where we want to store the HARQ combined output, and read from it accordingly later. + +In the current implementation, we use a fixed offset of 32K, i.e., this means that each block is 32K. + +An index is derived from a unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`). +Using that index, we then derived the corresponding offset in the memory (i.e., `harq_combined_offset`). + +```C +... + +// Calculate offset in the HARQ combined buffers +// Unique segment offset +uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; +// Prune to avoid shooting above maximum id +uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; +// Segment offset to byte offset +uint32_t harq_combined_offset = pruned_segment_offset * LDPC_MAX_CB_SIZE; + +... +``` + +This `harq_combined_offset` is then set for `harq_combined_input.offset` and `harq_combined_output.offset` to tell BBDEV where to read/write the HARQ buffers. + +Especially for the Intel ACCs, when providing the HARQ input, the `harq_combined_input.length` must be provided. +To do so, we maintain the length of previous round's output using `harq_buffers` by copying it in `retrieve_ldpc_dec_op`. + +#### External Memory + +Different from the Xilinx T2 and Intel ACC100, the Intel ACC200 uses DDR memory to maintain the HARQ buffers which is managed by OAI. + +In short, we use `harq_buffers` from the `active_device` struct, which have been initialzed with corresponding DPDK mempools for BBDEV to read/write the HARQ input/outputs. + +For indexing, we use the `pruned_segment_offset`, which is derived from the unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`). + +`harq_combined_input.offset` and `harq_combined_output.offset` is always set to 0 in our implementation. +Instead, we provide BBDEV the pointers to our allocated mempool regions through `harq_combined_input.data` and `harq_combined_output.data`. + +In `retrieve_ldpc_dec_op`, we perform a `rte_memcpy` to copy the HARQ combined outputs to `harq_buffers`, along with other necessary metadata information to prepare for subsequent HARQ rounds. +Similar to the Intel ACC100, when providing the HARQ input, the `harq_combined_input.length` must be provided. + +### LLR Scaling + +The LLR output from OAI's demodulation block is in 16 bits. +However, accelerators typically assume that the LLR inputs are scaled down using fixed-point arithmetic, e.g., 8 bits. + +For the Xilinx T2, saturation is performed. +However, our tests have shown that this implementation did not work well for the Intel ACCs. + +To that end, we perform LLR scaling differently for the Intel ACCs. +Through the function `llr_scaling`, we look at the min and max LLR values, perform scaling and subsequently saturation accordingly. + +The fixed-point representations supported by the hardware is defined by the `llr_size` and `llr_decimal`. +For example, if `llr_size=8`, and `llr_decimal=1`, it means that we use a S8.1 representation (1 fractional bit), the min and max values are -64 to 63.5. + +> *Limitation:* +> We observe that in certain configurations, such as when using 2 MIMO layers, the LLR (Log-Likelihood Ratio) outputs exhibit a much wider dynamic range (e.g., from -10,000 to 10,000) compared to the typical sub-1,000 range seen in most other cases. +> This suggests potential inconsistencies in the LLR values generated by the demodulator. +> We believe that the decoding performance, when compared to the CPU implementation under the same number of LDPC decoding iterations, could be improved by refining the LLR scaling approach or revisiting the LLR generation process during demodulation. +> This remains an area of active investigation. +> +> *Workaround:* +> As a temporary measure, increasing the number of LDPC decoding iterations—e.g., to 150—has shown to yield a decoding success rate comparable to that of the CPU implementation. +> This comes at a negligible increase in decoding time based on our current evaluation. + +### Special Case(s) + +#### TB mode + +For the T2, only CB mode is supported. + +For the Intel ACCs, while we also use CB mode mostly, there is a special case that must be handled properly as specified by the BBDEV documentations: +``` +... The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. ... +``` + +In such a case, we perform a check on whether there is only one TB, AND if the TB has only one CB. +If so, this has to be handled separately by using TB mode. + +### Future Work(s) + +#### TB mode + +The current channel coding interface assumes LDPC encoding and decoding are performed on a per-code block basis. +However, to fully leverage Transport Block (TB) mode as supported in BBDEV, which can improve efficiency, the L1 implementation surrounding channel coding needs to be refactored. +This remains an area for future investigation. + +#### CRC24A/CRC16 + +CRC24A/CRC16 is supported by the BBDEV device (i.e., Intel ACC200). +However, we do not make use of them and rely on OAI's existing CRC functions. +To use of BBDEV's CRC24A/CRC16 capability will be looked into in future MRs. diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c similarity index 57% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c rename to openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 04ad571de0..62a20ef822 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -2,13 +2,8 @@ * Copyright(c) 2017 Intel Corporation */ -/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c - * \note: based on testbbdev test_bbdev_perf.c functions. Harq buffer offset added. - * \mbuf and mempool allocated at the init step, LDPC parameters updated from OAI. - */ - -#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h" -#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h" +#include "../nrLDPC_coding_interface.h" +#include "nrLDPC_coding_aal.h" #include "PHY/sse_intrin.h" #include #define NR_LDPC_ENABLE_PARITY_CHECK @@ -19,6 +14,7 @@ #include #include #include +#include #include @@ -47,16 +43,21 @@ #define TIME_OUT_POLL 1e8 /* Headroom for filler LLRs insertion in HARQ buffer */ #define FILLER_HEADROOM 1024 -/* Number of segments that could be stored in HARQ combined buffers */ -#define HARQ_CODEBLOCK_ID_MAX (16 << 5) pthread_mutex_t encode_mutex; pthread_mutex_t decode_mutex; -/* Represents tested active devices */ +/* we assume one active baseband device only */ struct active_device { const char *driver_name; uint8_t dev_id; + struct rte_bbdev_info info; + bool is_t2; + uint32_t num_harq_codeblock; + /* Persistent data structure to keep track of HARQ-related information */ + // Note: This is used to store/keep track of the combined output information across iterations + struct rte_bbdev_op_data *harq_buffers; + bool support_internal_harq_memory; int dec_queue; int enc_queue; uint16_t queue_ids[MAX_QUEUES]; @@ -65,13 +66,15 @@ struct active_device { struct rte_mempool *bbdev_enc_op_pool; struct rte_mempool *in_mbuf_pool; struct rte_mempool *hard_out_mbuf_pool; -} active_devs[RTE_BBDEV_MAX_DEVS]; -static int nb_active_devs; + struct rte_mempool *harq_in_mbuf_pool; + struct rte_mempool *harq_out_mbuf_pool; +} active_dev; /* Data buffers used by BBDEV ops */ struct data_buffers { struct rte_bbdev_op_data *inputs; struct rte_bbdev_op_data *hard_outputs; + struct rte_bbdev_op_data *harq_outputs; }; /* Operation parameters specific for given test case */ @@ -93,26 +96,34 @@ struct thread_params { struct rte_mempool *bbdev_op_pool; }; -static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { +static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +{ uint16_t nb_segments = 0; for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { - nb_segments+=nrLDPC_slot_decoding_parameters->TBs[h].C; + nb_segments += nrLDPC_slot_decoding_parameters->TBs[h].C; } return nb_segments; } -static uint16_t nb_segments_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { +static uint16_t nb_segments_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +{ uint16_t nb_segments = 0; for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { - nb_segments+=nrLDPC_slot_encoding_parameters->TBs[h].C; + nb_segments += nrLDPC_slot_encoding_parameters->TBs[h].C; } return nb_segments; } +/* Read flag value 0/1 from bitmap */ +// DPDK BBDEV copy +static inline bool check_bit(uint32_t bitmap, uint32_t bitmask) +{ + return bitmap & bitmask; +} + /* calculates optimal mempool size not smaller than the val */ // DPDK BBDEV copy -static unsigned int -optimal_mempool_size(unsigned int val) +static unsigned int optimal_mempool_size(unsigned int val) { return rte_align32pow2(val + 1) - 1; } @@ -131,17 +142,16 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num OPS_POOL_SIZE_MIN)); /* Decoder ops mempool */ - ad->bbdev_dec_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_dec", RTE_BBDEV_OP_LDPC_DEC, - /* Encoder ops mempool */ ops_pool_size, OPS_CACHE_SIZE, socket_id); - ad->bbdev_enc_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_enc", RTE_BBDEV_OP_LDPC_ENC, - ops_pool_size, OPS_CACHE_SIZE, socket_id); + ad->bbdev_dec_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_dec", + RTE_BBDEV_OP_LDPC_DEC, + /* Encoder ops mempool */ ops_pool_size, + OPS_CACHE_SIZE, + socket_id); + ad->bbdev_enc_op_pool = + rte_bbdev_op_pool_create("bbdev_op_pool_enc", RTE_BBDEV_OP_LDPC_ENC, ops_pool_size, OPS_CACHE_SIZE, socket_id); if ((ad->bbdev_dec_op_pool == NULL) || (ad->bbdev_enc_op_pool == NULL)) - AssertFatal(1 == 0, - "ERROR Failed to create %u items ops pool for dev %u on socket %d.", - ops_pool_size, - ad->dev_id, - socket_id); + AssertFatal(1 == 0, "ERROR Failed to create %u items ops pool for dev %u on socket %d.", ops_pool_size, ad->dev_id, socket_id); /* Inputs */ mbuf_pool_size = optimal_mempool_size(ops_pool_size * nb_segments); @@ -149,9 +159,9 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num ad->in_mbuf_pool = rte_pktmbuf_pool_create("in_mbuf_pool", mbuf_pool_size, 0, 0, data_room_size, socket_id); AssertFatal(ad->in_mbuf_pool != NULL, "ERROR Failed to create %u items input pktmbuf pool for dev %u on socket %d.", - mbuf_pool_size, - ad->dev_id, - socket_id); + mbuf_pool_size, + ad->dev_id, + socket_id); /* Hard outputs */ data_room_size = RTE_MAX(out_buff_sz + RTE_PKTMBUF_HEADROOM + FILLER_HEADROOM, (unsigned int)RTE_MBUF_DEFAULT_BUF_SIZE); @@ -161,6 +171,27 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num mbuf_pool_size, ad->dev_id, socket_id); + + /* HARQ outputs */ + data_room_size = LDPC_MAX_CB_SIZE; + ad->harq_out_mbuf_pool = rte_pktmbuf_pool_create("harq_out_mbuf_pool", mbuf_pool_size, 0, 0, data_room_size, socket_id); + AssertFatal(ad->harq_out_mbuf_pool != NULL, + "ERROR Failed to create %u items harq output pktmbuf pool for dev %u on socket %d.", + mbuf_pool_size, + ad->dev_id, + socket_id); + + /* HARQ inputs */ + // Note: This is used as our harq buffer to store the combined outputs across iterations + data_room_size = LDPC_MAX_CB_SIZE; + ad->harq_in_mbuf_pool = + rte_pktmbuf_pool_create("harq_in_mbuf_pool", active_dev.num_harq_codeblock, 0, 0, data_room_size, socket_id); + AssertFatal(ad->harq_in_mbuf_pool != NULL, + "ERROR Failed to create %u items harq input pktmbuf pool for dev %u on socket %d.", + active_dev.num_harq_codeblock, + ad->dev_id, + socket_id); + return 0; } @@ -237,52 +268,130 @@ const char *ldpcdec_flag_bitmask[] = { "RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_FILLERS", }; -// based on DPDK BBDEV add_bbdev_dev -static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) +void debug_dev_capabilities(uint8_t dev_id, struct rte_bbdev_info *info) { - int ret; - struct active_device *ad = &active_devs[nb_active_devs]; - unsigned int nb_queues; - nb_queues = RTE_MIN(rte_lcore_count(), info->drv.max_num_queues); - nb_queues = RTE_MIN(nb_queues, (unsigned int)MAX_QUEUES); - /* Display for debug the capabilities of the card */ for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { - printf("device: %d, capability[%d]=%s\n", dev_id, i, rte_bbdev_op_type_str(info->drv.capabilities[i].type)); + LOG_D(NR_PHY, "device: %d, capability[%d]=%s\n", dev_id, i, rte_bbdev_op_type_str(info->drv.capabilities[i].type)); if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { const struct rte_bbdev_op_cap_ldpc_enc cap = info->drv.capabilities[i].cap.ldpc_enc; - printf(" buffers: src = %d, dst = %d\n capabilites: ", cap.num_buffers_src, cap.num_buffers_dst); + LOG_D(NR_PHY, " buffers: src = %d, dst = %d\n capabilites: ", cap.num_buffers_src, cap.num_buffers_dst); for (int j = 0; j < sizeof(cap.capability_flags) * 8; j++) if (cap.capability_flags & (1ULL << j)) - printf("%s ", ldpcenc_flag_bitmask[j]); - printf("\n"); + LOG_D(NR_PHY, "%s ", ldpcenc_flag_bitmask[j]); + LOG_D(NR_PHY, "\n"); } if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { const struct rte_bbdev_op_cap_ldpc_dec cap = info->drv.capabilities[i].cap.ldpc_dec; - printf(" buffers: src = %d, hard out = %d, soft_out %d, llr size %d, llr decimals %d \n capabilities: ", - cap.num_buffers_src, - cap.num_buffers_hard_out, - cap.num_buffers_soft_out, - cap.llr_size, - cap.llr_decimals); + LOG_D(NR_PHY, + " buffers: src = %d, hard out = %d, soft_out %d, llr size %d, llr decimals %d \n capabilities: ", + cap.num_buffers_src, + cap.num_buffers_hard_out, + cap.num_buffers_soft_out, + cap.llr_size, + cap.llr_decimals); for (int j = 0; j < sizeof(cap.capability_flags) * 8; j++) if (cap.capability_flags & (1ULL << j)) - printf("%s ", ldpcdec_flag_bitmask[j]); - printf("\n"); + LOG_D(NR_PHY, "%s ", ldpcdec_flag_bitmask[j]); + LOG_D(NR_PHY, "\n"); } } +} - /* setup device */ - ret = rte_bbdev_setup_queues(dev_id, nb_queues, info->socket_id); - if (ret < 0) { - printf("rte_bbdev_setup_queues(%u, %u, %d) ret %i\n", dev_id, nb_queues, info->socket_id, ret); - return -1; +void check_required_dev_capabilities(struct rte_bbdev_info *info) +{ + // check ldpc enc/ dec support + bool ldpc_enc = false; + bool ldpc_dec = false; + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { + ldpc_enc = true; + } + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + ldpc_dec = true; + } } + AssertFatal(ldpc_enc, "ERROR: bbdev device does not support LDPC encoding\n"); + AssertFatal(ldpc_dec, "ERROR: bbdev device does not support LDPC decoding\n"); + + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { + // check encoding capabilities + bool rate_match = check_bit(info->drv.capabilities[i].cap.ldpc_enc.capability_flags, RTE_BBDEV_LDPC_RATE_MATCH); + AssertFatal(rate_match, "ERROR: bbdev device does not support LDPC encoding with rate matching\n"); + } + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + // check decoding capabilities + bool iter_stop = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE); + AssertFatal(iter_stop, "ERROR: bbdev device does not support LDPC decoding with iteration stop\n"); + + bool crc_24b_drop = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP); + AssertFatal(crc_24b_drop, "ERROR: bbdev device does not support LDPC decoding with CRC-24B drop\n"); + + bool crc_24b_check = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK); + AssertFatal(crc_24b_check, "ERROR: bbdev device does not support LDPC decoding with CRC-24B check\n"); + } + } +} + +bool check_internal_harq_memory_capabilities(struct rte_bbdev_info *info) +{ + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + bool harq_in = + check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE); + bool harq_out = + check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE); + bool internal_harq_memory_support = harq_in & harq_out; + if (internal_harq_memory_support) { + LOG_I(NR_PHY, "bbdev device supports internal HARQ memory\n"); + } + return internal_harq_memory_support; + } + } + return false; +} + +// based on DPDK BBDEV add_bbdev_dev +static int add_dev(uint8_t dev_id, bool is_t2, uint32_t num_harq_codeblock) +{ + int ret; + unsigned int nb_queues; + + // retrieve device capabilities + rte_bbdev_info_get(dev_id, &active_dev.info); + LOG_I(NR_PHY, "using bbdev %d: %s\n", dev_id, active_dev.info.dev_name); + + active_dev.driver_name = active_dev.info.drv.driver_name; + active_dev.dev_id = dev_id; + + nb_queues = RTE_MIN(rte_lcore_count(), active_dev.info.drv.max_num_queues); + nb_queues = RTE_MIN(nb_queues, (unsigned int)MAX_QUEUES); + + // debug device capabilities + debug_dev_capabilities(dev_id, &active_dev.info); + + // check required device capabilities + check_required_dev_capabilities(&active_dev.info); + + // check internal harq memory capabilities + active_dev.support_internal_harq_memory = check_internal_harq_memory_capabilities(&active_dev.info); + + // setup harq buffers + active_dev.num_harq_codeblock = num_harq_codeblock; + active_dev.harq_buffers = malloc(sizeof(struct rte_bbdev_op_data) * active_dev.num_harq_codeblock); + + // is device T2? + active_dev.is_t2 = is_t2; + + // device setup + ret = rte_bbdev_setup_queues(dev_id, nb_queues, active_dev.info.socket_id); + AssertFatal(ret == 0, "rte_bbdev_setup_queues(%u, %u, %d) ret %i\n", dev_id, nb_queues, active_dev.info.socket_id, ret); /* setup device queues */ struct rte_bbdev_queue_conf qconf = { - .socket = info->socket_id, - .queue_size = info->drv.default_queue_conf.queue_size, + .socket = active_dev.info.socket_id, + .queue_size = active_dev.info.drv.default_queue_conf.queue_size, }; // Search a queue linked to HW capability ldpc decoding @@ -291,10 +400,10 @@ static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) for (queue_id = 0; queue_id < nb_queues; ++queue_id) { ret = rte_bbdev_queue_configure(dev_id, queue_id, &qconf); if (ret == 0) { - printf("Found LDCP encoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); + LOG_I(NR_PHY, "Found LDPC encoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); qconf.priority++; - ad->enc_queue = queue_id; - ad->queue_ids[queue_id] = queue_id; + active_dev.enc_queue = queue_id; + active_dev.queue_ids[queue_id] = queue_id; break; } } @@ -305,15 +414,30 @@ static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) for (queue_id++; queue_id < nb_queues; ++queue_id) { ret = rte_bbdev_queue_configure(dev_id, queue_id, &qconf); if (ret == 0) { - printf("Found LDCP decoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); + LOG_I(NR_PHY, "Found LDPC decoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); qconf.priority++; - ad->dec_queue = queue_id; - ad->queue_ids[queue_id] = queue_id; + active_dev.dec_queue = queue_id; + active_dev.queue_ids[queue_id] = queue_id; break; } } AssertFatal(queue_id != nb_queues, "ERROR Failed to configure encoding queues on dev %u", dev_id); - ad->nb_queues = 2; + active_dev.nb_queues = 2; + return 0; +} + +static int init_op_data_objs_harq(struct rte_bbdev_op_data *bufs, struct rte_mempool *mbuf_pool) +{ + for (int i = 0; i < active_dev.num_harq_codeblock; i++) { + struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool); + AssertFatal(m_head != NULL, + "Not enough mbufs in HARQ mbuf pool (needed %u, available %u)", + active_dev.num_harq_codeblock, + mbuf_pool->size); + bufs[i].data = m_head; + bufs[i].offset = 0; + bufs[i].length = 0; + } return 0; } @@ -337,7 +461,7 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs, op_type, nb_segments_decoding(nrLDPC_slot_decoding_parameters), mbuf_pool->size); - + if (data_len > RTE_BBDEV_LDPC_E_MAX_MBUF) { printf("Warning: Larger input size than DPDK mbuf %u\n", data_len); large_input = true; @@ -393,7 +517,7 @@ static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, op_type, nb_segments_encoding(nrLDPC_slot_encoding_parameters), mbuf_pool->size); - + if (data_len > RTE_BBDEV_LDPC_E_MAX_MBUF) { printf("Warning: Larger input size than DPDK mbuf %u\n", data_len); large_input = true; @@ -430,7 +554,6 @@ static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, return 0; } - // DPDK BBEV copy static int allocate_buffers_on_socket(struct rte_bbdev_op_data **buffers, const int len, const int socket) { @@ -457,53 +580,89 @@ static void free_mempools(struct active_device *ad) rte_mempool_free(ad->bbdev_enc_op_pool); rte_mempool_free(ad->in_mbuf_pool); rte_mempool_free(ad->hard_out_mbuf_pool); + rte_mempool_free(ad->harq_in_mbuf_pool); + rte_mempool_free(ad->harq_out_mbuf_pool); } // based on DPDK BBDEV copy_reference_ldpc_dec_op -static void -set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, - struct rte_bbdev_op_data *inputs, - struct rte_bbdev_op_data *outputs, - nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, + struct rte_bbdev_op_data *inputs, + struct rte_bbdev_op_data *outputs, + struct rte_bbdev_op_data *harq_outputs, + nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { int j = 0; + // The T2 only supports CB mode, and does not TB mode special case handling. + bool special_case_tb_mode = !active_dev.is_t2 && (nrLDPC_slot_decoding_parameters->nb_TBs == 1) + && (nb_segments_decoding(nrLDPC_slot_decoding_parameters) == 1); for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { - ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_dec.basegraph = nrLDPC_slot_decoding_parameters->TBs[h].BG; ops[j]->ldpc_dec.z_c = nrLDPC_slot_decoding_parameters->TBs[h].Z; ops[j]->ldpc_dec.q_m = nrLDPC_slot_decoding_parameters->TBs[h].Qm; ops[j]->ldpc_dec.n_filler = nrLDPC_slot_decoding_parameters->TBs[h].F; - ops[j]->ldpc_dec.n_cb = (nrLDPC_slot_decoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_decoding_parameters->TBs[h].Z) : (50 * nrLDPC_slot_decoding_parameters->TBs[h].Z); + ops[j]->ldpc_dec.n_cb = (nrLDPC_slot_decoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_decoding_parameters->TBs[h].Z) + : (50 * nrLDPC_slot_decoding_parameters->TBs[h].Z); ops[j]->ldpc_dec.iter_max = nrLDPC_slot_decoding_parameters->TBs[h].max_ldpc_iterations; ops[j]->ldpc_dec.rv_index = nrLDPC_slot_decoding_parameters->TBs[h].rv_index; - ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | - RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE | - RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE | - RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE; + ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE; if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) { *nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared = false; - *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; + if (active_dev.is_t2) + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; } else { ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE; + if (active_dev.support_internal_harq_memory) { + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE; + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE; + } } - if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { - ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP; - ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK; - } - ops[j]->ldpc_dec.code_block_mode = 1; + if (!active_dev.is_t2) + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; + + if (!special_case_tb_mode) { + ops[j]->ldpc_dec.code_block_mode = 1; + ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; + if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP; + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK; + } + } else { + /** + * This is a special case when #TB = 1 and #CB = 1 + * In this case, we must use TB mode + * Quoted from: https://doc.dpdk.org/guides-23.11/prog_guide/bbdev.html#bbdev-ldpc-decode-operation + * The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a + * special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. + */ + ops[j]->ldpc_dec.code_block_mode = 0; + ops[j]->ldpc_dec.tb_params.c = 1; + ops[j]->ldpc_dec.tb_params.r = 0; + ops[j]->ldpc_dec.tb_params.cab = 1; + ops[j]->ldpc_dec.tb_params.ea = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; + ops[j]->ldpc_dec.tb_params.eb = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; + } // Calculate offset in the HARQ combined buffers // Unique segment offset uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; // Prune to avoid shooting above maximum id - uint32_t pruned_segment_offset = segment_offset % HARQ_CODEBLOCK_ID_MAX; + uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; // Segment offset to byte offset uint32_t harq_combined_offset = pruned_segment_offset * LDPC_MAX_CB_SIZE; - ops[j]->ldpc_dec.harq_combined_input.offset = harq_combined_offset; - ops[j]->ldpc_dec.harq_combined_output.offset = harq_combined_offset; - + if (active_dev.support_internal_harq_memory) { + // retrieve corresponding HARQ output information from previous iteration, especially the length + ops[j]->ldpc_dec.harq_combined_input = active_dev.harq_buffers[pruned_segment_offset]; + // Note: When using INTERNAL_HARQ memory, the "offset" is used to point to a particular address + // within the BBDEV's onboard memory, and the address should be multiples of 32K. + harq_outputs[j].offset = harq_combined_offset; + ops[j]->ldpc_dec.harq_combined_output = harq_outputs[j]; + } else { + // retrieve corresponding HARQ buffers from previous iteration + ops[j]->ldpc_dec.harq_combined_input = active_dev.harq_buffers[pruned_segment_offset]; + ops[j]->ldpc_dec.harq_combined_output = harq_outputs[j]; + } ops[j]->ldpc_dec.hard_output = outputs[j]; ops[j]->ldpc_dec.input = inputs[j]; ++j; @@ -512,28 +671,47 @@ set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, } // based on DPDK BBDEV copy_reference_ldpc_enc_op -static void -set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, - struct rte_bbdev_op_data *inputs, - struct rte_bbdev_op_data *outputs, - nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +static void set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, + struct rte_bbdev_op_data *inputs, + struct rte_bbdev_op_data *outputs, + nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { int j = 0; + // The T2 only supports CB mode, and does not TB mode special case handling. + bool special_case_tb_mode = !active_dev.is_t2 && (nrLDPC_slot_encoding_parameters->nb_TBs == 1) + && (nb_segments_encoding(nrLDPC_slot_encoding_parameters) == 1); for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { - ops[j]->ldpc_enc.cb_params.e = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_enc.basegraph = nrLDPC_slot_encoding_parameters->TBs[h].BG; ops[j]->ldpc_enc.z_c = nrLDPC_slot_encoding_parameters->TBs[h].Z; ops[j]->ldpc_enc.q_m = nrLDPC_slot_encoding_parameters->TBs[h].Qm; ops[j]->ldpc_enc.n_filler = nrLDPC_slot_encoding_parameters->TBs[h].F; - ops[j]->ldpc_enc.n_cb = (nrLDPC_slot_encoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_encoding_parameters->TBs[h].Z) : (50 * nrLDPC_slot_encoding_parameters->TBs[h].Z); + ops[j]->ldpc_enc.n_cb = (nrLDPC_slot_encoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_encoding_parameters->TBs[h].Z) + : (50 * nrLDPC_slot_encoding_parameters->TBs[h].Z); if (nrLDPC_slot_encoding_parameters->TBs[h].tbslbrm != 0) { uint32_t Nref = 3 * nrLDPC_slot_encoding_parameters->TBs[h].tbslbrm / (2 * nrLDPC_slot_encoding_parameters->TBs[h].C); ops[j]->ldpc_enc.n_cb = min(ops[j]->ldpc_enc.n_cb, Nref); } ops[j]->ldpc_enc.rv_index = nrLDPC_slot_encoding_parameters->TBs[h].rv_index; ops[j]->ldpc_enc.op_flags = RTE_BBDEV_LDPC_RATE_MATCH; - ops[j]->ldpc_enc.code_block_mode = 1; + if (!special_case_tb_mode) { + ops[j]->ldpc_enc.code_block_mode = 1; + ops[j]->ldpc_enc.cb_params.e = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + } else { + /** + * This is a special case when #TB = 1 and #CB = 1 + * In this case, we must use TB mode + * Quoted from: https://doc.dpdk.org/guides-23.11/prog_guide/bbdev.html#bbdev-ldpc-decode-operation + * The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a + * special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. + */ + ops[j]->ldpc_enc.code_block_mode = 0; + ops[j]->ldpc_enc.tb_params.c = 1; + ops[j]->ldpc_enc.tb_params.r = 0; + ops[j]->ldpc_enc.tb_params.cab = 1; + ops[j]->ldpc_enc.tb_params.ea = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + ops[j]->ldpc_enc.tb_params.eb = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + } ops[j]->ldpc_enc.output = outputs[j]; ops[j]->ldpc_enc.input = inputs[j]; ++j; @@ -551,19 +729,30 @@ static int retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decod uint16_t data_len = rte_pktmbuf_data_len(m) - hard_output->offset; uint8_t *data = rte_pktmbuf_mtod_offset(m, uint8_t *, hard_output->offset); memcpy(nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c, data, data_len); + + uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; + uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; + struct rte_bbdev_op_data *harq_output = &ops[j]->ldpc_dec.harq_combined_output; + if (!active_dev.support_internal_harq_memory) { + struct rte_mbuf *m_src = harq_output->data; + uint8_t *data_src = rte_pktmbuf_mtod_offset(m_src, uint8_t *, 0); + struct rte_mbuf *m_dst = active_dev.harq_buffers[pruned_segment_offset].data; + uint8_t *data_dst = rte_pktmbuf_mtod_offset(m_dst, uint8_t *, 0); + rte_memcpy(data_dst, data_src, harq_output->length); + } + active_dev.harq_buffers[pruned_segment_offset].offset = harq_output->offset; + active_dev.harq_buffers[pruned_segment_offset].length = harq_output->length; ++j; } } return 0; } -static int -retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, - nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +static int retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { uint8_t *p_out = NULL; int j = 0; - for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { int E_sum = 0; int bit_offset = 0; int byte_offset = 0; @@ -606,8 +795,7 @@ retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, } // based on DPDK BBDEV throughput_pmd_lcore_ldpc_dec -static int -pmd_lcore_ldpc_dec(void *arg) +static int pmd_lcore_ldpc_dec(void *arg) { struct thread_params *tp = arg; nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters = tp->nrLDPC_slot_decoding_parameters; @@ -620,15 +808,12 @@ pmd_lcore_ldpc_dec(void *arg) AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); - struct rte_bbdev_info info; - rte_bbdev_info_get(tp->dev_id, &info); - while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); int ret = rte_bbdev_dec_op_alloc_bulk(tp->bbdev_op_pool, ops_enq, num_segments); AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); - set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, nrLDPC_slot_decoding_parameters); + set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, bufs->harq_outputs, nrLDPC_slot_decoding_parameters); uint16_t enq = 0, deq = 0; while (enq < num_segments) { @@ -656,24 +841,19 @@ pmd_lcore_ldpc_dec(void *arg) // Check if CRC is available otherwise rely on ops_enq[j]->status to detect decoding success // CRC is NOT available if the CRC type is 24_B which is when C is greater than 1 - if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { - *status = (ops_enq[j]->status == 0); - } else { - uint8_t *decoded_bytes = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c; uint8_t crc_type = crcType(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A); uint32_t len_with_crc = lenWithCrc(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A); *status = check_crc(decoded_bytes, len_with_crc, crc_type); - } if (*status) { - *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments + 1; + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments + 1; } - ++j; } } @@ -698,9 +878,6 @@ static int pmd_lcore_ldpc_enc(void *arg) AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); - struct rte_bbdev_info info; - rte_bbdev_info_get(tp->dev_id, &info); - while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); @@ -763,7 +940,8 @@ int start_pmd_dec(struct active_device *ad, t_params[0].nrLDPC_slot_decoding_parameters = nrLDPC_slot_decoding_parameters; used_cores++; // For now, we never enter here, we don't use the DPDK thread pool - RTE_LCORE_FOREACH_WORKER(lcore_id) { + RTE_LCORE_FOREACH_WORKER(lcore_id) + { if (used_cores >= num_lcores) break; t_params[used_cores].dev_id = ad->dev_id; @@ -807,7 +985,8 @@ int32_t start_pmd_enc(struct active_device *ad, t_params[0].nrLDPC_slot_encoding_parameters = nrLDPC_slot_encoding_parameters; used_cores++; // For now, we never enter here, we don't use the DPDK thread pool - RTE_LCORE_FOREACH_WORKER(lcore_id) { + RTE_LCORE_FOREACH_WORKER(lcore_id) + { if (used_cores >= num_lcores) break; t_params[used_cores].dev_id = ad->dev_id; @@ -828,46 +1007,116 @@ int32_t start_pmd_enc(struct active_device *ad, struct test_op_params *op_params = NULL; +static int normalize_dpdk_dev(const char *input, char *output, size_t out_len) +{ + regex_t regex_full, regex_short; + int reti; + + // patterns + const char *pattern_full = "^[0]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\\.[0-7]$"; + const char *pattern_short = "^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\\.[0-7]$"; + regcomp(®ex_full, pattern_full, REG_EXTENDED); + regcomp(®ex_short, pattern_short, REG_EXTENDED); + + // check full format + reti = regexec(®ex_full, input, 0, NULL, 0); + if (reti == 0) { + // already in full format + strncpy(output, input, out_len - 1); + output[out_len - 1] = '\0'; + } else { + // check short format + reti = regexec(®ex_short, input, 0, NULL, 0); + if (reti == 0) { + // convert to full format + snprintf(output, out_len, "0000:%s", input); + } else { + // invalid format + regfree(®ex_full); + regfree(®ex_short); + return -1; + } + } + regfree(®ex_full); + regfree(®ex_short); + return 0; +} + // OAI CODE int32_t nrLDPC_coding_init() { pthread_mutex_init(&encode_mutex, NULL); pthread_mutex_init(&decode_mutex, NULL); + int ret; - int dev_id = 0; - struct rte_bbdev_info info; - struct active_device *ad = active_devs; - char *dpdk_dev = NULL; //PCI address of the card - char *dpdk_core_list = NULL; // cores used by DPDK for T2 + int dev_id = -1; + + char *dpdk_dev = NULL; // PCI address of the card + char *dpdk_core_list = NULL; // cores used by DPDK for bbdev char *dpdk_file_prefix = NULL; + char *vfio_vf_token = NULL; // vfio token for the bbdev card + uint32_t num_harq_codeblock = 0; // size of the HARQ buffer in terms of the number of 32K blocks + uint32_t is_t2 = 0; paramdef_t LoaderParams[] = { - {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, - {"dpdk_core_list", NULL, 0, .strptr = &dpdk_core_list, .defstrval = NULL, TYPE_STRING, 0, NULL}, - {"dpdk_file_prefix", NULL, 0, .strptr = &dpdk_file_prefix, .defstrval = "b6", TYPE_STRING, 0, NULL} + {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"dpdk_core_list", NULL, 0, .strptr = &dpdk_core_list, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"dpdk_file_prefix", NULL, 0, .strptr = &dpdk_file_prefix, .defstrval = "b6", TYPE_STRING, 0, NULL}, + {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"num_harq_codeblock", NULL, 0, .uptr = &num_harq_codeblock, .defintval = 512, TYPE_UINT32, 0, NULL}, + {"is_t2", NULL, 0, .uptr = &is_t2, .defintval = 0, TYPE_UINT8, 0, NULL}, }; - config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2"); - AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); - AssertFatal(dpdk_core_list!=NULL, "nrLDPC_coding_t2.dpdk_core_list was not provided"); - char *argv_re[] = {"bbdev", "-a", dpdk_dev, "-l", dpdk_core_list, "--file-prefix", dpdk_file_prefix, "--"}; - // EAL initialization, if already initialized (init in xran lib) try to probe DPDK device - ret = rte_eal_init(sizeofArray(argv_re), argv_re); + config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); + if (dpdk_dev == NULL) + LOG_E(NR_PHY, + "could not find mandatory --nrLDPC_coding_aal.dpdk_dev. If you used --nrLDPC_coding_t2.*, please rename all options to " + "--nrLDPC_coding_aal.*\n"); + AssertFatal(dpdk_dev != NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); + AssertFatal(dpdk_core_list != NULL, "nrLDPC_coding_aal.dpdk_core_list was not provided"); + + char dpdk_dev_full[32]; + if (normalize_dpdk_dev(dpdk_dev, dpdk_dev_full, sizeof(dpdk_dev_full)) != 0) { + LOG_E(NR_PHY, "invalid DPDK device format: %s\n", dpdk_dev); + return -1; + } + + int argc = 7; + char *argv[11] = {"bbdev", "-l", dpdk_core_list, "-a", dpdk_dev_full, "--file-prefix", dpdk_file_prefix, "--", "--", "--", "--"}; + if (vfio_vf_token != NULL) { + argc += 2; + argv[7] = "--vfio-vf-token"; + argv[8] = vfio_vf_token; + } + ret = rte_eal_init(argc, argv); if (ret < 0) { - printf("EAL initialization failed, probing DPDK device %s\n", dpdk_dev); - if (rte_dev_probe(dpdk_dev) != 0) { - LOG_E(PHY, "T2 card %s not found\n", dpdk_dev); + LOG_W(NR_PHY, "EAL initialization failed, probing DPDK device %s\n", dpdk_dev_full); + if (rte_dev_probe(dpdk_dev_full) != 0) { + LOG_E(NR_PHY, "bbdev %s not found\n", dpdk_dev_full); return (-1); } } - // Use only device 0 - first detected device - rte_bbdev_info_get(0, &info); - // Set number of queues based on number of initialized cores (-l option) and driver - // capabilities - AssertFatal(add_dev(dev_id, &info)== 0, "Failed to setup bbdev"); + uint16_t nb_bbdevs = rte_bbdev_count(); + AssertFatal(nb_bbdevs > 0, "no bbdev found"); + + // find the baseband device that matches the dpdk_dev specified in the configurations + struct rte_bbdev_info info; + LOG_I(NR_PHY, "detected %u bbdev.\n", nb_bbdevs); + for (uint16_t device_id = 0; device_id < nb_bbdevs; device_id++) { + rte_bbdev_info_get(device_id, &info); + // check if info matches the dpdk_dev that we are looking for + if (strcmp(info.dev_name, dpdk_dev_full) == 0) { + LOG_I(NR_PHY, "bbdev %s found.\n", info.dev_name); + dev_id = device_id; + break; + } + } + AssertFatal(dev_id != -1, "bbdev %s not found.", dpdk_dev_full); + + AssertFatal(add_dev(dev_id, is_t2, num_harq_codeblock) == 0, "Failed to setup bbdev"); AssertFatal(rte_bbdev_stats_reset(dev_id) == 0, "Failed to reset stats of bbdev %u", dev_id); AssertFatal(rte_bbdev_start(dev_id) == 0, "Failed to start bbdev %u", dev_id); - //the previous calls have populated this global variable (beurk) - // One more global to remove, not thread safe global op_params + // the previous calls have populated this global variable (beurk) + // One more global to remove, not thread safe global op_params op_params = rte_zmalloc(NULL, sizeof(struct test_op_params), RTE_CACHE_LINE_SIZE); AssertFatal(op_params != NULL, "Failed to alloc %zuB for op_params", @@ -877,34 +1126,120 @@ int32_t nrLDPC_coding_init() int out_max_sz = 8448; // max code block size (for BG1), 22 * 384 int in_max_sz = LDPC_MAX_CB_SIZE; // max number of encoded bits (for BG2 and MCS0) int num_queues = 1; - int f_ret = create_mempools(ad, socket_id, num_queues, out_max_sz, in_max_sz); + int f_ret = create_mempools(&active_dev, socket_id, num_queues, out_max_sz, in_max_sz); if (f_ret != 0) { printf("Couldn't create mempools"); return -1; } + + // initialize persistent data structure to keep track of HARQ-related information + init_op_data_objs_harq(active_dev.harq_buffers, active_dev.harq_in_mbuf_pool); + op_params->num_lcores = 1; return 0; } int32_t nrLDPC_coding_shutdown() { - struct active_device *ad = active_devs; int dev_id = 0; struct rte_bbdev_stats stats; - free_mempools(ad); + free(active_dev.harq_buffers); + free_mempools(&active_dev); rte_free(op_params); rte_bbdev_stats_get(dev_id, &stats); rte_bbdev_stop(dev_id); rte_bbdev_close(dev_id); - memset(active_devs, 0, sizeof(active_devs)); - nb_active_devs = 0; + memset(&active_dev, 0, sizeof(active_dev)); return 0; } +static void +llr_scaling(int16_t *llr, int llr_len, uint8_t *llr_scaled, int8_t llr_size, int8_t llr_decimal, int8_t nb_layers, int8_t Qm) +{ + const int16_t llr_max = (1 << (llr_size - 1)) - 1; + const int16_t llr_min = -llr_max; + + // Step 1: Find the max absolute LLR + int16_t max_abs = 1; // prevent divide-by-zero + // SCALAR IMPLEMENTATION + // for (int i = 0; i < llr_len; i++) { + // int16_t abs_val = abs(llr[i]); + // if (abs_val > max_abs) max_abs = abs_val; + // } + // VECTORIZED IMPLEMENTATION + simde__m128i max_vec = simde_mm_set1_epi16(1); + for (int i = 0; i < llr_len; i += 8) { + simde__m128i llr_vec = simde_mm_loadu_si128((simde__m128i *)&llr[i]); + simde__m128i abs_vec = simde_mm_abs_epi16(llr_vec); + max_vec = simde_mm_max_epi16(max_vec, abs_vec); + } + // reduce max_vec to single max_abs + int16_t temp[8]; + simde_mm_storeu_si128((simde__m128i *)temp, max_vec); + for (int i = 0; i < 8; i++) { + if (temp[i] > max_abs) + max_abs = temp[i]; + } + + // Step 2: Compute dynamic scale factor + float fixed_point_range = (float)llr_max / (1 << llr_decimal); + float scale = fixed_point_range / (float)max_abs; + if (max_abs < fixed_point_range) { + scale = 1.0f; + } + + // Step 3: Scale and saturate + // SCALAR IMPLEMENTATION + // for (int i = 0; i < llr_len; i++) { + // float scaled = (float)llr[i] * scale; // map into fixed-point domain + // scaled = (int8_t)roundf(scaled * (1 << llr_decimal)); + // // Clamp to [-128, 127] + // if (scaled > llr_max) llr_scaled[i] = llr_max; + // else if (scaled < llr_min) llr_scaled[i] = llr_min; + // } + // VECTORIZED IMPLEMENTATION + simde__m128 scale_vec = simde_mm_set1_ps(scale); + simde__m128i llr_max_vec = simde_mm_set1_epi16(llr_max); + simde__m128i llr_min_vec = simde_mm_set1_epi16(llr_min); + simde__m128i decimal_shift = simde_mm_set1_epi16(1 << llr_decimal); + for (int i = 0; i < llr_len; i += 8) { + // load LLR values + simde__m128i llr_vec = simde_mm_loadu_si128((simde__m128i *)&llr[i]); + + // convert to float for scaling + simde__m128i llr_lo = simde_mm_cvtepi16_epi32(llr_vec); + simde__m128i llr_hi = simde_mm_cvtepi16_epi32(simde_mm_srli_si128(llr_vec, 8)); + simde__m128 float_lo = simde_mm_cvtepi32_ps(llr_lo); + simde__m128 float_hi = simde_mm_cvtepi32_ps(llr_hi); + + // scale + float_lo = simde_mm_mul_ps(float_lo, scale_vec); + float_hi = simde_mm_mul_ps(float_hi, scale_vec); + + // convert back to int16 with saturation + llr_lo = simde_mm_cvtps_epi32(float_lo); + llr_hi = simde_mm_cvtps_epi32(float_hi); + simde__m128i scaled_vec = simde_mm_packs_epi32(llr_lo, llr_hi); + + scaled_vec = simde_mm_mullo_epi16(scaled_vec, decimal_shift); + + // clamp to [llr_min, llr_max] + scaled_vec = simde_mm_min_epi16(scaled_vec, llr_max_vec); + scaled_vec = simde_mm_max_epi16(scaled_vec, llr_min_vec); + + // Pack to int8 and store + simde__m128i result = simde_mm_packs_epi16(scaled_vec, simde_mm_setzero_si128()); + simde_mm_storeu_si128((simde__m128i *)&llr_scaled[i], result); + } +} + int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { pthread_mutex_lock(&decode_mutex); + int ret; + int socket_id = active_dev.info.socket_id; + const uint16_t num_segments = nb_segments_decoding(nrLDPC_slot_decoding_parameters); /* It is not unlikely that l_ol becomes big enough to overflow the stack @@ -913,26 +1248,38 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec */ uint8_t l_ol[num_segments * LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); - // hardcoded we use first device - struct active_device *ad = active_devs; - struct rte_bbdev_info info; - int ret; - rte_bbdev_info_get(ad->dev_id, &info); - int socket_id = GET_SOCKET(info.socket_id); // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; + struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {active_dev.in_mbuf_pool, + active_dev.hard_out_mbuf_pool, + active_dev.harq_out_mbuf_pool}; struct data_buffers data_buffers; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; - + struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, + &data_buffers.hard_outputs, + &data_buffers.harq_outputs}; + int8_t llr_size = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_size; + int8_t llr_decimal = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_decimals; int offset = 0; for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int r = 0; r < nrLDPC_slot_decoding_parameters->TBs[h].C; r++) { - uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); - memcpy(z_ol, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); - simde__m128i *pv_ol128 = (simde__m128i *)z_ol; - simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; - for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { - pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); + if (active_dev.is_t2) { + // For the T2, we simply saturate the LLRs. + uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); + memcpy(z_ol, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); + simde__m128i *pv_ol128 = (simde__m128i *)z_ol; + simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; + for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { + pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); + } + } else { + llr_scaling(nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E, + &l_ol[offset], + llr_size, + llr_decimal, + nrLDPC_slot_decoding_parameters->TBs[h].nb_layers, + nrLDPC_slot_decoding_parameters->TBs[h].Qm); } offset += LDPC_MAX_CB_SIZE; } @@ -946,13 +1293,13 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec nrLDPC_slot_decoding_parameters, mbuf_pools[type], type, - info.drv.min_alignment); + active_dev.info.drv.min_alignment); AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_dec(ad, op_params, &data_buffers, nrLDPC_slot_decoding_parameters); + ret = start_pmd_dec(&active_dev, op_params, &data_buffers, nrLDPC_slot_decoding_parameters); if (ret < 0) { - LOG_E(PHY, "Couldn't start pmd dec\n"); + LOG_E(NR_PHY, "Couldn't start pmd dec\n"); } for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { @@ -973,27 +1320,28 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc const uint16_t num_segments = nb_segments_encoding(nrLDPC_slot_encoding_parameters); - // hardcoded to use the first found board - struct active_device *ad = active_devs; int ret; - struct rte_bbdev_info info; - rte_bbdev_info_get(ad->dev_id, &info); - int socket_id = GET_SOCKET(info.socket_id); - // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; - struct data_buffers data_buffers; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; + int socket_id = active_dev.info.socket_id; - for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + // fill_queue_buffers -> init_op_data_objs + struct rte_mempool *mbuf_pools[2] = {active_dev.in_mbuf_pool, active_dev.hard_out_mbuf_pool}; + struct data_buffers data_buffers; + struct rte_bbdev_op_data **queue_ops[2] = {&data_buffers.inputs, &data_buffers.hard_outputs}; + + for (enum op_data_type type = DATA_INPUT; type < 2; ++type) { ret = allocate_buffers_on_socket(queue_ops[type], num_segments * sizeof(struct rte_bbdev_op_data), socket_id); AssertFatal(ret == 0, "Couldn't allocate memory for rte_bbdev_op_data structs"); - ret = init_op_data_objs_enc(*queue_ops[type], nrLDPC_slot_encoding_parameters, mbuf_pools[type], type, info.drv.min_alignment); + ret = init_op_data_objs_enc(*queue_ops[type], + nrLDPC_slot_encoding_parameters, + mbuf_pools[type], + type, + active_dev.info.drv.min_alignment); AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_enc(ad, op_params, &data_buffers, nrLDPC_slot_encoding_parameters); + ret = start_pmd_enc(&active_dev, op_params, &data_buffers, nrLDPC_slot_encoding_parameters); - for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + for (enum op_data_type type = DATA_INPUT; type < 2; ++type) { for (int segment = 0; segment < num_segments; ++segment) rte_pktmbuf_free((*queue_ops[type])[segment].data); rte_free(*queue_ops[type]); @@ -1002,4 +1350,3 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc pthread_mutex_unlock(&encode_mutex); return ret; } - diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.h similarity index 79% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h rename to openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.h index 642ac8f55a..0337202b9b 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.h @@ -2,8 +2,8 @@ * Copyright(c) 2017 Intel Corporation */ -#ifndef _MAIN_H_ -#define _MAIN_H_ +#ifndef __NRLDPC_CODING_T2_H__ +#define __NRLDPC_CODING_T2_H__ #include #include @@ -17,6 +17,7 @@ enum op_data_type { DATA_INPUT = 0, DATA_HARD_OUTPUT, + DATA_HARQ_OUTPUT, DATA_NUM_TYPES, }; diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt deleted file mode 100644 index f64384568a..0000000000 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -########################################################## - -# LDPC offload library - AMD T2 Accelerator Card -########################################################## - -add_boolean_option(ENABLE_LDPC_T2 OFF "Build support for LDPC Offload to T2 library" OFF) - -if (ENABLE_LDPC_T2) - - pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk>=20.11.9) - - find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_T2_LIBRARY_DIRS}) - if (NOT PMD_T2) - message(FATAL_ERROR "could not find poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") - endif() - - message(STATUS "T2 build: use ${PMD_T2}") - add_library(ldpc_t2 MODULE nrLDPC_coding_t2.c) - - set_target_properties(ldpc_t2 PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API") - set_target_properties(ldpc_t2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - target_link_libraries(ldpc_t2 PRIVATE ldpc_gen_HEADERS ${LIBDPDK_T2_LDFLAGS} ${PMD_T2}) - - add_dependencies(nr-softmodem ldpc_t2) - add_dependencies(nr-uesoftmodem ldpc_t2) - add_dependencies(nr_ulsim ldpc_t2) - add_dependencies(nr_ulschsim ldpc_t2) - add_dependencies(nr_dlsim ldpc_t2) - add_dependencies(nr_dlschsim ldpc_t2) - -endif() diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index ed8f37f857..4eff24aacf 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -485,19 +485,19 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in char *shlibversion = NULL; // version of the LDPC coding library paramdef_t LoaderParams_shlibversion[] = {{"shlibversion", NULL, 0, .strptr = &shlibversion, .defstrval = NULL, TYPE_STRING, 0, NULL}}; config_get(config_get_if(), LoaderParams_shlibversion, sizeofArray(LoaderParams_shlibversion), "loader.ldpc"); - if (shlibversion != NULL && strncmp(shlibversion, "_t2", 3) == 0) { - uint32_t eal_init_bbdev = 0; // If not 0 then include the BBDEV device in the EAL init for FHI + if (shlibversion != NULL && strncmp(shlibversion, "_aal", 4) == 0) { + uint32_t is_t2 = 0; // If not 0 then include the BBDEV device in the EAL init for FHI char *dpdk_dev = NULL; // PCI address of the card char *vfio_vf_token = NULL; // vfio token for the bbdev card paramdef_t LoaderParams[] = { - {"eal_init_bbdev", NULL, 0, .uptr = &eal_init_bbdev, .defuintval = 0, TYPE_UINT, 0, NULL}, + {"is_t2", NULL, 0, .uptr = &is_t2, .defuintval = 0, TYPE_UINT, 0, NULL}, {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL} }; - config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2"); + config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); - if (eal_init_bbdev != 0) { - AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); + if (!is_t2) { + AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); snprintf(&bbdev_dev[0], sizeof(bbdev_dev), "%s", dpdk_dev); io_cfg->bbdev_dev[0] = &bbdev_dev[0]; // BBDev dev name; max devices = 1 if(vfio_vf_token != NULL) {