mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-14 13:10:28 +00:00
Compare commits
227 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70508ebaf5 | ||
|
|
4a84b92d88 | ||
|
|
9c7b3d02e0 | ||
|
|
3acb40cb85 | ||
|
|
aff84037a7 | ||
|
|
0f45f471b2 | ||
|
|
016ab2cfb3 | ||
|
|
7729bab5af | ||
|
|
fef2154525 | ||
|
|
546fc0628b | ||
|
|
ddea885bc7 | ||
|
|
be4d64a576 | ||
|
|
afcd77108d | ||
|
|
dde7ecf994 | ||
|
|
4bff655952 | ||
|
|
14b5bcb600 | ||
|
|
6e1bfb2c19 | ||
|
|
3921017988 | ||
|
|
e6dd656e52 | ||
|
|
223fd132e0 | ||
|
|
7d029ed5b1 | ||
|
|
991ede5c59 | ||
|
|
f34ecdd021 | ||
|
|
96dc20010e | ||
|
|
1edb0a09bb | ||
|
|
ff9b39145e | ||
|
|
ebdedfffcf | ||
|
|
486f4423d9 | ||
|
|
bfeba0de48 | ||
|
|
80a8b81e96 | ||
|
|
8bed33d44e | ||
|
|
1ec719bd44 | ||
|
|
9e94084e9d | ||
|
|
5fad2465f0 | ||
|
|
75545fa2c1 | ||
|
|
dc35ce95b9 | ||
|
|
48b329d115 | ||
|
|
6bdf1dde3d | ||
|
|
3a3fd87633 | ||
|
|
c8e36d3968 | ||
|
|
bf89f7997c | ||
|
|
d86bad7915 | ||
|
|
fcf078b18c | ||
|
|
fcea45b215 | ||
|
|
fe56b68514 | ||
|
|
ad304b62d6 | ||
|
|
0dab3bb6a1 | ||
|
|
ecb98e575f | ||
|
|
f63c444bb7 | ||
|
|
1cbfef20f4 | ||
|
|
2828e63dd8 | ||
|
|
384186b0a6 | ||
|
|
aa6f141498 | ||
|
|
32847ee143 | ||
|
|
b1c7bdf99b | ||
|
|
69a017f57b | ||
|
|
602d729d43 | ||
|
|
29c20ac232 | ||
|
|
0a885dcf5a | ||
|
|
447ff67aba | ||
|
|
465b57d8c5 | ||
|
|
35cf1c91bb | ||
|
|
b58631ce79 | ||
|
|
860232782a | ||
|
|
e179a6df8f | ||
|
|
1c659403b7 | ||
|
|
1a1502ac5a | ||
|
|
cb9dab74e3 | ||
|
|
f79f5087e5 | ||
|
|
f5aaacd9e8 | ||
|
|
cde7d98a61 | ||
|
|
d568920466 | ||
|
|
2716a5a3dc | ||
|
|
ede596cfc7 | ||
|
|
33ff225739 | ||
|
|
11cf337b6a | ||
|
|
c5d1895540 | ||
|
|
58eebfe1d8 | ||
|
|
3dbb22cad8 | ||
|
|
3226e5bb34 | ||
|
|
b61d41a440 | ||
|
|
7b5baa5104 | ||
|
|
cea29f9c3f | ||
|
|
a65ddf5cd3 | ||
|
|
e47e28301f | ||
|
|
8c4e48a670 | ||
|
|
6d75ca8e9c | ||
|
|
0d4a4a6d78 | ||
|
|
0abc46b708 | ||
|
|
05af0556af | ||
|
|
14ec3a930f | ||
|
|
cd376c904b | ||
|
|
2a3f0ffc02 | ||
|
|
e59473f8fe | ||
|
|
ea3e1dbaf2 | ||
|
|
326f2a0cfb | ||
|
|
fd794a85c1 | ||
|
|
9c0577ad1e | ||
|
|
102792c373 | ||
|
|
61a1e8c0d8 | ||
|
|
c2dbfd79d5 | ||
|
|
bc32bf8875 | ||
|
|
9f5ac5ac8e | ||
|
|
ac738ae714 | ||
|
|
6f7a26d681 | ||
|
|
6d437cffcf | ||
|
|
52c53c868f | ||
|
|
274d999526 | ||
|
|
c4125d1c74 | ||
|
|
919b61f147 | ||
|
|
5b2a75f518 | ||
|
|
0b7c6bbb95 | ||
|
|
d81d30af28 | ||
|
|
7b2252d52d | ||
|
|
fe211685ec | ||
|
|
29e75444e6 | ||
|
|
eba9f272ce | ||
|
|
09c9bb998f | ||
|
|
6d4573905a | ||
|
|
dc01a3540a | ||
|
|
c3f6af1008 | ||
|
|
633b6de92a | ||
|
|
ad5e941bc9 | ||
|
|
75d7fa275d | ||
|
|
328968cecf | ||
|
|
a5f60811c5 | ||
|
|
840abd0048 | ||
|
|
829d6fe7cf | ||
|
|
db7dbda8e2 | ||
|
|
f448088c8d | ||
|
|
a3268a9c2f | ||
|
|
716c29fe1f | ||
|
|
3cbf777835 | ||
|
|
813a90bd21 | ||
|
|
0548191fac | ||
|
|
145ab730c0 | ||
|
|
5f2108edb0 | ||
|
|
b7ab31854e | ||
|
|
f7d0ec0960 | ||
|
|
7032477784 | ||
|
|
7e0c0fe18b | ||
|
|
5a478deac4 | ||
|
|
40024d4caa | ||
|
|
dcae0070a6 | ||
|
|
3fb74dc66e | ||
|
|
82c3c04018 | ||
|
|
df5a9facbe | ||
|
|
71c692d727 | ||
|
|
159c219bdd | ||
|
|
ecfe769205 | ||
|
|
21329e1b70 | ||
|
|
0aa3f89c52 | ||
|
|
cd79eec3ec | ||
|
|
a1049e8dd1 | ||
|
|
28786af8bf | ||
|
|
eb1ae91051 | ||
|
|
31eb466a7d | ||
|
|
bbd8b295cf | ||
|
|
2d57ec1987 | ||
|
|
26553423ac | ||
|
|
0f50702483 | ||
|
|
7a0489842b | ||
|
|
a211c0ef5a | ||
|
|
9fd43efaf2 | ||
|
|
037c5dae31 | ||
|
|
47e9ad9cbd | ||
|
|
d776779509 | ||
|
|
3516ed3b5e | ||
|
|
4e92eabc18 | ||
|
|
c0f6e70259 | ||
|
|
3205c9a367 | ||
|
|
90c85e8b9c | ||
|
|
a3b077a30f | ||
|
|
b04fa5f7ae | ||
|
|
8542b813e1 | ||
|
|
89d578e7bb | ||
|
|
78b92485ee | ||
|
|
84bbe66c07 | ||
|
|
910f8cef33 | ||
|
|
d8744c52f2 | ||
|
|
5d309b93d1 | ||
|
|
294599d327 | ||
|
|
ddccd57e5c | ||
|
|
6cb51988b1 | ||
|
|
8d2895463b | ||
|
|
f51e47b1c2 | ||
|
|
7b93b83fd4 | ||
|
|
0d118c9821 | ||
|
|
b31846c62f | ||
|
|
e7f12fdd61 | ||
|
|
7cf0e252b3 | ||
|
|
10ff25e530 | ||
|
|
a52883c051 | ||
|
|
4b767c3ab1 | ||
|
|
8cd7dc4f04 | ||
|
|
2241af180f | ||
|
|
37cef0e2cb | ||
|
|
ff2e6f8425 | ||
|
|
d8c1f68da4 | ||
|
|
9651a571d9 | ||
|
|
4ed41739c0 | ||
|
|
659d184265 | ||
|
|
d3688ce345 | ||
|
|
74f661f3d6 | ||
|
|
5e37598e02 | ||
|
|
e5c95a2d7e | ||
|
|
c9e82d6a12 | ||
|
|
bae10b8831 | ||
|
|
21b901aefc | ||
|
|
6cba089c34 | ||
|
|
dc32077fe9 | ||
|
|
ec24751adb | ||
|
|
fff0ced519 | ||
|
|
8aa2f1b965 | ||
|
|
6dac6fed13 | ||
|
|
f19b44639d | ||
|
|
31084d6698 | ||
|
|
0bcc3b3229 | ||
|
|
8b04c038f1 | ||
|
|
9163c8b2bd | ||
|
|
5fae4b3c29 | ||
|
|
29d31cde8e | ||
|
|
dfb0c61c62 | ||
|
|
854090632f | ||
|
|
a1162f9fd3 | ||
|
|
096a107704 | ||
|
|
63af7fcfb8 |
@@ -5,3 +5,4 @@ common/utils/T/T_IDs.h
|
||||
common/utils/T/T_messages.txt.h
|
||||
common/utils/T/genids
|
||||
common/utils/T/genids.o
|
||||
build/
|
||||
|
||||
90
.github/workflows/jenkins-dispatch.yml
vendored
90
.github/workflows/jenkins-dispatch.yml
vendored
@@ -17,12 +17,72 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
verify-signed-commits:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
if: github.event_name == 'pull_request_target'
|
||||
|
||||
steps:
|
||||
- name: Check all commits are signed
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
UNSIGNED=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits \
|
||||
--paginate --jq '[.[] | select(.commit.verification.verified == false) | .sha[:7]] | join(", ")')
|
||||
|
||||
if [ -n "$UNSIGNED" ]; then
|
||||
echo -e "$UNSIGNED commits are missing signature"
|
||||
gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --body "$(cat <<EOF
|
||||
**Validation:**
|
||||
The following commit(s) are missing signature:
|
||||
|
||||
$UNSIGNED
|
||||
|
||||
Please use 'git commit -S' to sign your commits.
|
||||
For detailed instructions, refer to the [CONTRIBUTING.md](https://github.com/duranta-project/openairinterface5g/blob/develop/CONTRIBUTING.md) file at the root of this repository or [GitHub Docs](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
|
||||
EOF
|
||||
)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check-labels:
|
||||
needs: verify-signed-commits
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
if: github.event_name == 'pull_request_target'
|
||||
|
||||
steps:
|
||||
- name: Check for mandatory CI label
|
||||
run: |
|
||||
PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels) }}' | jq -r '.[].name')
|
||||
|
||||
for label in \
|
||||
"${{ vars.BUILD_ONLY_LABEL }}" \
|
||||
"${{ vars.DOC_LABEL }}" \
|
||||
"${{ vars.NR_LABEL }}" \
|
||||
"${{ vars.NRUE_LABEL }}" \
|
||||
"${{ vars.LTE_LABEL }}" \
|
||||
"${{ vars.CI_LABEL }}" \
|
||||
"${{ vars.RETRIGGER_CI_LABEL }}"; do
|
||||
if echo "$PR_LABELS" | grep -qxF "$label"; then
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
exit 1
|
||||
|
||||
detect-changes:
|
||||
needs: check-labels
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
if: |
|
||||
github.event.action != 'labeled' ||
|
||||
github.event.label.name == vars.RETRIGGER_CI_LABEL
|
||||
always() && (
|
||||
github.event_name == 'push' ||
|
||||
needs.check-labels.result == 'success'
|
||||
) && (
|
||||
github.event.action != 'labeled' ||
|
||||
github.event.label.name == vars.RETRIGGER_CI_LABEL
|
||||
)
|
||||
|
||||
outputs:
|
||||
protected_files_changed: ${{ steps.filter.outputs.protected }}
|
||||
@@ -49,8 +109,9 @@ jobs:
|
||||
require-maintainer-approval:
|
||||
needs: detect-changes
|
||||
|
||||
if: needs.detect-changes.outputs.protected_files_changed == 'true'
|
||||
|
||||
if: |
|
||||
always() &&
|
||||
needs.detect-changes.outputs.protected_files_changed == 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
environment:
|
||||
@@ -61,6 +122,7 @@ jobs:
|
||||
|
||||
trigger-jenkins:
|
||||
needs:
|
||||
- check-labels
|
||||
- detect-changes
|
||||
- require-maintainer-approval
|
||||
|
||||
@@ -105,8 +167,24 @@ jobs:
|
||||
-H "X-Github-Hook-Installation-Target-Type: repository" \
|
||||
--data @/tmp/filtered_payload.json
|
||||
|
||||
cleanup:
|
||||
needs:
|
||||
- verify-signed-commits
|
||||
- check-labels
|
||||
- detect-changes
|
||||
- require-maintainer-approval
|
||||
- trigger-jenkins
|
||||
|
||||
if: always() && github.event_name == 'pull_request_target'
|
||||
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Remove retrigger-ci label
|
||||
if: always() && github.event.action == 'labeled' && github.event.label.name == vars.RETRIGGER_CI_LABEL
|
||||
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "${{ vars.RETRIGGER_CI_LABEL }}"
|
||||
run: |
|
||||
PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels) }}' | jq -r '.[].name')
|
||||
if echo "$PR_LABELS" | grep -qxF "${{ vars.RETRIGGER_CI_LABEL }}"; then
|
||||
gh pr edit ${{ github.event.pull_request.number }} --remove-label "${{ vars.RETRIGGER_CI_LABEL }}" --repo ${{ github.repository }}
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -383,6 +383,12 @@ endif()
|
||||
add_boolean_option(ENABLE_IMSCOPE OFF "Enable phy scope based on imgui" OFF)
|
||||
add_boolean_option(ENABLE_IMSCOPE_RECORD OFF "Enable recording IQ data for imscope" OFF)
|
||||
|
||||
#########################
|
||||
##### E3 AGENT
|
||||
#########################
|
||||
set(E3_AGENT "OFF" CACHE STRING "O-RAN nGRG E3 Agent for dApps")
|
||||
set_property(CACHE E3_AGENT PROPERTY STRINGS "ON" "OFF")
|
||||
|
||||
##################################################
|
||||
# ASN.1 grammar C code generation & dependencies #
|
||||
##################################################
|
||||
@@ -855,7 +861,6 @@ set(NR_PHY_SRC_RU
|
||||
${OPENAIR1_DIR}/PHY/MODULATION/nr_beamforming.c
|
||||
${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_nr.c
|
||||
${OPENAIR1_DIR}/PHY/INIT/nr_init_ru.c
|
||||
${OPENAIR1_DIR}/PHY/if4_tools.c
|
||||
)
|
||||
|
||||
set(PHY_SRC_UE
|
||||
@@ -1206,6 +1211,7 @@ set (MAC_NR_SRC
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler_uci.c
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler_srs.c
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler_RA.c
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler_pcch.c
|
||||
${NR_GNB_MAC_DIR}/mac_rrc_dl_handler.c
|
||||
${NR_GNB_MAC_DIR}/mac_rrc_ul_direct.c
|
||||
${NR_GNB_MAC_DIR}/mac_rrc_ul_f1ap.c
|
||||
@@ -1768,23 +1774,25 @@ target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs
|
||||
# force the generation of ASN.1 so that we don't need to wait during the build
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE
|
||||
asn1_lte_rrc asn1_s1ap asn1_m2ap asn1_m3ap asn1_x2ap)
|
||||
# nr RRU
|
||||
add_executable(nr-oru
|
||||
${OPENAIR_DIR}/executables/nr-ru.c
|
||||
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c
|
||||
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
|
||||
${OPENAIR_DIR}/executables/main_nr_ru.c
|
||||
)
|
||||
target_link_libraries(nr-oru PRIVATE
|
||||
UTIL NR_PHY_RU PHY_NR shlib_loader dl
|
||||
radio_common softmodem_common nfapi_pnf_lib)
|
||||
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
|
||||
barrier actor nfapi_user_lib)
|
||||
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
|
||||
|
||||
if (OAI_RU_FRONTHAUL)
|
||||
add_executable(nr-oru
|
||||
${OPENAIR_DIR}/executables/nr-ru.c
|
||||
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c
|
||||
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
|
||||
${OPENAIR_DIR}/executables/main_nr_ru.c
|
||||
${OPENAIR_DIR}/executables/nr-oru.c
|
||||
)
|
||||
target_link_libraries(nr-oru PRIVATE
|
||||
UTIL NR_PHY_RU PHY_NR shlib_loader dl oru_fh MAC_NR_COMMON
|
||||
radio_common softmodem_common nfapi_pnf_lib)
|
||||
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
|
||||
barrier actor nfapi_user_lib)
|
||||
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
|
||||
endif()
|
||||
# nr-softmodem
|
||||
###################################################
|
||||
|
||||
@@ -1829,6 +1837,11 @@ if(E2_AGENT)
|
||||
target_compile_definitions(nr-softmodem PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
|
||||
endif()
|
||||
|
||||
if (E3_AGENT)
|
||||
target_link_libraries(nr-softmodem PRIVATE e3ap)
|
||||
target_compile_definitions(nr-softmodem PRIVATE E3_AGENT)
|
||||
endif()
|
||||
|
||||
|
||||
# force the generation of ASN.1 so that we don't need to wait during the build
|
||||
target_link_libraries(nr-softmodem PRIVATE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Contributing to OpenAirInterface
|
||||
# Contributing to Duranta
|
||||
|
||||
We want to make contributing to this project as easy and transparent as possible.
|
||||
|
||||
@@ -18,41 +18,30 @@ We want to make contributing to this project as easy and transparent as possible
|
||||
|
||||
## Commit Guidelines
|
||||
|
||||
Every pull request must pass two CI checks before it can be merged:
|
||||
|
||||
1. **[Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)**:
|
||||
Each commit must include a `Signed-off-by:` trailer in the commit message.
|
||||
Use `git commit -s` (or `--signoff`).
|
||||
|
||||
2. **[Verified commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)**:
|
||||
Each commit must be cryptographically signed using SSH or GPG keys to confirm
|
||||
its origin.
|
||||
|
||||
### Signing Commits
|
||||
|
||||
To sign commits:
|
||||
|
||||
You can also get the verified label on your commits via using [SSH keys or GPG
|
||||
keys](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
|
||||
|
||||
```
|
||||
# Edit .git/config in the git repository you are working on
|
||||
# Add the user section
|
||||
[user]
|
||||
name = YOUR NAME
|
||||
email = YOUR EMAIL ADDRESS
|
||||
|
||||
# If you use a signing key, use the below configuration instead
|
||||
[user]
|
||||
name = YOUR NAME
|
||||
email = YOUR EMAIL ADDRESS
|
||||
signingkey = LOCATION OF SSH KEYS or GPG KEY
|
||||
|
||||
[gpg]
|
||||
format = ssh
|
||||
|
||||
[commit]
|
||||
gpgsign = true
|
||||
```
|
||||
|
||||
> **NOTE:** If your commits are not signed the CI framework will not accept the PR.
|
||||
GitHub supports commit signing using either SSH keys or GPG keys. For the
|
||||
step-by-step setup (key generation, Git configuration, registering the key on
|
||||
GitHub, and verifying signatures locally), see the
|
||||
[commit signing section of the Git guide](doc/git-guide.md#setting-up-commit-signing).
|
||||
|
||||
> **NOTE:** If your commits are not signed, the CI framework will not accept the PR.
|
||||
For more information regarding contribution guidelines
|
||||
please check [this document](doc/code-style-contrib.md)
|
||||
|
||||
## License
|
||||
|
||||
By contributing to OpenAirInterface, you agree that your contributions will be
|
||||
By contributing to Duranta, you agree that your contributions will be
|
||||
licensed under
|
||||
|
||||
1. [CSSL v1.0 license](LICENSES/preferred/CSSL-v1.0.txt): for RAN and UE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<h1 align="center">
|
||||
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-OAI-Combined.png" alt="Duranta OAI" width="550"></a>
|
||||
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="Duranta OAI" width="550"></a>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
|
||||
61
ROADMAP.md
Normal file
61
ROADMAP.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Duranta-OpenAirInterface Roadmap
|
||||
|
||||
## RAN Roadmap
|
||||
|
||||
### Q2 2026
|
||||
|
||||
- GPU LDPC Accelerator
|
||||
- NR-DC Support
|
||||
- CN Paging procedures
|
||||
- Support for 64 UEs with E2E throughput validation
|
||||
- Basic Cat-A OAI O-RU (simulated radio)
|
||||
|
||||
### Q3 2026
|
||||
|
||||
- QoS-enforcing scheduler
|
||||
- Xn Handover Support
|
||||
- UL MU-MIMO
|
||||
- Aperiodic UL channels (SRS/CSI reporting)
|
||||
- NR user plane protocol
|
||||
- Multi-cell support in L2
|
||||
- Support for 128 UEs with E2E throughput validation
|
||||
|
||||
### Q4 2026
|
||||
|
||||
- Cat-B Support for FHI 7.2 at RU/DU
|
||||
- RAN Paging
|
||||
- Support of 5G RAN Slicing
|
||||
- Multi-cell performance evaluation (with at-least 3 cells)
|
||||
|
||||
### Q1 2027
|
||||
|
||||
- Digital Beamforming Support
|
||||
- DL MU-MIMO at L1
|
||||
|
||||
---
|
||||
|
||||
## UE Roadmap
|
||||
|
||||
### Q2 2026
|
||||
|
||||
- Support for Handover Procedures
|
||||
- Basic Sidelink Procedures (PSSCH, PSCCH)
|
||||
- PUCCH formats 1&3
|
||||
|
||||
### Q3 2026
|
||||
|
||||
- Support for 2 UL layers
|
||||
- Support for 2 DL layers
|
||||
- RU sharing
|
||||
|
||||
### Q4 2026
|
||||
|
||||
- Reduce feedback time
|
||||
- AT command interface
|
||||
- DL KPI Improvements
|
||||
- UL KPI Improvements
|
||||
|
||||
### Q1 2027
|
||||
|
||||
- Scan carrier
|
||||
- Power control procedures for outdoor operation
|
||||
@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm subchart for 4G physims network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm subchart for 5G physims network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -48,7 +48,6 @@ def nsaTDDB200Status = ''
|
||||
def saTDDB200Status = ''
|
||||
def phytestLDPCoffloadStatus = ''
|
||||
def saAW2SStatus = ''
|
||||
def cn5gCOTSUESanityCheck = ''
|
||||
def saAERIALStatus = ''
|
||||
def saMultiAntennaStatus = ''
|
||||
def saFHI72Status = ''
|
||||
@@ -712,29 +711,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ("Sanity-Check OAI-CN5G") {
|
||||
when { expression {do5Gtest} }
|
||||
steps {
|
||||
script {
|
||||
triggerCN5GDownstreamJob ('OAI-CN5G-COTS-UE-Test')
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
// Using a unique variable name for each test stage to avoid overwriting on a global variable
|
||||
// due to parallel-time concurrency
|
||||
cn5gCOTSUESanityCheck = finalizeDownstreamJob('OAI-CN5G-COTS-UE-Test')
|
||||
}
|
||||
}
|
||||
failure {
|
||||
script {
|
||||
currentBuild.result = 'FAILURE'
|
||||
failingStages += cn5gCOTSUESanityCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ("SA-AERIAL-CN5G") {
|
||||
when { expression {do5Gtest} }
|
||||
steps {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define N_ANTENNA_UL 1
|
||||
#define TDD 1
|
||||
|
||||
log_options: "all.level=debug,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
|
||||
log_options: "all.level=info,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
|
||||
log_filename: "/tmp/ue.log",
|
||||
|
||||
/* Enable remote API and Web interface */
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define N_ANTENNA_UL 1
|
||||
#define TDD 1
|
||||
|
||||
log_options: "all.level=debug,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
|
||||
log_options: "all.level=info,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
|
||||
log_filename: "/tmp/ue.log",
|
||||
|
||||
/* Enable remote API and Web interface */
|
||||
|
||||
@@ -217,11 +217,19 @@ amarisoft_00105_40MHz:
|
||||
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_fhi72_mplane_40MHz/multi-00105-40.cfg &
|
||||
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
|
||||
NetworkScript: ip netns exec ue1 ip a show dev pdn0
|
||||
Tracing:
|
||||
Start: '' # nothing to be done
|
||||
Stop: '' # nothing to be done
|
||||
Collect: 'cp /tmp/ue.log %%log_dir%%'
|
||||
amarisoft_00105_100MHz:
|
||||
Host: amariue
|
||||
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_fhi72_mplane_100MHz/multi-00105-100.cfg &
|
||||
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
|
||||
NetworkScript: ip netns exec ue1 ip a show dev pdn0
|
||||
Tracing:
|
||||
Start: '' # nothing to be done
|
||||
Stop: '' # nothing to be done
|
||||
Collect: 'cp /tmp/ue.log %%log_dir%%'
|
||||
amarisoft_ue_1:
|
||||
Host: amariue
|
||||
AttachScript: /root/lteue-linux-2025-03-15/ws.js 127.0.0.1:9002 '{"message":"power_on","ue_id":1}'
|
||||
|
||||
@@ -95,13 +95,13 @@ class Analysis():
|
||||
test_summary['Nbfail'] = nb_failed
|
||||
return nb_failed == 0, test_summary, test_result
|
||||
|
||||
def analyze_rt_stats(thresholds, L1_stats, MAC_stats):
|
||||
def analyze_rt_stats(thresholds, stat_files):
|
||||
with open(thresholds, 'r') as f:
|
||||
datalog_rt_stats = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
rt_keys = datalog_rt_stats['Ref']
|
||||
real_time_stats = {}
|
||||
for sf in [L1_stats, MAC_stats]:
|
||||
for sf in stat_files:
|
||||
with open(sf, 'r') as f:
|
||||
for line in f.readlines():
|
||||
for k in rt_keys:
|
||||
|
||||
@@ -401,7 +401,7 @@ class Containerize():
|
||||
# I would like to run it with --rm and mount the ctest result directory to avoid 'docker cp'
|
||||
# below, but then permissions are messed up and we can't remove the directory without sudo
|
||||
# making the next pipeline fail
|
||||
ret = cmd.run(f'docker run -a STDOUT {runtime_opt} --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ran-unittests ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc) {ctest_opt}')
|
||||
ret = cmd.run(f'docker run -a STDOUT {runtime_opt} --shm-size=2g --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ran-unittests ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc) {ctest_opt}')
|
||||
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTest.log .')
|
||||
archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTest.log')
|
||||
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTestsFailed.log .')
|
||||
@@ -644,7 +644,7 @@ class Containerize():
|
||||
logging.error('\u001B[1m Undeploying objects Failed\u001B[0m')
|
||||
return success
|
||||
|
||||
def AnalyzeRTStatsObject(self, HTML, node, ctx, thresholds, service=None):
|
||||
def AnalyzeRTStatsObject(self, HTML, node, ctx, thresholds, service=None, stats_files=None):
|
||||
logging.info(f'Analyzing realtime stats from server: {node}')
|
||||
yaml = self.yamlPath.strip('/')
|
||||
wd = f'{self.workspace}/{yaml}'
|
||||
@@ -660,12 +660,20 @@ class Containerize():
|
||||
raise RuntimeError(f"Requested service {s} not found among services: {deployed_services}")
|
||||
logging.info(f"Analyzing deployed service '{s}'")
|
||||
# similar to BuildRunTests(), use docker cp to avoid problems with permissions
|
||||
cmd.run(f'docker compose -f {wd_yaml} cp {s}:/opt/oai-gnb/nrL1_stats.log {wd}/')
|
||||
l1_file = archiveArtifact(cmd, ctx, f"{wd}/nrL1_stats.log")
|
||||
cmd.run(f'docker compose -f {wd_yaml} cp {s}:/opt/oai-gnb/nrMAC_stats.log {wd}/')
|
||||
mac_file = archiveArtifact(cmd, ctx, f"{wd}/nrMAC_stats.log")
|
||||
local_files = []
|
||||
for sf in stats_files:
|
||||
basename = os.path.basename(sf)
|
||||
ret = cmd.run(f'docker compose -f {wd_yaml} cp {s}:{sf} {wd}/')
|
||||
if ret.returncode != 0:
|
||||
logging.error(f"Cannot retrieve {s}:{sf}")
|
||||
return False
|
||||
file = archiveArtifact(cmd, ctx, f"{wd}/{basename}")
|
||||
if not file:
|
||||
logging.error(f"Cannot retrieve file {basename}")
|
||||
return False
|
||||
local_files.append(file)
|
||||
|
||||
logging.info(f"check against thresholds from {thresholds}")
|
||||
success, datalog_rt_stats = cls_analysis.Analysis.analyze_rt_stats(thresholds, l1_file, mac_file)
|
||||
success, datalog_rt_stats = cls_analysis.Analysis.analyze_rt_stats(thresholds, local_files)
|
||||
HTML.CreateHtmlDataLogTable(datalog_rt_stats)
|
||||
return success
|
||||
|
||||
@@ -75,12 +75,12 @@ class HTMLManagement():
|
||||
self.htmlFile.write(' <tr style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <a href="http://www.openairinterface.org/">\n')
|
||||
self.htmlFile.write(' <img src="http://www.openairinterface.org/wp-content/uploads/2016/03/cropped-oai_final_logo2.png" alt="" border="none" height=50 width=150>\n')
|
||||
self.htmlFile.write(' <img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="" border="none" style="margin-right: 2rem;" width=150>\n')
|
||||
self.htmlFile.write(' </img>\n')
|
||||
self.htmlFile.write(' </a>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none; vertical-align: center;">\n')
|
||||
self.htmlFile.write(' <b><font size = "6">Job Summary -- Job: TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
|
||||
self.htmlFile.write(' <b><font size = "6">TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
|
||||
@@ -184,7 +184,8 @@ L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
}
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
|
||||
@@ -184,7 +184,8 @@ L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
}
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
|
||||
@@ -216,6 +216,7 @@ L1s =
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ L1s =
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ L1s =
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ L1s =
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 200;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
222
ci-scripts/conf_files/gnb.band79.106prb.vrtsim.phytest-dora.conf
Normal file
222
ci-scripts/conf_files/gnb.band79.106prb.vrtsim.phytest-dora.conf
Normal file
@@ -0,0 +1,222 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({mcc = 208; mnc = 99; mnc_length = 2;});
|
||||
|
||||
tr_s_preference = "local_mac"
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
min_rxtxtime = 6;
|
||||
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
|
||||
physCellId = 0;
|
||||
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 4412.64 MHz
|
||||
absoluteFrequencySSB = 694176;
|
||||
dl_frequencyBand = 79;
|
||||
# this is 4400.94 MHz
|
||||
dl_absoluteFrequencyPointA = 693396;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=41,L=24 (275*(L-1))+RBstart
|
||||
initialDLBWPlocationAndBandwidth = 6368;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 79;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 6368;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 98;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 13;
|
||||
preambleReceivedTargetPower = -118;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
|
||||
#one (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
|
||||
msg3_DeltaPreamble = 1;
|
||||
p0_NominalWithGrant =-90;
|
||||
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
|
||||
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
|
||||
ssPBCH_BlockPower = -25;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// MME parameters:
|
||||
mme_ip_address = ({ ipv4 = "192.168.12.26"; port = 36412; });
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
|
||||
GNB_IPV4_ADDRESS_FOR_S1_MME = "192.168.12.111/24";
|
||||
GNB_IPV4_ADDRESS_FOR_S1U = "192.168.12.111/24";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
});
|
||||
|
||||
L1s = (
|
||||
{
|
||||
tr_n_preference = "local_mac";
|
||||
});
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1
|
||||
nb_rx = 1
|
||||
att_tx = 0
|
||||
att_rx = 0;
|
||||
bands = [7];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 50;
|
||||
eNB_instances = [0];
|
||||
});
|
||||
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
};
|
||||
|
||||
32
ci-scripts/datalog_rt_stats.gnb.40.vrtsim.yaml
Normal file
32
ci-scripts/datalog_rt_stats.gnb.40.vrtsim.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
#this is a configuration file
|
||||
#used to build real time processing statistics
|
||||
#for 5G NR phy test (gNB terminate)
|
||||
Title : gNB Processing Time (us) from datalog_rt_stats.default.yaml
|
||||
ColNames :
|
||||
- Metric
|
||||
- Average; Max; Count
|
||||
- Average vs Reference Deviation (Reference Value; Acceptability Deviation Threshold)
|
||||
Ref :
|
||||
L1 Tx processing : 190.0
|
||||
DLSCH encoding : 120.0
|
||||
L1 Rx processing : 235.0
|
||||
UL segments decoding : 120.0
|
||||
PUSCH inner-receiver : 150.0
|
||||
UL Indication : 2.0
|
||||
Slot Indication : 10.0
|
||||
feprx : 40.0
|
||||
feptx_ofdm (per port, half_slot) : 30
|
||||
feptx_total : 52
|
||||
DeviationThreshold :
|
||||
L1 Tx processing : 0.25
|
||||
DLSCH encoding : 0.25
|
||||
L1 Rx processing : 0.25
|
||||
UL segments decoding : 0.25
|
||||
PUSCH inner-receiver : 0.25
|
||||
UL Indication : 1.00
|
||||
Slot Indication : 0.50
|
||||
feprx : 0.25
|
||||
feptx_ofdm (per port, half_slot) : 0.25
|
||||
feptx_total : 0.25
|
||||
40
ci-scripts/datalog_rt_stats.ue.40.vrtsim.yaml
Normal file
40
ci-scripts/datalog_rt_stats.ue.40.vrtsim.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
#this is a configuration file
|
||||
#used to build real time processing statistics
|
||||
#for 5G NR phy test (gNB terminate)
|
||||
Title : UE Processing Time (us) from datalog_rt_stats.ue.40.vrtsim.yaml
|
||||
ColNames :
|
||||
- Metric
|
||||
- Average; Max; Count
|
||||
- Average vs Reference Deviation (Reference Value; Acceptability Deviation Threshold)
|
||||
Ref :
|
||||
RX_PDSCH_STATS : 62.0
|
||||
DLSCH_RX_PDCCH_STATS : 30.5
|
||||
RX_DFT_STATS : 5.5
|
||||
DLSCH_CHANNEL_ESTIMATION_STATS : 39.0
|
||||
DLSCH_DECODING_STATS : 178.0
|
||||
DLSCH_LDPC_DECODING_STATS : 78.0
|
||||
DLSCH_LLR_STATS : 25.0
|
||||
DLSCH_LAYER_DEMAPPING : 8.5
|
||||
DLSCH_PROCEDURES_STATS : 188.0
|
||||
PHY_PROC_TX : 158.0
|
||||
PUSCH_PROC_STATS : 167.0
|
||||
ULSCH_LDPC_ENCODING_STATS : 43.0
|
||||
ULSCH_ENCODING_STATS : 121.0
|
||||
OFDM_MOD_STATS : 44.5
|
||||
DeviationThreshold :
|
||||
RX_PDSCH_STATS : 0.25
|
||||
DLSCH_RX_PDCCH_STATS : 0.25
|
||||
RX_DFT_STATS : 0.25
|
||||
DLSCH_CHANNEL_ESTIMATION_STATS : 0.25
|
||||
DLSCH_DECODING_STATS : 0.25
|
||||
DLSCH_LDPC_DECODING_STATS : 0.25
|
||||
DLSCH_LLR_STATS : 0.25
|
||||
DLSCH_LAYER_DEMAPPING : 0.5
|
||||
DLSCH_PROCEDURES_STATS : 0.25
|
||||
PHY_PROC_TX : 0.25
|
||||
PUSCH_PROC_STATS : 0.25
|
||||
ULSCH_LDPC_ENCODING_STATS : 0.25
|
||||
ULSCH_ENCODING_STATS : 0.25
|
||||
OFDM_MOD_STATS : 0.25
|
||||
@@ -10,8 +10,7 @@
|
||||
FROM ran-base:develop AS ran-tests
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
|
||||
libgtest-dev \
|
||||
libyaml-cpp-dev \
|
||||
libzmq3-dev \
|
||||
@@ -26,7 +25,9 @@ WORKDIR /oai-ran
|
||||
COPY . .
|
||||
|
||||
WORKDIR /oai-ran/build
|
||||
RUN cmake .. -GNinja \
|
||||
RUN --mount=type=cache,target=/root/.cache/ccache/ \
|
||||
--mount=type=cache,target=/root/.cache/cpm/ \
|
||||
cmake .. -GNinja \
|
||||
-DENABLE_TESTS=ON -DOAI_ZMQ=ON -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DSANITIZE_ADDRESS=True -DOAI_VRTSIM_TAPS_CLIENT=ON -DOAI_RU_FRONTHAUL=ON \
|
||||
&& \
|
||||
|
||||
@@ -245,7 +245,8 @@ def ExecuteActionWithParam(action, ctx, node):
|
||||
elif action == 'AnalyzeRTStats_Object':
|
||||
yaml = test.findtext('stats_cfg')
|
||||
service = test.findtext('service')
|
||||
success = CONTAINERS.AnalyzeRTStatsObject(HTML, node, ctx, yaml, service)
|
||||
stats_files = (test.findtext('stats_file') or '').split()
|
||||
success = CONTAINERS.AnalyzeRTStatsObject(HTML, node, ctx, yaml, service, stats_files)
|
||||
|
||||
else:
|
||||
logging.warning(f"unknown action {action}, skip step")
|
||||
|
||||
@@ -145,6 +145,6 @@ class RANManagement():
|
||||
mac_file = archiveArtifact(cmd, ctx, f"{logdir}/nrMAC_stats.log")
|
||||
|
||||
logging.info(f"check against thresholds from {thresholds}")
|
||||
success, datalog_rt_stats = cls_analysis.Analysis.analyze_rt_stats(thresholds, l1_file, mac_file)
|
||||
success, datalog_rt_stats = cls_analysis.Analysis.analyze_rt_stats(thresholds, [l1_file, mac_file])
|
||||
HTML.CreateHtmlDataLogTable(datalog_rt_stats)
|
||||
return success
|
||||
|
||||
@@ -26,7 +26,7 @@ class TestAnalysis(unittest.TestCase):
|
||||
rtsf = "datalog_rt_stats.100.2x2.yaml"
|
||||
l1f = "tests/analysis/gnb_phytest.success.nrL1.log"
|
||||
macf = "tests/analysis/gnb_phytest.success.nrMAC.log"
|
||||
status, s = cls_analysis.Analysis.analyze_rt_stats(rtsf, l1f, macf)
|
||||
status, s = cls_analysis.Analysis.analyze_rt_stats(rtsf, [l1f, macf])
|
||||
self.assertTrue(status)
|
||||
self.assertEqual(s['Title'], "Processing Time (us) from datalog_rt_stats.100.2x2.yaml")
|
||||
self.assertEqual(s['ColNames'], ["Metric", "Average; Max; Count", "Average vs Reference Deviation (Reference Value; Acceptability Deviation Threshold)"])
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<desc>Analyze Real-Time Stats</desc>
|
||||
<stats_cfg>datalog_rt_stats.100.4x4.fhi72.yaml</stats_cfg>
|
||||
<service>oai-gnb</service>
|
||||
<stats_file>/opt/oai-gnb/nrL1_stats.log /opt/oai-gnb/nrMAC_stats.log</stats_file>
|
||||
<node>bulbul</node>
|
||||
</testCase>
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<desc>Analyze Real-Time Stats</desc>
|
||||
<stats_cfg>datalog_rt_stats.100.2x2.fhi72.cacofonix.yaml</stats_cfg>
|
||||
<service>oai-gnb</service>
|
||||
<stats_file>/opt/oai-gnb/nrL1_stats.log /opt/oai-gnb/nrMAC_stats.log</stats_file>
|
||||
<node>cacofonix</node>
|
||||
</testCase>
|
||||
|
||||
|
||||
82
ci-scripts/xml_files/phytest_vrtsim_40MHz.xml
Normal file
82
ci-scripts/xml_files/phytest_vrtsim_40MHz.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
|
||||
|
||||
<testCaseList>
|
||||
<htmlTabRef>PHY-Test-40-vrtsim</htmlTabRef>
|
||||
<htmlTabName>Timing phytest 40 MHz with vrtsim</htmlTabName>
|
||||
<htmlTabIcon>tasks</htmlTabIcon>
|
||||
|
||||
<testCase>
|
||||
<class>Pull_Cluster_Image</class>
|
||||
<desc>Pull Images from Cluster</desc>
|
||||
<images>oai-gnb oai-nr-ue</images>
|
||||
<node>caracal</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Create_Workspace</class>
|
||||
<desc>Create new Workspace</desc>
|
||||
<node>caracal</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Initialize gNB</desc>
|
||||
<node>caracal</node>
|
||||
<yaml_path>ci-scripts/yaml_files/phytest_vrtsim_40MHz</yaml_path>
|
||||
<services>oai-gnb</services>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Initialize UE</desc>
|
||||
<node>caracal</node>
|
||||
<yaml_path>ci-scripts/yaml_files/phytest_vrtsim_40MHz</yaml_path>
|
||||
<services>oai-nr-ue</services>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>IdleSleep</class>
|
||||
<desc>Sleep</desc>
|
||||
<idle_sleep_time_in_sec>60</idle_sleep_time_in_sec>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>AnalyzeRTStats_Object</class>
|
||||
<desc>Analyze Real-Time Stats on gNB</desc>
|
||||
<always_exec>true</always_exec>
|
||||
<stats_cfg>datalog_rt_stats.gnb.40.vrtsim.yaml</stats_cfg>
|
||||
<service>oai-gnb</service>
|
||||
<stats_file>/opt/oai-gnb/nrL1_stats.log /opt/oai-gnb/nrMAC_stats.log</stats_file>
|
||||
<node>caracal</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>AnalyzeRTStats_Object</class>
|
||||
<desc>Analyze Real-Time Stats on UE</desc>
|
||||
<always_exec>true</always_exec>
|
||||
<stats_cfg>datalog_rt_stats.ue.40.vrtsim.yaml</stats_cfg>
|
||||
<service>oai-nr-ue</service>
|
||||
<stats_file>/opt/oai-nr-ue/nrL1_UE_stats-0.log</stats_file>
|
||||
<node>caracal</node>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Undeploy_Object</class>
|
||||
<node>caracal</node>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Terminate gNB</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/phytest_vrtsim_40MHz</yaml_path>
|
||||
<analysis>
|
||||
<services>oai-gnb=EndsWithBye oai-nr-ue=EndsWithBye</services>
|
||||
</analysis>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Clean_Test_Server_Images</class>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Clean Test Images on Test Server</desc>
|
||||
<node>caracal</node>
|
||||
<images>oai-nr-ue oai-gnb</images>
|
||||
</testCase>
|
||||
|
||||
</testCaseList>
|
||||
@@ -1,20 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Full Stack 4G-LTE RF simulation with containers</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This page is only valid for an `Ubuntu 22` host.
|
||||
OAI Full Stack 4G-LTE RF simulation with containers
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Full Stack 5G-NR RF simulation with containers</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This page is only valid for an `Ubuntu 22` host.
|
||||
|
||||
**NOTE: this version (2023-01-27) has been updated for the `v1.5.0` version of the `OAI 5G CN`.**
|
||||
OAI Full Stack 5G-NR RF simulation with containers
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
services:
|
||||
oai-gnb:
|
||||
image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-gnb
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SYS_NICE
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --phy-test --device.name vrtsim --vrtsim.role server -q -U 768 -T 106 -t 23 -D 127 -m 28 -M 106 --log_config.global_log_options level,nocolor,time
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
ipc: host
|
||||
volumes:
|
||||
- ../../conf_files/gnb.band79.106prb.vrtsim.phytest-dora.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
- rrc.config:/opt/oai-gnb/
|
||||
- tmp_data:/tmp/
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-nr-ue:
|
||||
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-nr-ue
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SYS_NICE
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --phy-test --reconfig-file etc/rrc/reconfig.raw --rbconfig-file etc/rrc/rbconfig.raw --device.name vrtsim --vrtsim.role client -q --log_config.global_log_options level,nocolor,time
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
ipc: host
|
||||
volumes:
|
||||
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
|
||||
- rrc.config:/opt/oai-nr-ue/etc/rrc/
|
||||
- tmp_data:/tmp/
|
||||
depends_on:
|
||||
- oai-gnb
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-uesoftmodem"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
rrc.config:
|
||||
tmp_data:
|
||||
@@ -1,18 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI O-RAN 7.2 Front-haul Docker Compose</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
OAI O-RAN 7.2 Front-haul Docker Compose
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ Options:
|
||||
Sets build directory (will be <oai-root>/cmake_targets/<build-dir>/build)
|
||||
--build-e2
|
||||
Enable the the E2 Agent
|
||||
--build-e3
|
||||
Enable the the E3 Agent
|
||||
-I | --install-external-packages
|
||||
Installs required packages such as LibXML, asn1.1 compiler, ...
|
||||
This option will require root password
|
||||
@@ -143,6 +145,10 @@ function main() {
|
||||
CMAKE_CMD="$CMAKE_CMD -DE2_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
--build-e3 )
|
||||
CMAKE_CMD="$CMAKE_CMD -DE3_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
-I | --install-external-packages)
|
||||
INSTALL_EXTERNAL=1
|
||||
echo_info "Will install external packages"
|
||||
@@ -201,6 +207,7 @@ function main() {
|
||||
shift;;
|
||||
--nrRU)
|
||||
NRRU=1
|
||||
CMAKE_CMD="$CMAKE_CMD -DOAI_RU_FRONTHAUL=ON"
|
||||
TARGET_LIST="$TARGET_LIST nr-oru"
|
||||
echo_info "Will compile NR O-RU"
|
||||
shift;;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Finds the xran library. Note that the library number is as follows:
|
||||
# - oran_e_maintenance_release_v1.5 -> 5.1.6
|
||||
# - oran_f_release_v1.3 -> 6.1.4
|
||||
# - oran_k_release_v1.1 -> 11.1.1
|
||||
# - oran_k_release_v1.3 -> 11.1.3
|
||||
#
|
||||
# Required options
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -494,8 +494,8 @@ install_simde_from_source(){
|
||||
if [[ -v SIMDE_VERSION ]]; then
|
||||
git checkout -f $SIMDE_VERSION
|
||||
else
|
||||
# At time of writing, last working commit for OAI: c7f26b7
|
||||
git checkout c7f26b73ba8e874b95c2cec2b497826ad2188f68
|
||||
# At time of writing, last working commit for OAI: 1c68d9a
|
||||
git checkout 1c68d9ad60bf63f3fb527c4ee3b2319d828ffcc6
|
||||
fi
|
||||
# Showing which version is used
|
||||
git log -n1
|
||||
|
||||
@@ -39,8 +39,9 @@ typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
|
||||
/* 5QI (5G QoS Identifier) - 3GPP TS 23.501 §5.7.2.1
|
||||
* Range: 0..255
|
||||
* - Standardized 5QI values: have one-to-one mapping to standardized 5G QoS characteristics (Table 5.7.4-1)
|
||||
* - Pre-configured 5QI values: pre-configured in the AN
|
||||
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile */
|
||||
* - Pre-configured 5QI values: pre-configured in the AN (not in Table 5.7.4-1)
|
||||
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile
|
||||
* OAI implements standardized non-dynamic 5QI only. */
|
||||
#define MIN_FIVEQI 0
|
||||
#define MAX_STANDARDIZED_FIVEQI 90
|
||||
#define MAX_FIVEQI 255
|
||||
|
||||
@@ -45,10 +45,20 @@
|
||||
|
||||
#define NR_MAX_NB_PDU_SESSIONS (256)
|
||||
|
||||
#define NR_MAX_NB_ALLOWED_SNSSAI (8) /* Maximum number of allowed S-NSSAI in TS 38.413 */
|
||||
|
||||
#define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE
|
||||
* defined for NGAP in TS 38.413 - maxnoofDRBs */
|
||||
#define MAX_PDUS_PER_UE (8) /* Maximum number of PDU Sessions per UE */
|
||||
|
||||
/** Maximum value of nrofPDCCH-MonitoringOccasionPerSSB-InPO-r16 (TS 38.331 PCCH-Config) */
|
||||
#define NR_PCCH_MAX_MO_PER_SSB_IN_PO 4
|
||||
|
||||
/** Maximum number of Paging Occasions per Paging Frame (TS 38.331 PCCH-Config) */
|
||||
#define NR_PCCH_MAX_PO 4
|
||||
|
||||
#define NR_PHYS_CELL_ID_MAX 1007 /* Maximum Physical Cell ID (0..1007) */
|
||||
|
||||
#define NB_RB_MBMS_MAX (29 * 16) /* 29 = LTE_maxSessionPerPMCH + 16 = LTE_maxServiceCount from LTE_asn_constant.h */
|
||||
|
||||
#define NB_RAB_MAX 11 /* from LTE_maxDRB in LTE_asn_constant.h */
|
||||
@@ -71,6 +81,9 @@
|
||||
// SDAP
|
||||
#define MAX_QOS_FLOWS 64
|
||||
|
||||
/** Maximum number of PagingRecords in one PCCH Paging message (TS 38.331) */
|
||||
#define NR_PCCH_MAX_PAGING_RECORDS 32
|
||||
|
||||
// SDAP/5G NAS NOS1
|
||||
#define DEFAULT_NOS1_PDU_ID 10
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
|
||||
LOG_T(<component>,<format>,<argument>,...)
|
||||
)
|
||||
```
|
||||
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functionnal module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
|
||||
these macros are used in place of the printf C function. The additional ***component*** parameter identifies the functional module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
|
||||
|
||||
| macro | level letter | level value | level name |
|
||||
|:---------|:---------------|:---------------|----------------:|
|
||||
|
||||
@@ -155,6 +155,7 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""};
|
||||
COMP_DEF(NFAPI_PNF, log) \
|
||||
COMP_DEF(ITTI, log) \
|
||||
COMP_DEF(UTIL, log) \
|
||||
COMP_DEF(E3AP, log) \
|
||||
COMP_DEF(MAX_LOG_PREDEF_COMPONENTS, )
|
||||
|
||||
#define COMP_ENUM(comp, file_extension) comp,
|
||||
|
||||
@@ -434,6 +434,27 @@ ID = LEGACY_MAC_TRACE
|
||||
GROUP = ALL:LEGACY_MAC:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_E3AP_INFO
|
||||
DESC = E3AP legacy logs - info level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_INFO:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_ERROR
|
||||
DESC = E3AP legacy logs - error level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_ERROR:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_WARNING
|
||||
DESC = E3AP legacy logs - warning level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_WARNING:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_DEBUG
|
||||
DESC = E3AP legacy logs - debug level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_DEBUG:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_TRACE
|
||||
DESC = E3AP legacy logs - trace level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_NR_MAC_INFO
|
||||
DESC = NR_MAC legacy logs - info level
|
||||
GROUP = ALL:LEGACY_NR_MAC:LEGACY_GROUP_INFO:LEGACY
|
||||
|
||||
@@ -257,11 +257,9 @@ uint32_t nr_timer_remaining_time(const NR_timer_t *timer);
|
||||
|
||||
int set_default_nta_offset(frequency_range_t freq_range, uint32_t samples_per_subframe);
|
||||
|
||||
static inline int get_num_dmrs(uint16_t dmrs_mask )
|
||||
static inline int get_num_dmrs(uint16_t dmrs_mask)
|
||||
{
|
||||
int num_dmrs=0;
|
||||
for (int i=0;i<16;i++) num_dmrs+=((dmrs_mask>>i)&1);
|
||||
return(num_dmrs);
|
||||
return __builtin_popcount(dmrs_mask);
|
||||
}
|
||||
|
||||
void warn_higher_threequarter_fs(const int n_rb, const int mu);
|
||||
|
||||
@@ -40,6 +40,9 @@ typedef struct ShmTDIQChannel_s {
|
||||
|
||||
ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant)
|
||||
{
|
||||
// Unlink any stale shared memory segment first to ensure we start fresh and reclaim space
|
||||
shm_unlink(name);
|
||||
|
||||
// Create shared memory segment
|
||||
int fd = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
AssertFatal(fd != -1, "shm_open failed: %s\n", strerror(errno));
|
||||
@@ -96,29 +99,48 @@ ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_secon
|
||||
{
|
||||
// Create shared memory segment
|
||||
int fd = -1;
|
||||
while (timeout_in_seconds > 0 && fd == -1) {
|
||||
double timeout_in_uS = timeout_in_seconds * 1000000.0;
|
||||
while (timeout_in_uS > 0 && fd == -1) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
fd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd != -1) {
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
timeout_in_uS -= 1000;
|
||||
}
|
||||
timeout_in_seconds--;
|
||||
if (fd == -1) {
|
||||
printf("Waiting for server to create shared memory segment\n");
|
||||
}
|
||||
}
|
||||
AssertFatal(fd != -1, "shm_open() failed: errno %d, %s", errno, strerror(errno));
|
||||
|
||||
// Map just the header first to wait for initialization
|
||||
ShmTDIQChannelData *shm_ptr = mmap(NULL, sizeof(ShmTDIQChannelData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (shm_ptr == MAP_FAILED) {
|
||||
perror("mmap header");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Wait until server initializes the segment
|
||||
while (timeout_in_uS > 0 && shm_ptr->magic != SHM_MAGIC_NUMBER) {
|
||||
usleep(1000);
|
||||
timeout_in_uS -= 1000;
|
||||
}
|
||||
AssertFatal(shm_ptr->magic == SHM_MAGIC_NUMBER, "Timeout waiting for server to initialize shared memory segment\n");
|
||||
|
||||
// Now that it's initialized, ftruncate has finished, so we can get the actual size
|
||||
struct stat buf;
|
||||
fstat(fd, &buf);
|
||||
size_t total_size = buf.st_size;
|
||||
|
||||
// Map shared memory segment to address space
|
||||
ShmTDIQChannelData *shm_ptr = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
// Unmap the temporary header mapping
|
||||
munmap(shm_ptr, sizeof(ShmTDIQChannelData));
|
||||
|
||||
// Map the entire shared memory segment
|
||||
shm_ptr = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (shm_ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
perror("mmap full");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -134,10 +156,6 @@ ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_secon
|
||||
channel->nb_rx_ant = channel->data->num_antennas_tx;
|
||||
printf("\033[38;5;208mnb_tx_ant, nb_rx_ant: %d, %d\n\033[0m", channel->nb_tx_ant, channel->nb_rx_ant);
|
||||
channel->type = IQ_CHANNEL_TYPE_CLIENT;
|
||||
while (shm_ptr->magic != SHM_MAGIC_NUMBER) {
|
||||
printf("Waiting for server to initialize shared memory\n");
|
||||
sleep(1);
|
||||
}
|
||||
close(fd);
|
||||
return channel;
|
||||
}
|
||||
@@ -238,7 +256,7 @@ int shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t
|
||||
fprintf(stderr, "Error: clock_gettime failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ts.tv_sec += timeout_uS / 1000000; // Convert microseconds to seconds
|
||||
ts.tv_nsec += (timeout_uS % 1000000) * 1000; // Convert remaining microseconds to nanoseconds
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <common/utils/assertions.h>
|
||||
#include <common/utils/LOG/log.h>
|
||||
#include "common_lib.h"
|
||||
|
||||
#ifdef T
|
||||
#undef T
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
|
||||
#include "openair2/RRC/NR/rrc_gNB_NGAP.h"
|
||||
#include "openair3/NGAP/ngap_gNB_ue_context.h"
|
||||
|
||||
#define TELNETSERVERCODE
|
||||
#include "telnetsrv.h"
|
||||
@@ -106,9 +108,50 @@ int rrc_gNB_trigger_release_all(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rrc_gNB_trigger_ue_context_release_req(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
{
|
||||
UNUSED(debug);
|
||||
int ue_id = -1;
|
||||
|
||||
if (!buf) {
|
||||
ue_id = get_single_ue_id();
|
||||
if (ue_id < 1) {
|
||||
prnt("No UE found!\n");
|
||||
ERROR_MSG_RET("No UE found!\n");
|
||||
}
|
||||
} else {
|
||||
char *end = NULL;
|
||||
errno = 0;
|
||||
long parsed_id = strtol(buf, &end, 10);
|
||||
if (end == buf || *end != '\0' || errno != 0 || parsed_id < 1 || parsed_id >= 0xfffffe) {
|
||||
ERROR_MSG_RET("UE ID needs to be [1,0xfffffe]\n");
|
||||
}
|
||||
ue_id = parsed_id;
|
||||
}
|
||||
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[0];
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, ue_id);
|
||||
if (!ue_context_p) {
|
||||
ERROR_MSG_RET("No RRC UE context for ue_id %d\n", ue_id);
|
||||
}
|
||||
if (!ngap_get_ue_context(ue_id)) {
|
||||
ERROR_MSG_RET("No NGAP UE context for ue_id %d\n", ue_id);
|
||||
}
|
||||
|
||||
ngap_cause_t cause = {
|
||||
.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.value = NGAP_CAUSE_RADIO_NETWORK_USER_INACTIVITY,
|
||||
};
|
||||
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, ue_context_p, cause);
|
||||
|
||||
prnt("Sent NGAP UE Context Release Request (user-inactivity) for ue_id %d\n", ue_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static telnetshell_cmddef_t rrc_cmds[] = {
|
||||
{"release_rrc", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_release},
|
||||
{"release_rrc_all", "", rrc_gNB_trigger_release_all},
|
||||
{"ctx_rel_req", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_ue_context_release_req},
|
||||
{"", "", NULL},
|
||||
};
|
||||
|
||||
|
||||
@@ -184,6 +184,18 @@ static bool set_if_flags(int sock_fd, const char *ifn, short flags)
|
||||
return true;
|
||||
}
|
||||
|
||||
short tuntap_set_up(const char *ifname, int sock_fd)
|
||||
{
|
||||
short flags = 0;
|
||||
if (!get_if_flags(sock_fd, ifname, &flags))
|
||||
return -1;
|
||||
|
||||
flags |= IFF_UP;
|
||||
if (!set_if_flags(sock_fd, ifname, flags))
|
||||
return -1;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool tun_config(const char* ifname, const char *ipv4, const char *ipv6)
|
||||
{
|
||||
@@ -215,10 +227,10 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6)
|
||||
close(sock_fd);
|
||||
}
|
||||
|
||||
// if successfully set IP addresses: set iterface up, disable ARP, no
|
||||
// multicast, point-to-point
|
||||
if (success)
|
||||
success = set_if_flags(sock_fd, ifname, (IFF_UP | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST);
|
||||
if (success) {
|
||||
const short flags = tuntap_set_up(ifname, sock_fd);
|
||||
success = flags >= 0 && set_if_flags(sock_fd, ifname, (flags | IFF_NOARP | IFF_POINTOPOINT) & ~IFF_MULTICAST);
|
||||
}
|
||||
|
||||
if (success)
|
||||
LOG_A(OIP, "TUN Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6);
|
||||
@@ -233,11 +245,11 @@ bool tap_config(const char* ifname)
|
||||
{
|
||||
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
LOG_E(UTIL, "Failed creating socket for interface management: %d, %s\n", errno, strerror(errno));
|
||||
LOG_E(UTIL, "tap_config: failed creating socket %d, %s\n", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = set_if_flags(sock_fd, ifname, IFF_UP);
|
||||
const bool success = tuntap_set_up(ifname, sock_fd) >= 0;
|
||||
|
||||
if (success)
|
||||
LOG_A(OIP, "TAP interface %s successfully configured\n", ifname);
|
||||
@@ -282,6 +294,7 @@ int tuntap_generate_ue_ifname(char *ifname, int flag, int instance_id, int pdu_s
|
||||
char pdu_session_string[10];
|
||||
snprintf(pdu_session_string, sizeof(pdu_session_string), "p%d", pdu_session_id);
|
||||
const char *basename = flag == IFF_TUN ? "oaitun_ue" : "oaitap_ue";
|
||||
// ifname: oaitun_ue<ue_id+1>[p<pdu_session_id>] when not default
|
||||
return snprintf(ifname, IFNAMSIZ, "%s%d%s", basename, instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string);
|
||||
}
|
||||
|
||||
@@ -295,7 +308,7 @@ void tuntap_destroy(const char *dev)
|
||||
}
|
||||
|
||||
// interface not up => down
|
||||
short flags;
|
||||
short flags = 0;
|
||||
bool success = get_if_flags(fd, dev, &flags);
|
||||
success = success && set_if_flags(fd, dev, flags & ~IFF_UP);
|
||||
if (success) {
|
||||
|
||||
@@ -90,4 +90,12 @@ int tuntap_alloc(int flag, const char *dev);
|
||||
*/
|
||||
void tuntap_destroy(const char *dev);
|
||||
|
||||
/*!
|
||||
* \brief Bring a TUN/TAP interface administratively up (IOCTL SIOCSIFFLAGS, set IFF_UP).
|
||||
* \param[in] ifname name of the interface
|
||||
* \param[in] sock_fd IPv4 SOCK_DGRAM fd for ioctl (opened by the caller)
|
||||
* \return interface flags with IFF_UP set, or -1 on failure
|
||||
*/
|
||||
short tuntap_set_up(const char *ifname, int sock_fd);
|
||||
|
||||
#endif /*TUN_IF_H_*/
|
||||
|
||||
36
doc/5Gnas.md
36
doc/5Gnas.md
@@ -17,17 +17,26 @@ Key 5GS NAS messages:
|
||||
|
||||
## OAI Implementation Status
|
||||
|
||||
The following tables lists implemented NAS messages and whether there is an encoder or decoder function, and if a corresponding unit test exists.
|
||||
The following table lists NAS messages with dedicated `encode_*` / `decode_*` codecs under
|
||||
`openair3/NAS/NR_UE/5GS/` (`5GMM/MSG`, `5GSM/MSG`). Unit test entries refer to
|
||||
[`nas_lib_test.c`](../openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c), most are encode/decode round-trips.
|
||||
|
||||
| Type | Message | Encoding | Decoding | Unit test |
|
||||
|-------|-------------------------------------------|----------|----------|------------|
|
||||
| 5GMM | Service Request | yes | yes | yes |
|
||||
| 5GMM | Service Accept | yes | yes | yes |
|
||||
| 5GMM | Service Reject | yes | yes | yes |
|
||||
| 5GMM | Authentication Failure | yes | yes | yes |
|
||||
| 5GMM | Authentication Reject | yes | yes | yes |
|
||||
| 5GMM | Security Mode Reject | yes | yes | yes |
|
||||
| 5GMM | Identity Request | no | yes | no |
|
||||
| 5GMM | Authentication Response | yes | no | no |
|
||||
| 5GMM | Identity Response | yes | no | no |
|
||||
| 5GMM | Security Mode Complete | yes | no | no |
|
||||
| 5GMM | Uplink NAS Transport | yes | no | no |
|
||||
| 5GMM | Authentication Failure | yes | yes | yes |
|
||||
| 5GMM | Authentication Reject | yes | yes | yes |
|
||||
| 5GMM | Security Mode Reject | yes | yes | yes |
|
||||
| 5GMM | Registration Request | yes | yes | no |
|
||||
| 5GMM | Registration Accept | yes | yes | yes |
|
||||
| 5GMM | Registration Complete | yes | yes | no |
|
||||
@@ -35,6 +44,23 @@ The following tables lists implemented NAS messages and whether there is an enco
|
||||
| 5GSM | PDU Session Establishment Request | yes | no | no |
|
||||
| 5GSM | PDU Session Establishment Accept | no | yes | no |
|
||||
|
||||
### Runtime-handled messages
|
||||
|
||||
These network-originated messages are handled in [`nr_nas_msg.c`](../openair3/NAS/NR_UE/nr_nas_msg.c):
|
||||
|
||||
* Authentication Request
|
||||
* Security Mode Command
|
||||
* Downlink NAS Transport
|
||||
* Deregistration Accept (UE originating)
|
||||
* Registration Reject
|
||||
* PDU Session Establishment Reject
|
||||
|
||||
## Integration testing
|
||||
|
||||
End-to-end attach can be tested with the [NR UE NAS simulator](../tests/nr-ue-nas-simulator/README.md),
|
||||
which runs the OAI UE NAS stack and gNB NGAP against the AMF, forwarding NAS PDUs without PHY/MAC/RLC.
|
||||
Use `nas_lib_test` for isolated codec round-trips.
|
||||
|
||||
### Code Structure
|
||||
|
||||
[openair3/NAS/NR_UE/nr_nas_msg.c](../openair3/NAS/NR_UE/nr_nas_msg.c):
|
||||
@@ -45,10 +71,14 @@ The following tables lists implemented NAS messages and whether there is an enco
|
||||
|
||||
[openair3/NAS/NR_UE/5GS/fgs_nas_lib.c](../openair3/NAS/NR_UE/5GS/fgs_nas_lib.c):
|
||||
|
||||
* top-level encode/decode dispatch for NAS 5GMM/5GSM payloads
|
||||
* delegates message-specific encoding/decoding to `5GMM/MSG` and `5GSM/MSG`
|
||||
|
||||
[openair3/NAS/NR_UE/5GS/NR_NAS_defs.h](../openair3/NAS/NR_UE/5GS/NR_NAS_defs.h):
|
||||
|
||||
* encoding and decoding functions for 5G NAS message headers and payloads
|
||||
* relies on 5GMM/5GSM messages libs for payload encoding
|
||||
* 5GS NAS message types and security header definitions
|
||||
* shared NAS structures used by UE NAS handlers and encoders
|
||||
* prototypes for 5GMM header and security-header encode/decode, implementations in `fgs_nas_lib.c`
|
||||
|
||||
[openair3/NAS/NR_UE/5GS/fgs_nas_utils.h](../openair3/NAS/NR_UE/5GS/fgs_nas_utils.h):
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ Both `GNB_IPV4_ADDRESS_FOR_NG_AMF` and `GNB_IPV4_ADDRESS_FOR_NGU` need to be
|
||||
set to the IP address of the NIC referenced previously.
|
||||
|
||||
**Note**: If the Core Network is running on the same server, 3 cores should be
|
||||
allocated to it. 2 for the UPF and 1 for the all the remaining services as shown
|
||||
allocated to it. 2 for the UPF and 1 core shared between the remaining services as shown
|
||||
below.
|
||||
|
||||
```patch
|
||||
|
||||
@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Full experimental OpenSource LTE and NR implementation
|
||||
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
||||
# the logo to the output directory.
|
||||
|
||||
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/images/oai_logo.png
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
|
||||
@@ -145,6 +145,11 @@ These modes of operation are supported:
|
||||
- evalution of RSRP report
|
||||
- evaluation of CQI report
|
||||
- MAC scheduling of SR reception
|
||||
- Paging (PCCH/P-RNTI)
|
||||
- CN paging records queued
|
||||
- PF/PO dequeue from SIB1 PCCH-Config
|
||||
- PCCH encoded at the UE's PO
|
||||
- Type2 common search-space based P-RNTI PDCCH + PDSCH scheduling for PCCH
|
||||
- Intra-frequency handover
|
||||
- Inter-frequency handover
|
||||
- Measurement gaps are automatically computed at the DU if the CU has neighbor information and the configured
|
||||
@@ -230,9 +235,12 @@ These modes of operation are supported:
|
||||
- NGAP Initial UE message
|
||||
- NGAP Initial context setup request/response
|
||||
- NGAP Downlink/Uplink NAS transfer
|
||||
- NGAP UE context release request/complete
|
||||
- NGAP Paging
|
||||
- NGAP UE context release request/command/complete
|
||||
- NGAP UE radio capability info indication
|
||||
- NGAP PDU session resource setup request/response
|
||||
- NGAP PDU session resource modify request/response
|
||||
- NGAP PDU session resource release command/response
|
||||
- NGAP Mobility Management Procedures:
|
||||
* NGAP Handover Required
|
||||
* NGAP Handover Request
|
||||
@@ -259,6 +267,7 @@ These modes of operation are supported:
|
||||
* F1 UE Context modification request/response
|
||||
* F1 UE Context modification required
|
||||
* F1 UE Context release req/cmd/complete
|
||||
- F1 Paging
|
||||
- F1 gNB CU configuration update
|
||||
- F1 gNB DU configuration update
|
||||
- F1 Reset (handled at DU only, full reset only)
|
||||
@@ -279,10 +288,14 @@ These modes of operation are supported:
|
||||
- E1 Bearer Context Setup (gNB-CU-CP initiated)
|
||||
- E1 Bearer Context Setup Request
|
||||
- E1 Bearer Context Setup Response
|
||||
- E1 Bearer Context Setup Failure
|
||||
- Bearer Context Modification (gNB-CU-CP initiated)
|
||||
- E1 Bearer Context Modification Request
|
||||
- E1 Bearer Context Modification Response
|
||||
- E1 Reset
|
||||
- E1 Bearer Context Modification Failure
|
||||
- Bearer Context Release (gNB-CU-CP initiated)
|
||||
- E1 Bearer Context Release Command
|
||||
- E1 Bearer Context Release Complete
|
||||
- Interface with RRC and PDCP/SDAP
|
||||
- One CU-CP can handle multiple CU-UPs
|
||||
|
||||
@@ -356,9 +369,9 @@ These modes of operation are supported:
|
||||
* NR-PRACH
|
||||
- Formats 0,1,2,3, A1-A3, B1-B3
|
||||
* NTN
|
||||
- TA adjustemt based on ntn-Config-r17 information
|
||||
- Different TA adjustemt algorithms between SIB19 receptions:
|
||||
- Autonomous TA adjustemt based on DL time tracking
|
||||
- TA adjustment based on ntn-Config-r17 information
|
||||
- Different TA adjustment algorithms between SIB19 receptions:
|
||||
- Autonomous TA adjustment based on DL time tracking
|
||||
- Standard compliant epoch time based TA adjustment including orbital propagation
|
||||
- DL Doppler compensation based on ntn-Config-r17 information
|
||||
- UL Doppler pre-compensation based on ntn-Config-r17 information and residual DL FO estimation
|
||||
@@ -395,9 +408,12 @@ These modes of operation are supported:
|
||||
- Fallback not supported
|
||||
* DCI processing
|
||||
- format 10 (RA-RNTI, C-RNTI, SI-RNTI, TC-RNTI)
|
||||
- format 10 with P-RNTI
|
||||
- format 00 (C-RNTI, TC-RNTI)
|
||||
- format 11 (C-RNTI)
|
||||
- format 01 (C-RNTI)
|
||||
* Paging monitoring and reception
|
||||
- PF/PO-based paging PDCCH monitoring in IDLE/non-connected states
|
||||
* UCI processing
|
||||
- ACK/NACK processing
|
||||
- Scheduling request procedures
|
||||
@@ -462,6 +478,7 @@ These modes of operation are supported:
|
||||
- Support for master cell group configuration
|
||||
- Reception of UECapabilityEnquiry, encoding and transmission of UECapability
|
||||
- Support for measurement report of Event A2/A3
|
||||
- Paging: PCCH reception
|
||||
* NTN according to 38.331 Rel.17
|
||||
- Reception of ntn-Config-r17 from SIB19 or reconfigurationWithSync
|
||||
- Handling of ntn-UlSyncValidityDuration-r17 in SIB19
|
||||
@@ -472,7 +489,8 @@ These modes of operation are supported:
|
||||
|
||||
* Transfer of NAS messages between the AMF and the UE supporting the UE registration with the core network and the PDU session establishment according to 24.501 Rel.16
|
||||
* 5GMM (5G Mobility Management) messages:
|
||||
- Service Request/Accept/Reject (enc/dec library only)
|
||||
- Service Request/Accept/Reject (Network-triggered Service Request TS 23.502 §4.2.3.3,
|
||||
UE-Triggered Service Request after paging, TS 23.502 §4.2.3.2)
|
||||
- Identity Request/Response
|
||||
- Authentication Request/Response
|
||||
- Security Mode Command/Complete
|
||||
|
||||
@@ -44,7 +44,8 @@ PTP enabled switches and Grandmaster clock we have tested with:
|
||||
|Fibrolan Falcon-RX/812/G|
|
||||
|Qulsar Qg2 (Grandmaster)|
|
||||
|
||||
**S-Plane synchronization is mandatory.** S-plane support is done via `ptp4l` and `phc2sys`. Make sure your version matches.
|
||||
**S-Plane synchronization is mandatory.** S-plane support is done via
|
||||
`ptp4l` and `phc2sys`. Make sure your version matches.
|
||||
|
||||
| Software | Software Version|
|
||||
|-----------|-----------------|
|
||||
@@ -228,19 +229,19 @@ Once installed you can use this configuration file for ptp4l (`/etc/ptp4l.conf`)
|
||||
```
|
||||
[global]
|
||||
domainNumber 24
|
||||
slaveOnly 1
|
||||
clientOnly 1
|
||||
time_stamping hardware
|
||||
tx_timestamp_timeout 1
|
||||
tx_timestamp_timeout 50
|
||||
logging_level 6
|
||||
summary_interval 0
|
||||
#priority1 127
|
||||
|
||||
[your_PTP_ENABLED_NIC]
|
||||
[PTP_ENABLED_NIC_INTERFACE]
|
||||
network_transport L2
|
||||
hybrid_e2e 0
|
||||
```
|
||||
|
||||
You need to increase `tx_timestamp_timeout` to 50 or 100 for Intel E-810. You will see that in the logs of ptp.
|
||||
You need to increase `tx_timestamp_timeout` to 100 if needed. You will see that in the logs of ptp.
|
||||
|
||||
Create the configuration file for ptp4l (`/etc/sysconfig/ptp4l`)
|
||||
|
||||
@@ -251,7 +252,7 @@ OPTIONS="-f /etc/ptp4l.conf"
|
||||
Create the configuration file for phc2sys (`/etc/sysconfig/phc2sys`)
|
||||
|
||||
```
|
||||
OPTIONS="-a -r -r -n 24"
|
||||
OPTIONS="-s PTP_ENABLED_NIC_INTERFACE -w -n 24 -r -r -m -R 8"
|
||||
```
|
||||
|
||||
The service of ptp4l (`/usr/lib/systemd/system/ptp4l.service`) should be configured as below:
|
||||
@@ -295,8 +296,7 @@ Beware that PTP issues may show up only when running OAI and XRAN. If you are us
|
||||
1. Make sure that you have `skew_tick=1` in `/proc/cmdline`
|
||||
2. For Intel E-810 cards set `tx_timestamp_timeout` to 50 or 100 if there are errors in ptp4l logs
|
||||
3. Other time sources than PTP, such as NTP or chrony timesources, should be disabled. Make sure they are enabled as further below.
|
||||
4. Make sure you set `kthread_cpus=<cpu_list>` in `/proc/cmdline`.
|
||||
5. If `rms` or `delay` in `ptp4l` or `offset` in `phc2sys` logs remain high then you can try pinning the `ptp4l` and `phc2sys` processes to an isolated CPU.
|
||||
4. If `rms` or `delay` in `ptp4l` or `offset` in `phc2sys` logs remain high then you can try pinning the `ptp4l` and `phc2sys` processes to an isolated CPU.
|
||||
|
||||
```bash
|
||||
#to check there is NTP enabled or not
|
||||
@@ -421,14 +421,14 @@ git apply ~/openairinterface5g/cmake_targets/tools/oran_fhi_integration_patches/
|
||||
```bash
|
||||
git clone https://github.com/openairinterface/o-du-phy.git ~/phy
|
||||
cd ~/phy
|
||||
git checkout <desired-tag> # shall match a variable `K_VERSION`
|
||||
git checkout 11.1.3 # the tag points to the `main` branch which has all patches applied that are relevant for OAI integration; the tag matches the value of cmake variable `K_VERSION`
|
||||
```
|
||||
or use `xran_DOWNLOAD` option when compiling OAI gNB.
|
||||
|
||||
Compile the fronthaul interface library by calling `make` and the option
|
||||
`XRAN_LIB_SO=1` to have it build a shared object. Note that we provide two
|
||||
environment variables `RTE_SDK` for the path to the source tree of DPDK, and
|
||||
`XRAN_DIR` to set the path to the fronthaul library. For building for a Arm
|
||||
target, set as well the environment variable `TARGET=armv8`.
|
||||
`XRAN_DIR` to set the path to the fronthaul library.
|
||||
|
||||
**Note**: you need at least gcc-11 and g++-11.
|
||||
|
||||
@@ -1258,7 +1258,7 @@ The OAI configuration file [`gnb.sa.band78.106prb.fhi72.1x1-proto-ru.conf`](../t
|
||||
First, compile the RU as outlined in the [building ProtO-RU tutorial](https://github.com/NUS-CIR/ProtO-RU/tree/proto-ru?tab=readme-ov-file#building-proto-ru).
|
||||
Then, ensure that both your DU and ProtO-RU host are PTP synchronized.
|
||||
|
||||
Next, use the RU config, [protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml](https://github.com/NUS-CIR/ProtO-RU/blob/proto-ru/proto-ru/conf-files/protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml), which corresponds to the above mentioned DU config file.
|
||||
Next, use the RU config, [protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml](https://github.com/NUS-CIR/ProtO-RU/blob/proto-ru/proto-ru/conf-files/protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml), which corresponds to the above mentioned DU config file. Please note that the RU delay profile might need to be adjusted according to the setup. The E2E test with xran K release required `T2a_max_cp_ul: 2985`.
|
||||
In addition, please adapt the DU MAC address and VLAN tag to your needs.
|
||||
|
||||
ProtO-RU was successfully tested with USRP B210.
|
||||
@@ -1556,7 +1556,10 @@ Edit the sample OAI gNB configuration file and check following parameters:
|
||||
|
||||
* `RUs` section
|
||||
* Set an isolated core for RU thread `ru_thread_core`, in our environment we are using CPU 6
|
||||
|
||||
* If testing with a numerology different than 1 (e.g., FDD with numerology 0),
|
||||
set `nr_scs_for_raster` to the used numerology, and adapt `sl_ahead`: it must be
|
||||
strictly less than the number of slots in a frame (e.g., 5 for numerology 0).
|
||||
|
||||
* `fhi_72` (FrontHaul Interface) section: this config follows the structure
|
||||
that is employed by the xRAN library (`xran_fh_init` and `xran_fh_config`
|
||||
structs in the code):
|
||||
|
||||
@@ -133,6 +133,9 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
|
||||
## Developer tools
|
||||
|
||||
- [code-style-contrib.md](./code-style-contrib.md): overall working practices, code style, and review process
|
||||
- [git-guide.md](./git-guide.md): Git how-tos — commit signing setup, branch
|
||||
management, submodules, recovering from mistakes, reusing conflict
|
||||
resolutions (rerere)
|
||||
- [cross-compile.md](./cross-compile.md): how to cross-compile OAI for ARM
|
||||
- [clang-format.md](./clang-format.md): how to format the code. See also the
|
||||
next entry for an error detection tool.
|
||||
|
||||
@@ -626,6 +626,116 @@ sequenceDiagram
|
||||
Note over ue,tdu: UE active on target DU
|
||||
```
|
||||
|
||||
### Paging and Network Triggered Service Request (CM-IDLE to CM-CONNECTED)
|
||||
|
||||
The following flow documents the current OAI stack path for paging-triggered
|
||||
service resumption in SA: context release to idle, NGAP/F1AP/PCCH paging, then
|
||||
RRC setup with NAS Service Request.
|
||||
|
||||
End-to-end flow is split across these 3GPP procedures:
|
||||
|
||||
| Spec clause | Procedure name | Actor | Spec role in this flow |
|
||||
|-------------|----------------|-------|------------------------|
|
||||
| TS 23.502 §4.2.3.3 | Network Triggered Service Request | AMF / 5GC | Network must deliver MT signalling/data to a CM-IDLE UE. "The Paging Request triggers the UE Triggered Service Request procedure in the UE". Step 4b = Paging Request to RAN, step 6 = UE initiates §4.2.3.2 |
|
||||
| TS 23.502 §4.2.3.2 | UE Triggered Service Request | UE | CM-IDLE UE may initiate SR "as a response to a network paging request" |
|
||||
| TS 24.501 §5.6.2.2 | Paging for 5GS services | UE NAS | On paging indication from lower layers: initiate service request (§5.6.1.2) when in `5GMM-REGISTERED` and `5GMM-IDLE` |
|
||||
| TS 24.501 §8.2.16 | SERVICE REQUEST | UE NAS | NAS PDU sent by UE |
|
||||
|
||||
#### OAI implementation
|
||||
|
||||
In summary:
|
||||
- UE transitions to `RRC_IDLE` and AMF transitions UE to `CM-IDLE`
|
||||
- CN-triggered paging is performed with `5G-S-TMSI` (NGAP Paging -> RRC Paging)
|
||||
- One radio page per cell per NGAP PAGING at UE PO
|
||||
- UE resumes via NAS Service Request and `RRCSetup`/`RRCSetupComplete`
|
||||
- AMF continues §4.2.3.3 after Initial UE Message / context setup
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant dn as DN
|
||||
participant cn as 5GC
|
||||
participant cu as gNB-CU
|
||||
participant du as gNB-DU
|
||||
participant ue as UE
|
||||
|
||||
cu->>cn: NGAP UE Context Release Request
|
||||
Note over cu: example trigger: telnet `rrc ctx_rel_req`
|
||||
cu->>cu: rrc_gNB_trigger_ue_context_release_req()
|
||||
cu->>cu: rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ()
|
||||
cn->>cu: NGAP UE Context Release Command
|
||||
cu->>cu: rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND()
|
||||
cu->>cu: rrc_gNB_generate_RRCRelease()
|
||||
cu->>du: F1AP DL RRC Message Transfer (RRCRelease)
|
||||
du->>ue: RRCRelease
|
||||
ue->>ue: handle_RRCRelease()
|
||||
ue->>ue: nr_rrc_going_to_IDLE()
|
||||
Note over ue: RRC_IDLE, NAS remains REGISTERED and can request service
|
||||
du->>cu: F1AP UE Context Release Complete
|
||||
cu->>cn: NGAP UE Context Release Complete
|
||||
|
||||
Note over dn,ue: Trigger Paging
|
||||
dn->>cn: DL user data
|
||||
Note over cn: AMF paging decision [23.502 §4.2.3.3]
|
||||
cn->>cu: NGAP Paging (5G-S-TMSI)
|
||||
cu->>cu: ngap_gNB_handle_paging()
|
||||
cu->>cu: decode_ng_paging()
|
||||
cu->>cu: rrc_gNB_process_PAGING_IND()
|
||||
cu->>cu: rrc_send_paging_to_dus()
|
||||
cu->>du: F1AP Paging
|
||||
du->>du: DU_handle_Paging()
|
||||
du->>du: f1_paging()
|
||||
du->>du: nr_mac_pcch_enqueue()
|
||||
Note over du: at UE PO: schedule_nr_pcch()
|
||||
du->>du: do_NR_Paging()
|
||||
du->>ue: RRC Paging (ng-5G-S-TMSI, P-RNTI)
|
||||
|
||||
ue->>ue: nr_rrc_ue_decode_pcch()
|
||||
ue->>ue: NAS_PAGING_IND
|
||||
ue->>ue: generateServiceRequest()
|
||||
ue->>ue: send_nas_initial_ul_transfer_req()
|
||||
Note over ue: paging match triggers Service Request [24.501 §5.6.2]
|
||||
Note over ue,cn: Resume service
|
||||
|
||||
ue->>ue: NAS_INITIAL_UL_TRANSFER_REQ
|
||||
ue->>ue: nr_rrc_ue_prepare_RRCSetupRequest()
|
||||
ue->>ue: nr_rrc_trigger_mac_ra(NR_MAC_RA_START_SETUP)
|
||||
ue->>du: RRCSetupRequest
|
||||
du->>cu: F1AP Initial UL RRC Message Transfer (RRCSetupRequest)
|
||||
cu->>cu: rrc_handle_RRCSetupRequest()
|
||||
cu->>du: F1AP DL RRC Message Transfer (RRCSetup)
|
||||
du->>ue: RRCSetup
|
||||
ue->>ue: do_RRCSetupComplete()
|
||||
Note over ue: uses dedicatedNAS when no SRB exists
|
||||
ue->>du: RRCSetupComplete + NAS SERVICE REQUEST
|
||||
du->>cu: F1AP UL RRC Message Transfer (RRCSetupComplete + NAS)
|
||||
cu->>cu: rrc_handle_RRCSetupComplete()
|
||||
cu->>cn: NGAP Initial UE Message
|
||||
cn->>cu: NGAP Initial Context Setup Request
|
||||
cu->>cu: rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ()
|
||||
cu->>du: F1AP DL RRC Message Transfer (RRCReconfiguration)
|
||||
du->>ue: RRCReconfiguration
|
||||
ue->>du: RRCReconfigurationComplete
|
||||
du->>cu: F1AP UL RRC Message Transfer (RRCReconfigurationComplete)
|
||||
cu->>cu: handle_rrcReconfigurationComplete()
|
||||
cu->>cn: NGAP Initial Context Setup Response
|
||||
cn->>ue: DL user data
|
||||
```
|
||||
|
||||
In OAI RFsim lab runs, a practical trigger sequence is:
|
||||
1. `rrc ctx_rel_req <ue-id>` at gNB telnet
|
||||
2. wait until AMF reports UE in IDLE state
|
||||
3. send host-side traffic to UE IP
|
||||
|
||||
Relevant specs:
|
||||
- TS 23.502: §4.2.3.3 Network Triggered Service Request (AMF pages, step 4b),
|
||||
UE Triggered Service Request §4.2.3.2 (by network paging)
|
||||
- TS 24.501 §5.6.2.2: Paging for 5G services
|
||||
- TS 24.501 §5.6.1: Service Request procedure
|
||||
- TS 38.331 §5.3.2: Paging
|
||||
- TS 38.331 §5.3.11: UE actions upon going to RRC_IDLE
|
||||
- TS 38.413 §8.5: Paging procedures (NGAP Paging)
|
||||
- TS 38.473 §8.7: Paging procedures (F1AP CU-to-DU paging)
|
||||
|
||||
## Structures
|
||||
|
||||
### DUs and Cells
|
||||
|
||||
@@ -90,9 +90,9 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
|
||||
`0xffffff` is a reserved value and means "no SD"
|
||||
Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD
|
||||
is "mMTC"
|
||||
- `enable_sdap` (default: true): enable the use of the SDAP layer. If
|
||||
deactivated, a transparent SDAP header is prepended to packets, but no
|
||||
further processing is being done.
|
||||
- `enable_sdap` (default: true): set `sdap-HeaderUL` and `sdap-HeaderDL` to
|
||||
present in the RRC `SDAP-Config` IE for SA PDU sessions. If false, both
|
||||
headers are absent (per DRB). SDAP entities are still created, SDAP layer always enabled.
|
||||
- `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission.
|
||||
Currently supported:
|
||||
- SIB2: serving-cell reselection parameters (configured in `sib2_config`)
|
||||
|
||||
@@ -118,43 +118,12 @@ e.g., `v3.0`. We target to make releases bi-yearly.
|
||||
|
||||
### How to manage your own branch
|
||||
|
||||
Before starting to work, please make sure to branch off the latest `develop`
|
||||
branch. Make commits as appropriate.
|
||||
```bash
|
||||
$ git fetch origin
|
||||
$ git checkout develop
|
||||
$ git checkout -b my-new-feature # name as appropriate
|
||||
$ git add -p # add changes for change set 1, use `-p` to review what to include
|
||||
$ git commit # in the editor, describe your changes
|
||||
$ git add -p # add changes for change set 2
|
||||
$ git commit # in the editor, describe your changes
|
||||
```
|
||||
|
||||
Again, commit message should take multiple lines; after the initial title, a
|
||||
blank line should follow. Read the `DISCUSSION` section in `man git commit` for
|
||||
more information.
|
||||
|
||||
If your development takes longer, make sure to synchronize regularly with
|
||||
`origin/develop` using `git rebase`:
|
||||
```bash
|
||||
$ git fetch origin
|
||||
$ git rebase -i origin/develop
|
||||
```
|
||||
|
||||
If you do logical changes, you should not have to resolve the same conflicts
|
||||
over and over again. Note that if you jumped over multiple develop tags, you
|
||||
can also rebase in intermediate steps, in case you fear the differences might
|
||||
be too big.
|
||||
```
|
||||
$ git rebase -i 2023.w38
|
||||
$ git rebase -i 2023.w41
|
||||
$ git rebase -i develop
|
||||
```
|
||||
|
||||
Once you rebased, push the changes to the remote
|
||||
```
|
||||
$ git push origin my-new-feature --force-with-lease # force with lease let's you only overwrite what you also have locally in origin/my-new-feature
|
||||
```
|
||||
Branch off the latest `develop` branch before starting to work, keep your
|
||||
branch synchronized with `origin/develop` through regular rebases, and push
|
||||
with `--force-with-lease` after rebasing. The step-by-step commands — including
|
||||
how to rebase over multiple develop tags in intermediate steps and how to avoid
|
||||
resolving the same conflicts repeatedly with `git rerere` — are in the
|
||||
[branch management section of the Git guide](./git-guide.md#managing-your-own-branch).
|
||||
|
||||
### Use of git commit trailers
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Correct (Markdown):
|
||||
**Bold text**
|
||||
*Italic text*
|
||||
|
||||
 instead of <img src="./images/oai_final_logo.png" alt="OAI Logo">
|
||||

|
||||
|
||||
| Feature | Description |
|
||||
|---------------|---------------------------------|
|
||||
@@ -75,7 +75,7 @@ Incorrect (HTML):
|
||||
<b>Bold text</b>
|
||||
<i>Italic text</i>
|
||||
|
||||
<img src="./images/oai_final_logo.png" alt="OAI Logo">
|
||||
<img src="./path/to/image.png" alt="some image">
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr>
|
||||
@@ -114,7 +114,7 @@ Incorrect (HTML):
|
||||
is](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#separate-your-changes).
|
||||
- See [OAI CN5G configuration files](#22-oai-cn5g-configuration-files) for
|
||||
details.
|
||||
- 
|
||||
- 
|
||||
```
|
||||
|
||||
## Inline code for technical elements
|
||||
|
||||
390
doc/git-guide.md
Normal file
390
doc/git-guide.md
Normal file
@@ -0,0 +1,390 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Git guide
|
||||
|
||||
This guide collects the practical Git knowledge needed to contribute to OAI in
|
||||
one place: how to set up commit signing, how to manage and synchronize a
|
||||
feature branch, how to handle submodules, how to recover from common mistakes,
|
||||
and how to avoid resolving the same merge conflicts over and over. It is a
|
||||
how-to companion to the contribution *requirements*, which are
|
||||
defined in [CONTRIBUTING.md](../CONTRIBUTING.md) (CLA, DCO, verified commits)
|
||||
and [code-style-contrib.md](./code-style-contrib.md) (workflow, commit, and
|
||||
review policy).
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Setting up commit signing
|
||||
|
||||
Every commit in a pull request must pass two independent CI checks, described
|
||||
in [CONTRIBUTING.md](../CONTRIBUTING.md#commit-guidelines):
|
||||
|
||||
1. **[Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)**:
|
||||
the commit message carries a `Signed-off-by:` trailer.
|
||||
2. **[Verified commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)**:
|
||||
the commit is cryptographically signed with an SSH or GPG key.
|
||||
|
||||
These are two different mechanisms: the sign-off is a line of text you add with
|
||||
`git commit -s`, the signature is created automatically by Git once signing is
|
||||
configured. You need both.
|
||||
|
||||
### Quick setup (SSH signing)
|
||||
|
||||
```bash
|
||||
# 1. Generate a key pair (skip if you already have one)
|
||||
ssh-keygen -t ed25519 -C "<your email>"
|
||||
|
||||
# 2. Configure Git to sign every commit with it
|
||||
git config --global user.name "<Your Name>"
|
||||
git config --global user.email "<your email>"
|
||||
git config --global gpg.format ssh
|
||||
git config --global user.signingkey ~/.ssh/id_ed25519.pub
|
||||
git config --global commit.gpgsign true
|
||||
```
|
||||
|
||||
> **NOTE:**
|
||||
> `--global` writes to `~/.gitconfig` and applies to every repository on the
|
||||
> machine. When working on a shared server (or with different identities in
|
||||
> different clones), drop `--global` to store the same settings in the current
|
||||
> repository's `.git/config` only.
|
||||
|
||||
Then print the public key with `cat ~/.ssh/id_ed25519.pub` and paste it into
|
||||
your GitHub account under *Settings → SSH and GPG keys → New SSH key*, choosing
|
||||
the key type **Signing Key**.
|
||||
|
||||
> **NOTE:**
|
||||
> Adding an SSH key for repository access does not automatically enable commit
|
||||
> signing. The key must also be added under GitHub's Signing Keys settings.
|
||||
|
||||
For commits to show as *Verified* on GitHub:
|
||||
|
||||
- your `git config user.email` must match an email of your GitHub account,
|
||||
- that email must be [verified in your GitHub account](https://docs.github.com/en/account-and-profile/how-tos/email-preferences/verifying-your-email-address),
|
||||
- and it must be the email address used for the CLA (see
|
||||
[CONTRIBUTING.md](../CONTRIBUTING.md)).
|
||||
|
||||
If you prefer GPG over SSH, set `gpg.format` to `openpgp` and `user.signingkey`
|
||||
to your GPG key ID instead; see the [GitHub documentation on signing
|
||||
commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
|
||||
for the full walkthrough of both methods.
|
||||
|
||||
### Signing off your commits (DCO)
|
||||
|
||||
The `Signed-off-by:` trailer is added with the `-s`/`--signoff` flag:
|
||||
|
||||
```bash
|
||||
git commit -s # new commit
|
||||
git commit --amend -s --no-edit # add the trailer to the last commit
|
||||
```
|
||||
|
||||
It must read `Signed-off-by: Full Name <email-for-cla>`. See the
|
||||
[commit trailers section](./code-style-contrib.md#use-of-git-commit-trailers)
|
||||
of the contribution guidelines for this and other trailers.
|
||||
|
||||
### Verifying signed commits
|
||||
|
||||
You can verify that commits are properly signed locally using:
|
||||
|
||||
```bash
|
||||
git log --show-signature
|
||||
```
|
||||
|
||||
GitHub should also display a *Verified* badge next to signed commits once the
|
||||
signing key has been correctly configured in your account.
|
||||
|
||||
For SSH commit signing, local Git verification may require an
|
||||
`allowed_signers` file. This is only used for local verification in Git and is
|
||||
not required by GitHub. If you see errors such as:
|
||||
|
||||
```text
|
||||
No principal matched
|
||||
Can't check signature
|
||||
error: gpg.ssh.allowedSignersFile needs to be configured
|
||||
```
|
||||
|
||||
create the file, add your signing identity, and enable it in your Git config:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/git
|
||||
echo "user@example.com ssh-ed25519 AAAACexamplekeystringhere" > ~/.config/git/allowed_signers
|
||||
git config gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
|
||||
```
|
||||
|
||||
> **NOTE:**
|
||||
> This is only for local Git signature verification and does not affect GitHub,
|
||||
> or remote repository behavior.
|
||||
|
||||
## Managing your own branch
|
||||
|
||||
The general development branch, and the target of every contribution, is
|
||||
`develop`; see [GET_SOURCES.md](./GET_SOURCES.md) for the branch and tag model
|
||||
(weekly `YYYY.wXX` tags, `vX.Y` releases). The rules for what a branch should
|
||||
look like — linear history, small self-contained logical commits, commit
|
||||
messages that explain *why* — are policy and live in
|
||||
[code-style-contrib.md](./code-style-contrib.md#workflow).
|
||||
|
||||
Before starting to work, please make sure to branch off the latest `develop`
|
||||
branch. Make commits as appropriate.
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git checkout develop
|
||||
git checkout -b my-new-feature # name as appropriate
|
||||
git add -p # add changes for change set 1, use `-p` to review what to include
|
||||
git commit -s # in the editor, describe your changes
|
||||
git add -p # add changes for change set 2
|
||||
git commit -s # in the editor, describe your changes
|
||||
```
|
||||
|
||||
Recent Git versions also offer `git switch` as a clearer alternative to
|
||||
`git checkout` for branch operations: `git switch develop` changes branch,
|
||||
`git switch -c my-new-feature` creates one.
|
||||
|
||||
Commit messages should take multiple lines; after the initial title, a blank
|
||||
line should follow. Read the `DISCUSSION` section in `man git commit` for more
|
||||
information. For documentation-only commits, prefix the title with `docs:`
|
||||
(see [doc_best_practices.md](./doc_best_practices.md)).
|
||||
|
||||
Code must be formatted with clang-format; an optional pre-commit hook can
|
||||
check this automatically at every commit — see
|
||||
[clang-format.md](./clang-format.md) for its installation and how to combine
|
||||
it with `git add -p`/`git stash -p`.
|
||||
|
||||
If your development takes longer, make sure to synchronize regularly with
|
||||
`origin/develop` using `git rebase`:
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase -i origin/develop
|
||||
```
|
||||
|
||||
If you do logical changes, you should not have to resolve the same conflicts
|
||||
over and over again. If the same conflicts do keep reappearing, e.g., when
|
||||
maintaining a long-lived fork, consider enabling
|
||||
[`git rerere`](#reusing-conflict-resolutions-with-git-rerere). Note that if
|
||||
you jumped over multiple develop tags, you can also rebase in intermediate
|
||||
steps, in case you fear the differences might be too big.
|
||||
|
||||
```bash
|
||||
git rebase -i 2023.w38
|
||||
git rebase -i 2023.w41
|
||||
git rebase -i develop
|
||||
```
|
||||
|
||||
Once you rebased, push the changes to the remote:
|
||||
|
||||
```bash
|
||||
git push origin my-new-feature --force-with-lease # force with lease lets you only overwrite what you also have locally in origin/my-new-feature
|
||||
```
|
||||
|
||||
### Fixing up earlier commits
|
||||
|
||||
The [workflow policy](./code-style-contrib.md#workflow) asks for a history
|
||||
without "clean up" commits: when review or testing reveals a problem in an
|
||||
earlier commit of your branch, fold the fix into that commit instead of
|
||||
appending a `Fix bug` commit on top. Git automates this with fixup commits and
|
||||
`--autosquash`:
|
||||
|
||||
```bash
|
||||
git add -p # stage the fix
|
||||
git commit --fixup=<commit> # creates a commit titled "fixup! <original title>"
|
||||
git rebase -i --autosquash origin/develop # moves it after <commit> and squashes the two
|
||||
```
|
||||
|
||||
During the `--autosquash` rebase, Git pre-arranges the todo list so each
|
||||
`fixup!` commit is squashed into the commit it references; you normally just
|
||||
accept it. The result is the same clean history as if the fix had been part of
|
||||
the original commit.
|
||||
|
||||
A handy variant is `git commit --fixup=amend:<commit>`, which folds in the fix
|
||||
and also rewrites the commit message: during the `--autosquash` rebase the
|
||||
editor opens pre-filled with the original message, ready to be edited into the
|
||||
new one.
|
||||
|
||||
## Working with submodules
|
||||
|
||||
Parts of the tree are Git submodules. After cloning, and after every branch
|
||||
switch or pull, make sure they match the superproject:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
A recurring review problem is the *unintended submodule pointer update*: a
|
||||
submodule whose checked-out commit differs from what the superproject records
|
||||
shows up in `git status` as `modified: <path> (new commits)`, and a broad
|
||||
`git add .`, `git add -A`, or `git commit -a` silently records the new pointer
|
||||
in your commit. To avoid it:
|
||||
|
||||
- review `git status` before committing and stage files explicitly (e.g. with
|
||||
`git add -p`) rather than adding everything;
|
||||
- if a pointer change was staged by accident, unstage it with
|
||||
`git restore --staged <path>` and realign the submodule with
|
||||
`git submodule update --init <path>`.
|
||||
|
||||
Only commit a submodule pointer change when updating that submodule is the
|
||||
purpose of the commit, and say so in the commit message.
|
||||
|
||||
## Recovering from mistakes
|
||||
|
||||
To unstage a file that was added by accident (the changes stay in your working
|
||||
tree), or to throw away local changes to a file:
|
||||
|
||||
```bash
|
||||
git restore --staged <file> # unstage; keeps the modifications
|
||||
git restore <file> # discard unstaged modifications - cannot be undone
|
||||
```
|
||||
|
||||
`git reset` moves the current branch to another commit and differs in what it
|
||||
does to your files:
|
||||
|
||||
```bash
|
||||
git reset --soft HEAD~1 # undo the last commit, keep its changes staged (e.g. to re-split it)
|
||||
git reset --hard <commit> # make branch, index and working tree identical to <commit>
|
||||
```
|
||||
|
||||
> **Warning:** `git reset --hard` discards all uncommitted changes; there is no
|
||||
> way to recover them.
|
||||
|
||||
Committed work is much harder to lose than it seems: `git reflog` records every
|
||||
position of `HEAD` (commits, rebases, resets, checkouts) for a retention period
|
||||
of at least 30 days, even for commits no branch points to anymore. If a rebase
|
||||
or reset went wrong, find the last good state and reset back to it:
|
||||
|
||||
```bash
|
||||
git reflog # e.g.: e75076172 HEAD@{5}: commit: doc: add git rerere guide
|
||||
git reset --hard 'HEAD@{5}' # return the branch to that state
|
||||
```
|
||||
|
||||
## Reusing conflict resolutions with git rerere
|
||||
|
||||
The `develop` branch is updated roughly once a week. Feature branches that live
|
||||
for more than a few days therefore have to be re-synced with `develop`
|
||||
repeatedly, and the same merge conflicts tend to reappear at every sync - often
|
||||
in the same scheduler, PHY, or RRC files that several contributors touch at
|
||||
once. Resolving the identical conflict by hand every week is error-prone and
|
||||
wastes time.
|
||||
|
||||
Git ships a built-in feature for exactly this situation: `rerere`, short for
|
||||
**reuse recorded resolution**. Once enabled, Git remembers how you resolved a
|
||||
given conflict and replays that resolution automatically the next time the same
|
||||
conflict appears.
|
||||
|
||||
This section explains how to enable and use it. It is a local developer
|
||||
convenience: nothing about it changes the repository, the history you push, or
|
||||
the contribution workflow.
|
||||
|
||||
### What it does
|
||||
|
||||
When a conflict occurs, `rerere` records the conflicted hunk (the *preimage*).
|
||||
After you resolve it, `rerere` records your resolution (the *postimage*), keyed
|
||||
by a hash of the preimage. The next time a conflict with the same preimage shows
|
||||
up - in a later rebase, a later merge, or even another branch - Git reapplies
|
||||
your recorded resolution instead of presenting the conflict again.
|
||||
|
||||
The data lives in `.git/rr-cache/` inside your local clone. It is never part of
|
||||
any commit and is never pushed.
|
||||
|
||||
### Enabling it
|
||||
|
||||
Enable it once, globally, so it applies to every repository on your machine:
|
||||
|
||||
```bash
|
||||
git config --global rerere.enabled true
|
||||
git config --global rerere.autoupdate true
|
||||
```
|
||||
|
||||
`rerere.autoupdate` stages a replayed resolution automatically. Without it, the
|
||||
resolution is still written into your working tree, but you have to `git add`
|
||||
the file yourself.
|
||||
|
||||
### Typical flow
|
||||
|
||||
The first time you hit a conflict after enabling `rerere`, resolve it exactly as
|
||||
you always have:
|
||||
|
||||
```bash
|
||||
# during a rebase or a merge that conflicts
|
||||
git status # rerere reports which paths it is recording
|
||||
# edit the conflicted files, remove the markers
|
||||
git add <resolved-files>
|
||||
git rebase --continue # or: git commit, for a merge
|
||||
```
|
||||
|
||||
That resolution is now recorded. The next time the same conflict appears, Git
|
||||
resolves it for you. With `autoupdate` on, the file is already staged and you can
|
||||
go straight to:
|
||||
|
||||
```bash
|
||||
git rebase --continue # or git commit
|
||||
```
|
||||
|
||||
Always review the replayed result before continuing - see *Caveats* below.
|
||||
|
||||
### Inspecting and undoing recorded resolutions
|
||||
|
||||
```bash
|
||||
git rerere status # paths with a recorded preimage in the current operation
|
||||
git rerere diff # the resolution rerere is applying
|
||||
git rerere forget <path> # discard a recorded resolution (e.g. a wrong one)
|
||||
```
|
||||
|
||||
`git rerere forget` is the escape hatch when you recorded a bad resolution: it
|
||||
drops the cached entry for that path so the next conflict is presented fresh.
|
||||
|
||||
### Seeding from existing history
|
||||
|
||||
If your branch already contains **merge commits** whose conflicts you resolved
|
||||
before enabling `rerere`, you can backfill the cache so those resolutions are
|
||||
available immediately. Git ships a helper for this in `contrib/`:
|
||||
|
||||
```bash
|
||||
sh /path/to/git/contrib/rerere-train.sh origin/develop..HEAD
|
||||
```
|
||||
|
||||
It replays the merge commits in the given range, reconstructs each conflict, and
|
||||
records the resolution found in the merge commit.
|
||||
|
||||
> **Note:** this only works for resolutions captured in merge commits. A purely
|
||||
> linear (rebased) history has no merge commits to learn from, so there is
|
||||
> nothing to backfill - `rerere` will simply start recording from your next
|
||||
> conflict onward.
|
||||
|
||||
### Sharing the cache (optional)
|
||||
|
||||
The cache is local. If you work across several machines, or want a team to share
|
||||
resolutions for the same recurring conflicts, copy the directory:
|
||||
|
||||
```bash
|
||||
rsync -a ~/work/oai-A/.git/rr-cache/ ~/work/oai-B/.git/rr-cache/
|
||||
```
|
||||
|
||||
There is no built-in push/pull for the cache; treat it as an ordinary directory
|
||||
to sync.
|
||||
|
||||
### Caveats and good practice
|
||||
|
||||
- `rerere` matches on the **exact** conflicting text. If `develop` changed the
|
||||
lines surrounding your change, the preimage differs and the conflict is
|
||||
presented as new. This is expected - the resolution is still recorded for the
|
||||
next identical occurrence.
|
||||
- A replayed resolution is only as correct as the original. When the code around
|
||||
a conflict has evolved, an old resolution can apply cleanly yet be wrong.
|
||||
**Review every replayed resolution and build/test before continuing.**
|
||||
- `rerere` reduces repeated manual work; it does not change which branch
|
||||
strategy you use. It helps both when rebasing onto `develop` and when merging
|
||||
`develop` into a feature branch. Remember that branches intended for
|
||||
contribution must have a linear history without merge commits (see the
|
||||
[workflow policy](./code-style-contrib.md#workflow)); a fork can of course
|
||||
carry merge commits if that is convenient for its development.
|
||||
|
||||
## See also
|
||||
|
||||
- [CONTRIBUTING.md](../CONTRIBUTING.md) - CLA, DCO, and licensing requirements.
|
||||
- [code-style-contrib.md](./code-style-contrib.md) - workflow, commit, and
|
||||
review policy, including commit trailers.
|
||||
- [GET_SOURCES.md](./GET_SOURCES.md) - branches, tags, and how to obtain the
|
||||
sources.
|
||||
- [clang-format.md](./clang-format.md) - code formatting and its Git
|
||||
integration (pre-commit hook).
|
||||
- The [Git Book](https://git-scm.com/book/en/v2) and the
|
||||
[`git rerere` manual](https://git-scm.com/docs/git-rerere)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 209 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -9,7 +9,7 @@
|
||||
|
||||
FROM ran-base:latest AS ran-base
|
||||
|
||||
ARG xran_VERSION=11.1.1
|
||||
ARG xran_VERSION=11.1.3
|
||||
ARG E2AP_VERSION=E2AP_V3
|
||||
ARG KPM_VERSION=KPM_V3_00
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
@@ -27,7 +27,6 @@ RUN apt-get update && \
|
||||
|
||||
RUN rm -Rf /oai-ran
|
||||
WORKDIR /oai-ran
|
||||
COPY . .
|
||||
|
||||
## Download and build DPDK
|
||||
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
|
||||
@@ -42,7 +41,7 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
|
||||
cd /opt/phy && \
|
||||
git checkout $xran_VERSION &&\
|
||||
cd /opt/phy/fhi_lib/lib && \
|
||||
TARGET=armv8 WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
|
||||
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
|
||||
|
||||
## Build Arm RAN Acceleration Library
|
||||
RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \
|
||||
@@ -54,6 +53,8 @@ RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \
|
||||
ninja && \
|
||||
ninja install
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM ran-base AS ran-build-fhi72
|
||||
## Build and install OAI
|
||||
#run build_oai to build the target image
|
||||
|
||||
@@ -12,9 +12,8 @@ ENV TZ=Europe
|
||||
|
||||
RUN rm -Rf /oai-ran
|
||||
WORKDIR /oai-ran
|
||||
COPY . .
|
||||
|
||||
ARG xran_VERSION=11.1.1
|
||||
ARG xran_VERSION=11.1.3
|
||||
|
||||
## Download and build DPDK
|
||||
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
|
||||
@@ -31,6 +30,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
|
||||
cd /opt/phy/fhi_lib/lib && \
|
||||
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM ran-base AS ran-build-fhi72
|
||||
ARG E2AP_VERSION=E2AP_V3
|
||||
ARG KPM_VERSION=KPM_V3_00
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
FROM ran-base:latest AS ran-base
|
||||
|
||||
ARG xran_VERSION=11.1.1
|
||||
ARG xran_VERSION=11.1.3
|
||||
ARG DPDK_VERSION=22.11
|
||||
ARG T2_PATCH=AMD-T2-SDFEC_25-03-1.patch
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
@@ -27,7 +27,7 @@ RUN apt-get update && \
|
||||
|
||||
RUN rm -Rf /oai-ran
|
||||
WORKDIR /oai-ran
|
||||
COPY . .
|
||||
|
||||
## Copy T2 patch
|
||||
COPY ./$T2_PATCH /opt/.
|
||||
## Download and build DPDK
|
||||
@@ -46,6 +46,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
|
||||
cd /opt/phy/fhi_lib/lib && \
|
||||
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-$DPDK_VERSION/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM ran-base AS ran-build-fhi72-t2
|
||||
## Build and install OAI
|
||||
## Run build_oai to build the target image, if T2 patch present, build with T2 support
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
FROM ran-base:latest AS ran-base
|
||||
|
||||
ARG xran_VERSION=11.1.1
|
||||
ARG xran_VERSION=11.1.3
|
||||
ARG E2AP_VERSION=E2AP_V3
|
||||
ARG KPM_VERSION=KPM_V3_00
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
@@ -32,7 +32,6 @@ RUN apt-get update && \
|
||||
|
||||
RUN rm -Rf /oai-ran
|
||||
WORKDIR /oai-ran
|
||||
COPY . .
|
||||
|
||||
RUN git clone https://github.com/CESNET/libyang.git && \
|
||||
cd libyang && \
|
||||
@@ -81,6 +80,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
|
||||
cd /opt/phy/fhi_lib/lib && \
|
||||
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM ran-base AS ran-build-fhi72
|
||||
## Build and install OAI
|
||||
#run build_oai to build the target image
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Docker/Podman Build and Usage Procedures</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
---
|
||||
OAI Docker/Podman Build and Usage Procedures
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "SCHED/sched_eNB.h"
|
||||
#include "PHY/LTE_TRANSPORT/transport_proto.h"
|
||||
#include "nfapi/oai_integration/vendor_ext.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
|
||||
#include "PHY/if4_tools.h"
|
||||
#include "PHY/LTE_ESTIMATION/lte_estimation.h"
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "SCHED/sched_common.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/ethernet_lib.h"
|
||||
|
||||
/* these variables have to be defined before including ENB_APP/enb_paramdef.h */
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "common/ran_context.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/if_defs.h"
|
||||
|
||||
#include <openair1/PHY/phy_extern_ue.h>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "assertions.h"
|
||||
#include "PHY/types.h"
|
||||
#include "PHY/defs_eNB.h"
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "common/ran_context.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/if_defs.h"
|
||||
|
||||
#include "PHY/phy_vars_ue.h"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
#include "common/ran_context.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/if_defs.h"
|
||||
#include "PHY/phy_vars.h"
|
||||
#include "PHY/phy_extern.h"
|
||||
@@ -22,6 +21,9 @@
|
||||
#include <executables/softmodem-common.h>
|
||||
#include <executables/thread-common.h>
|
||||
#include "executables/nr-softmodem.h"
|
||||
#include "nr-oru.h"
|
||||
#include "openair1/PHY/INIT/nr_phy_init.h"
|
||||
#include "openair1/SCHED_NR/sched_nr.h"
|
||||
|
||||
pthread_cond_t sync_cond;
|
||||
pthread_mutex_t sync_mutex;
|
||||
@@ -33,6 +35,9 @@ int sf_ahead = 4;
|
||||
int emulate_rf = 0;
|
||||
|
||||
RAN_CONTEXT_t RC;
|
||||
|
||||
|
||||
|
||||
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
|
||||
|
||||
void nfapi_setmode(nfapi_mode_t nfapi_mode)
|
||||
@@ -106,26 +111,15 @@ struct timespec timespec_sub(struct timespec, struct timespec)
|
||||
struct timespec t = {0};
|
||||
return t;
|
||||
};
|
||||
|
||||
void perform_symbol_rotation(const int nsymb, const int numerology_index, double f0, c16_t *symbol_rotation)
|
||||
void beam_index_allocation(uint16_t fapi_beam_index,
|
||||
int ant,
|
||||
int num_ports,
|
||||
int symbols_per_slot,
|
||||
int slot,
|
||||
uint16_t bitmap_symbols,
|
||||
int num_ant_max,
|
||||
uint16_t **ant_beam_id_list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void init_timeshift_rotation(const int ofdm_symbol_size,
|
||||
const int nb_prefix_samples,
|
||||
const uint ofdm_offset_divisor,
|
||||
c16_t *timeshift_symbol_rotation)
|
||||
{
|
||||
return;
|
||||
};
|
||||
int beam_index_allocation(bool das,
|
||||
int fapi_beam_index,
|
||||
NR_gNB_COMMON *common_vars,
|
||||
int slot,
|
||||
int symbols_per_slot,
|
||||
int bitmap_symbols)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint16_t get_first_ant_idx(bool das, uint16_t num_ports_beams, uint16_t beam_id, uint16_t fapi_start_port)
|
||||
{
|
||||
@@ -135,6 +129,12 @@ void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
static void sig_handler(int sig_num)
|
||||
{
|
||||
oai_exit = 1;
|
||||
}
|
||||
|
||||
uint16_t nr_du[838];
|
||||
|
||||
uint64_t downlink_frequency[MAX_NUM_CCs][4];
|
||||
@@ -171,6 +171,7 @@ int main(int argc, char **argv)
|
||||
printf("About to Init RU threads\n");
|
||||
|
||||
lock_memory_to_ram();
|
||||
load_dftslib();
|
||||
|
||||
RC.nb_RU = 1;
|
||||
RC.ru = malloc(sizeof(RC.ru));
|
||||
@@ -178,24 +179,77 @@ int main(int argc, char **argv)
|
||||
init_NR_RU(config_get_if(), NULL);
|
||||
|
||||
RU_t *ru = RC.ru[0];
|
||||
ORU_t oru = {0};
|
||||
oru.ru = ru;
|
||||
int ret = get_oru_options(&oru);
|
||||
AssertFatal(ret == 0, "Cannot configure oru, check your config file/cmdline");
|
||||
ru->numerology = oru.numerology;
|
||||
oru_init_frame_parms(&oru);
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nr_dump_frame_parms(fp);
|
||||
init_symbol_rotation(fp);
|
||||
init_timeshift_rotation(fp->ofdm_symbol_size, fp->nb_prefix_samples, fp->ofdm_offset_divisor, fp->timeshift_symbol_rotation);
|
||||
ru->if_south = LOCAL_RF;
|
||||
nr_phy_init_RU(oru.ru);
|
||||
fill_rf_config(ru, ru->rf_config_file);
|
||||
ru->N_TA_offset = set_default_nta_offset(fp->freq_range, fp->samples_per_subframe);
|
||||
|
||||
while (oai_exit == 0)
|
||||
/* set PRACH configuration */
|
||||
nfapi_nr_prach_config_t *prach_config = &ru->config.prach_config;
|
||||
prach_config->prach_ConfigurationIndex.value = oru.prach_config_index;
|
||||
prach_config->num_prach_fd_occasions_list[0].k1.value = oru.prach_msg1_freq;
|
||||
prach_config->prach_sequence_length.value = 1;
|
||||
prach_config->prach_sub_c_spacing.value = 1;
|
||||
prach_config->num_prach_fd_occasions.value = 1;
|
||||
|
||||
reset_meas(&oru.rx_prach);
|
||||
oru.prach_info = get_nr_prach_occasion_info_from_index(oru.prach_config_index, FR1, fp->frame_type);
|
||||
LOG_A(PHY, "PRACH configuration index %d\n", oru.prach_config_index);
|
||||
LOG_A(PHY,
|
||||
"PRACH format %d start_symbol %d duration %d\n",
|
||||
oru.prach_info.format,
|
||||
oru.prach_info.start_symbol,
|
||||
oru.prach_info.N_dur);
|
||||
prepare_prach_item(&oru);
|
||||
|
||||
oru.fronthaul = oru_fh_init(&oru.fh_config);
|
||||
AssertFatal(oru.fronthaul != NULL, "Cannot configure oru fronthaul, check your config file/cmdline");
|
||||
|
||||
LOG_I(PHY, "starting rfdevice\n");
|
||||
ret = openair0_device_load(&ru->rfdevice, &ru->openair0_cfg);
|
||||
AssertFatal(ret == 0, "RU %u: openair0_device_load() ret %d: cannot initialize rfdevice\n", ru->idx, ret);
|
||||
ret = ru->rfdevice.trx_start_func(&ru->rfdevice);
|
||||
AssertFatal(ret == 0, "RU %u: trx_start_func() ret %d: cannot start rfdevice\n", ru->idx, ret);
|
||||
|
||||
threadCreate(&oru.north_read_thread, oru_north_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
|
||||
threadCreate(&oru.south_read_thread, oru_south_read_thread, (void *)&oru, "south_read_thread", -1, OAI_PRIORITY_RT_MAX);
|
||||
usleep(1000);
|
||||
oru_fh_start(oru.fronthaul);
|
||||
|
||||
// Signal handler
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
while (oai_exit == 0) {
|
||||
oru_fh_print_stats(oru.fronthaul);
|
||||
sleep(1);
|
||||
// stop threads
|
||||
}
|
||||
|
||||
kill_NR_RU_proc(0);
|
||||
pthread_join(oru.north_read_thread, NULL);
|
||||
pthread_join(oru.south_read_thread, NULL);
|
||||
|
||||
end_configmodule(uniqCfg);
|
||||
oru_fh_stop(oru.fronthaul);
|
||||
|
||||
if (ru->rfdevice.trx_stop_func) {
|
||||
ru->rfdevice.trx_stop_func(&ru->rfdevice);
|
||||
ru->rfdevice.trx_stop_func = NULL;
|
||||
}
|
||||
|
||||
if (ru->rfdevice.trx_end_func) {
|
||||
ru->rfdevice.trx_end_func(&ru->rfdevice);
|
||||
ru->rfdevice.trx_end_func = NULL;
|
||||
}
|
||||
|
||||
if (ru->ifdevice.trx_end_func) {
|
||||
ru->ifdevice.trx_end_func(&ru->ifdevice);
|
||||
ru->ifdevice.trx_end_func = NULL;
|
||||
}
|
||||
end_configmodule(uniqCfg);
|
||||
|
||||
logClean();
|
||||
printf("Bye.\n");
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
|
||||
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/if_defs.h"
|
||||
|
||||
#include "PHY/phy_vars.h"
|
||||
|
||||
760
executables/nr-oru.c
Normal file
760
executables/nr-oru.c
Normal file
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/system.h"
|
||||
#include "nr-oru.h"
|
||||
#include "openair1/PHY/defs_nr_common.h"
|
||||
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
||||
#include "oru_packet_processor.h"
|
||||
#include <time.h>
|
||||
#include "openair1/PHY/MODULATION/nr_modulation.h"
|
||||
#include "openair1/SCHED_NR/sched_nr.h"
|
||||
#include "openair1/PHY/MODULATION/modulation_common.h"
|
||||
#include "openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h"
|
||||
|
||||
#define CONFIG_SECTION_ORU "ORUs.[0]"
|
||||
|
||||
#define CONFIG_STRING_ORU_TX_BW_LIST "tx_bw"
|
||||
#define CONFIG_STRING_ORU_RX_BW_LIST "rx_bw"
|
||||
#define CONFIG_STRING_ORU_CARRIER_TX_LIST "carrier_tx"
|
||||
#define CONFIG_STRING_ORU_CARRIER_RX_LIST "carrier_rx"
|
||||
#define CONFIG_STRING_ORU_FRAME_TYPE "frame_type"
|
||||
#define CONFIG_STRING_ORU_PRACH_CONFIGID "prach_config_index"
|
||||
#define CONFIG_STRING_ORU_PRACH_MSG1FREQ "prach_msg1_start"
|
||||
#define CONFIG_STRING_ORU_NUMEROLOGY "mu"
|
||||
#define CONFIG_STRING_ORU_TDD_PERIOD "tdd_period"
|
||||
#define CONFIG_STRING_ORU_NUM_DL_SLOTS "num_dl_slots"
|
||||
#define CONFIG_STRING_ORU_NUM_UL_SLOTS "num_ul_slots"
|
||||
#define CONFIG_STRING_ORU_NUM_DL_SYMBOLS "num_dl_symbols"
|
||||
#define CONFIG_STRING_ORU_NUM_UL_SYMBOLS "num_ul_symbols"
|
||||
|
||||
#define HLP_ORU_TX_BW "set the TX bandwidth list per component carrier"
|
||||
#define HLP_ORU_RX_BW "set the RX bandwidth list per component carrier"
|
||||
#define HLP_ORU_CARRIER_TX "set the TX carrier frequencies per component carrier"
|
||||
#define HLP_ORU_CARRIER_RX "set the RX carrier frequencies per component carrier"
|
||||
#define HLP_ORU_FRAMETYPE "set the Frame type TDD/FDD of all component carriers"
|
||||
#define HLP_ORU_PRACH_CONFIGID "set the PRACH configuration id of all component carriers"
|
||||
#define HLP_ORU_PRACH_MSG1FREQ "set the PRACH MSG1 frequency of all component carriers"
|
||||
#define HLP_ORU_NUMEROLOGY "set the numerology of the RU"
|
||||
#define HLP_ORU_TDD_PERIOD "set the 3GPP TDD periodificty 0-9"
|
||||
#define HLP_ORU_NUM_DL_SLOTS "set the number of DL Slots in TDD"
|
||||
#define HLP_ORU_NUM_UL_SLOTS "set the number of UL Slots in TDD"
|
||||
#define HLP_ORU_NUM_DL_SYMBOLS "set the number of DL symbols in the mixed slot"
|
||||
#define HLP_ORU_NUM_UL_SYMBOLS "set the number of UL symbols in the mixed slot"
|
||||
|
||||
// clang-format off
|
||||
#define CMDLINE_PARAMS_DESC_ORU \
|
||||
{ \
|
||||
{CONFIG_STRING_ORU_TX_BW_LIST, HLP_ORU_TX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_RX_BW_LIST, HLP_ORU_RX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_CARRIER_TX_LIST, HLP_ORU_CARRIER_TX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_CARRIER_RX_LIST, HLP_ORU_CARRIER_RX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_FRAME_TYPE, HLP_ORU_FRAMETYPE, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_PRACH_CONFIGID, HLP_ORU_PRACH_CONFIGID, 0, .uptr=NULL, .defintval=152, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_PRACH_MSG1FREQ, HLP_ORU_PRACH_MSG1FREQ, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUMEROLOGY, HLP_ORU_NUMEROLOGY, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_TDD_PERIOD, HLP_ORU_TDD_PERIOD, 0, .uptr=NULL, .defintval=5, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_DL_SLOTS, HLP_ORU_NUM_DL_SLOTS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_UL_SLOTS, HLP_ORU_NUM_UL_SLOTS, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_DL_SYMBOLS, HLP_ORU_NUM_DL_SYMBOLS, 0, .uptr=NULL, .defintval=7, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_UL_SYMBOLS, HLP_ORU_NUM_UL_SYMBOLS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
#define CONFIG_SECTION_ORU_FH "ORUs.[0].fronthaul"
|
||||
|
||||
#define CONFIG_STRING_ORU_DPDK_DEVICES "dpdk_devices"
|
||||
#define CONFIG_STRING_RX_CORE "rx_core"
|
||||
#define CONFIG_STRING_EXTRA_EAL_ARGS "extra_eal_args"
|
||||
#define CONFIG_STRING_DU_MAC_ADDRESSES "du_mac_addr"
|
||||
#define CONFIG_STRING_MTU "mtu"
|
||||
#define CONFIG_STRING_T2A_UP "T2a_up"
|
||||
#define CONFIG_STRING_T2A_CP "T2a_cp"
|
||||
#define CONFIG_STRING_PRACH_EAXC_OFFSET "prach_eaxc_offset"
|
||||
|
||||
#define HLP_DPDK_DEVICES "DPDK devices to use for the O-RU."
|
||||
#define HLP_RX_CORE "The CPU core to be used to deploy dpdk RX worker for O-RU."
|
||||
#define HLP_EXTRA_EAL_ARGS "Extra arguments passed to RTE_EAL_INIT."
|
||||
#define HLP_DU_MAC_ADDRESSES "DU MAC addreses, used to prepare Ethernet headers."
|
||||
#define HLP_MTU "MTU for RX and TX."
|
||||
#define HLP_PRACH_EAXC_OFFSET "PRACH eAxC offset."
|
||||
|
||||
// clang-format off
|
||||
#define CMDLINE_PARAMS_DESC_ORU_FH \
|
||||
{ \
|
||||
{CONFIG_STRING_ORU_DPDK_DEVICES, HLP_DPDK_DEVICES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_RX_CORE, HLP_RX_CORE, PARAMFLAG_MANDATORY, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_EXTRA_EAL_ARGS, HLP_EXTRA_EAL_ARGS, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_DU_MAC_ADDRESSES, HLP_DU_MAC_ADDRESSES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_MTU, HLP_MTU, 0, .iptr=NULL, .defintval=9600, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_T2A_UP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_T2A_CP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_PRACH_EAXC_OFFSET, HLP_PRACH_EAXC_OFFSET, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0} \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
extern void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case);
|
||||
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols);
|
||||
|
||||
void prepare_prach_item(ORU_t *oru)
|
||||
{
|
||||
AssertFatal(oru->ru != NULL, "ORU not configured\n");
|
||||
AssertFatal(oru->ru->nr_frame_parms != NULL, "ORU not configured\n");
|
||||
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
|
||||
RU_t *ru = oru->ru;
|
||||
prach_item_t *prach_item = &oru->prach_item;
|
||||
prach_item->num_slots = oru->prach_info.format < 4 ? get_long_prach_dur(oru->prach_info.format, fp->numerology_index) : 1;
|
||||
prach_item->msg1_frequencystart = oru->prach_msg1_freq;
|
||||
prach_item->mu = fp->numerology_index;
|
||||
nfapi_nr_config_request_scf_t *cfg = &ru->config;
|
||||
prach_item->prach_sequence_length = cfg->prach_config.prach_sequence_length.value;
|
||||
prach_item->restricted_set = 0;
|
||||
prach_item->numerology_index = fp->numerology_index;
|
||||
prach_item->nb_rx = ru->nb_rx;
|
||||
prach_item->rx_prach = &oru->rx_prach;
|
||||
|
||||
// Fill PRACH PDU
|
||||
nfapi_nr_prach_pdu_t *prach_pdu = &prach_item->pdu;
|
||||
prach_pdu->prach_start_symbol = oru->prach_info.start_symbol;
|
||||
prach_pdu->num_prach_ocas = 1; // TODO: Hardcoded.
|
||||
|
||||
uint16_t format0 = oru->prach_info.format & 0xff;
|
||||
uint16_t format1 = (oru->prach_info.format >> 8) & 0xff;
|
||||
if (format1 != 0xff) {
|
||||
switch (format0) {
|
||||
case 0xa1:
|
||||
prach_pdu->prach_format = 11;
|
||||
break;
|
||||
case 0xa2:
|
||||
prach_pdu->prach_format = 12;
|
||||
break;
|
||||
case 0xa3:
|
||||
prach_pdu->prach_format = 13;
|
||||
break;
|
||||
default:
|
||||
AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
|
||||
}
|
||||
} else {
|
||||
switch (format0) {
|
||||
case 0:
|
||||
prach_pdu->prach_format = 0;
|
||||
break;
|
||||
case 1:
|
||||
prach_pdu->prach_format = 1;
|
||||
break;
|
||||
case 2:
|
||||
prach_pdu->prach_format = 2;
|
||||
break;
|
||||
case 3:
|
||||
prach_pdu->prach_format = 3;
|
||||
break;
|
||||
case 0xa1:
|
||||
prach_pdu->prach_format = 4;
|
||||
break;
|
||||
case 0xa2:
|
||||
prach_pdu->prach_format = 5;
|
||||
break;
|
||||
case 0xa3:
|
||||
prach_pdu->prach_format = 6;
|
||||
break;
|
||||
case 0xb1:
|
||||
prach_pdu->prach_format = 7;
|
||||
break;
|
||||
case 0xb4:
|
||||
prach_pdu->prach_format = 8;
|
||||
break;
|
||||
case 0xc0:
|
||||
prach_pdu->prach_format = 9;
|
||||
break;
|
||||
case 0xc2:
|
||||
prach_pdu->prach_format = 10;
|
||||
break;
|
||||
default:
|
||||
AssertFatal(1 == 0, "Invalid PRACH format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_oru_options(ORU_t *oru)
|
||||
{
|
||||
int DEFBW[] = {273};
|
||||
int DEFCARRIER[] = {3430560};
|
||||
paramdef_t param[] = CMDLINE_PARAMS_DESC_ORU;
|
||||
int nump = sizeofArray(param);
|
||||
|
||||
int ret = config_get(config_get_if(), param, nump, CONFIG_SECTION_ORU);
|
||||
if (ret <= 0) {
|
||||
LOG_E(NR_PHY, "problem reading section \"%s\"\n", CONFIG_SECTION_ORU);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < oru->ru->num_bands; i++) {
|
||||
oru->bw_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_TX_BW_LIST)->iptr[i];
|
||||
oru->bw_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_RX_BW_LIST)->iptr[i];
|
||||
oru->carrier_freq_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_TX_LIST)->iptr[i];
|
||||
oru->carrier_freq_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_RX_LIST)->iptr[i];
|
||||
}
|
||||
oru->frame_type = *gpd(param, nump, CONFIG_STRING_ORU_FRAME_TYPE)->iptr;
|
||||
oru->prach_config_index = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_CONFIGID)->iptr;
|
||||
oru->prach_msg1_freq = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_MSG1FREQ)->iptr;
|
||||
oru->numerology = *gpd(param, nump, CONFIG_STRING_ORU_NUMEROLOGY)->iptr;
|
||||
oru->tdd_period = *gpd(param, nump, CONFIG_STRING_ORU_TDD_PERIOD)->iptr;
|
||||
oru->num_DL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SLOTS)->iptr;
|
||||
oru->num_UL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SLOTS)->iptr;
|
||||
oru->num_DL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SYMBOLS)->iptr;
|
||||
oru->num_UL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SYMBOLS)->iptr;
|
||||
|
||||
paramdef_t fh_param[] = CMDLINE_PARAMS_DESC_ORU_FH;
|
||||
nump = sizeofArray(fh_param);
|
||||
oru_fh_config_t *fh_cfg = &oru->fh_config;
|
||||
ret = config_get(config_get_if(), fh_param, nump, CONFIG_SECTION_ORU_FH);
|
||||
if (ret <= 0) {
|
||||
printf("problem reading section \"%s\"\n", CONFIG_SECTION_ORU_FH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oru_fh_dpdk_config_t *dpdk_conf = &fh_cfg->dpdk_conf;
|
||||
int num_dpdk_devices = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->numelt;
|
||||
dpdk_conf->num_dpdk_devices = num_dpdk_devices;
|
||||
AssertFatal(num_dpdk_devices > 0 && num_dpdk_devices <= 2,
|
||||
"Invalid number of DPDK devices (%d). Configure 1 or 2 devices\n",
|
||||
num_dpdk_devices);
|
||||
for (int i = 0; i < num_dpdk_devices; i++) {
|
||||
dpdk_conf->dpdk_devices[i] = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->strlistptr[i];
|
||||
}
|
||||
dpdk_conf->extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->strlistptr;
|
||||
dpdk_conf->num_extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->numelt;
|
||||
|
||||
fh_cfg->num_du_mac_addrs = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->numelt;
|
||||
for (int i = 0; i < fh_cfg->num_du_mac_addrs; i++) {
|
||||
fh_cfg->du_mac_addrs[i] = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->strlistptr[i];
|
||||
AssertFatal(strlen(fh_cfg->du_mac_addrs[i]) == 17, "Invalid MAC address\n");
|
||||
}
|
||||
fh_cfg->enable_compression = false;
|
||||
fh_cfg->rx_core = *gpd(fh_param, nump, CONFIG_STRING_RX_CORE)->iptr;
|
||||
fh_cfg->mtu = *gpd(fh_param, nump, CONFIG_STRING_MTU)->iptr;
|
||||
fh_cfg->num_prbs = oru->bw_tx[0];
|
||||
fh_cfg->numerology = oru->numerology;
|
||||
fh_cfg->prach_eaxc_offset = *gpd(fh_param, nump, CONFIG_STRING_PRACH_EAXC_OFFSET)->iptr;
|
||||
|
||||
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_UP);
|
||||
fh_cfg->T2a_up_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[0];
|
||||
fh_cfg->T2a_up_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[1];
|
||||
AssertFatal(fh_cfg->T2a_up_min_uS <= fh_cfg->T2a_up_max_uS,
|
||||
"T2a max (%d) has to be greater than T2a min (%d)\n",
|
||||
fh_cfg->T2a_up_max_uS,
|
||||
fh_cfg->T2a_up_min_uS);
|
||||
|
||||
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_CP);
|
||||
fh_cfg->T2a_cp_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[0];
|
||||
fh_cfg->T2a_cp_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[1];
|
||||
AssertFatal(fh_cfg->T2a_cp_min_uS <= fh_cfg->T2a_cp_max_uS,
|
||||
"T2a max (%d) has to be greater than T2a min (%d)\n",
|
||||
fh_cfg->T2a_cp_max_uS,
|
||||
fh_cfg->T2a_cp_min_uS);
|
||||
|
||||
oru_fh_tdd_pattern_t *tdd_pattern = &fh_cfg->tdd_pattern;
|
||||
tdd_pattern->num_dl_slots = oru->num_DL_slots;
|
||||
tdd_pattern->num_ul_slots = oru->num_UL_slots;
|
||||
tdd_pattern->num_dl_symbols = oru->num_DL_symbols;
|
||||
tdd_pattern->num_ul_symbols = oru->num_UL_symbols;
|
||||
int num_slots_frame = (1 << oru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
||||
int num_period_frame = get_nb_periods_per_frame(oru->tdd_period);
|
||||
int num_slots_period = num_slots_frame / num_period_frame;
|
||||
tdd_pattern->tdd_pattern_length_slots = num_slots_period;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oru_init_frame_parms(ORU_t *oru)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
|
||||
fp->frame_type = oru->frame_type;
|
||||
ru->config.cell_config.frame_duplex_type.value = oru->frame_type;
|
||||
ru->config.cell_config.frame_duplex_type.tl.tag = 0x100D;
|
||||
fp->N_RB_DL = oru->bw_tx[0];
|
||||
ru->config.ssb_config.scs_common.value = ru->numerology;
|
||||
ru->config.carrier_config.dl_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_tx[0];
|
||||
fp->N_RB_UL = oru->bw_rx[0];
|
||||
ru->config.carrier_config.ul_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_rx[0];
|
||||
fp->numerology_index = ru->numerology;
|
||||
LOG_I(NR_PHY,
|
||||
"Set RU frame type to %s, N_RB_DL %d, N_RB_UL %d, mu %d\n",
|
||||
oru->frame_type == TDD ? "TDD" : "FDD",
|
||||
oru->bw_tx[0],
|
||||
oru->bw_rx[0],
|
||||
ru->numerology);
|
||||
set_scs_parameters(fp, fp->numerology_index, oru->bw_tx[0], 0);
|
||||
fp->slots_per_frame = 10 * fp->slots_per_subframe;
|
||||
fp->nb_antennas_rx = ru->nb_rx;
|
||||
fp->nb_antennas_tx = ru->nb_tx;
|
||||
fp->symbols_per_slot = 14;
|
||||
fp->samples_per_subframe_wCP = fp->ofdm_symbol_size * fp->symbols_per_slot * fp->slots_per_subframe;
|
||||
fp->samples_per_frame_wCP = 10 * fp->samples_per_subframe_wCP;
|
||||
fp->samples_per_slot_wCP = fp->symbols_per_slot * fp->ofdm_symbol_size;
|
||||
fp->samples_per_slotN0 = (fp->nb_prefix_samples + fp->ofdm_symbol_size) * fp->symbols_per_slot;
|
||||
fp->samples_per_slot0 =
|
||||
fp->nb_prefix_samples0 + ((fp->symbols_per_slot - 1) * fp->nb_prefix_samples) + (fp->symbols_per_slot * fp->ofdm_symbol_size);
|
||||
fp->samples_per_subframe = (fp->nb_prefix_samples0 + fp->ofdm_symbol_size) * 2
|
||||
+ (fp->nb_prefix_samples + fp->ofdm_symbol_size) * (fp->symbols_per_slot * fp->slots_per_subframe - 2);
|
||||
fp->samples_per_frame = 10 * fp->samples_per_subframe;
|
||||
fp->freq_range = (oru->carrier_freq_tx[0] < 6e6) ? FR1 : FR2;
|
||||
|
||||
fp->dl_CarrierFreq = (double)oru->carrier_freq_tx[0] * 1000;
|
||||
fp->ul_CarrierFreq = (double)oru->carrier_freq_rx[0] * 1000;
|
||||
fp->Ncp = NORMAL;
|
||||
fp->ofdm_offset_divisor = 8;
|
||||
|
||||
// Split 7.2 parameters
|
||||
ru->config.prach_config.num_prach_fd_occasions.value = 1;
|
||||
ru->config.prach_config.prach_ConfigurationIndex.value = oru->prach_config_index;
|
||||
ru->config.prach_config.prach_ConfigurationIndex.tl.tag = 0x1029;
|
||||
ru->config.prach_config.num_prach_fd_occasions_list = malloc(sizeof(*ru->config.prach_config.num_prach_fd_occasions_list));
|
||||
ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value = oru->prach_msg1_freq;
|
||||
if (ru->config.cell_config.frame_duplex_type.value == 1 /* TDD */) {
|
||||
ru->config.tdd_table.tdd_period.value = oru->tdd_period;
|
||||
ru->config.tdd_table.tdd_period.tl.tag = 0x1026;
|
||||
int numb_slots_frame = (1 << ru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
||||
int numb_period_frame = get_nb_periods_per_frame(oru->tdd_period);
|
||||
int numb_slots_period = numb_slots_frame / numb_period_frame;
|
||||
ru->config.tdd_table.max_tdd_periodicity_list =
|
||||
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list) * (numb_slots_frame));
|
||||
for (int n = 0; n < numb_slots_frame; n++) {
|
||||
int s = 0;
|
||||
int p = n % numb_slots_period;
|
||||
nfapi_nr_max_tdd_periodicity_t *periodicity = &ru->config.tdd_table.max_tdd_periodicity_list[n];
|
||||
periodicity->max_num_of_symbol_per_slot_list =
|
||||
malloc(sizeof(*periodicity->max_num_of_symbol_per_slot_list) * NR_SYMBOLS_PER_SLOT);
|
||||
nfapi_nr_max_num_of_symbol_per_slot_t *symbol_list = periodicity->max_num_of_symbol_per_slot_list;
|
||||
if (p < oru->num_DL_slots) {
|
||||
for (s = 0; s < 14; s++)
|
||||
symbol_list[s].slot_config.value = 0;
|
||||
} else if (p == oru->num_DL_slots) {
|
||||
for (s = 0; s < oru->num_DL_symbols; s++)
|
||||
symbol_list[s].slot_config.value = 0;
|
||||
for (; s < NR_SYMBOLS_PER_SLOT - oru->num_UL_symbols; s++)
|
||||
symbol_list[s].slot_config.value = 2;
|
||||
for (; s < NR_SYMBOLS_PER_SLOT; s++)
|
||||
symbol_list[s].slot_config.value = 1;
|
||||
} else {
|
||||
for (s = 0; s < NR_SYMBOLS_PER_SLOT; s++)
|
||||
symbol_list[s].slot_config.value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fft_and_cp_insertion(NR_DL_FRAME_PARMS *fp, c16_t *txdataF, c16_t *txdata, int slot, int symbol)
|
||||
{
|
||||
int nb_prefix_samples = fp->nb_prefix_samples;
|
||||
if (fp->Ncp != 1) {
|
||||
if (fp->numerology_index != 0) {
|
||||
if (!(slot % (fp->slots_per_subframe / 2)) && (symbol == 0)) {
|
||||
nb_prefix_samples = fp->nb_prefix_samples0;
|
||||
}
|
||||
} else {
|
||||
if (symbol % 0x7 == 0) {
|
||||
nb_prefix_samples = fp->nb_prefix_samples0;
|
||||
}
|
||||
}
|
||||
}
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, nb_prefix_samples, CYCLIC_PREFIX);
|
||||
}
|
||||
|
||||
static void dl_symbol_process(RU_t *ru, int frame, int slot, int symbol, c16_t **txDataF, int64_t timestamp)
|
||||
{
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
uint32_t slot_offset = get_samples_slot_timestamp(fp, slot);
|
||||
uint32_t symbol_offset = get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
|
||||
__attribute__((aligned(64))) c16_t txdataF_shifted[fp->ofdm_symbol_size];
|
||||
memset(txdataF_shifted, 0, sizeof(txdataF_shifted));
|
||||
c16_t *rotation = fp->symbol_rotation[0] + (slot % fp->slots_per_subframe) * fp->symbols_per_slot + symbol;
|
||||
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
|
||||
// Phase compensation
|
||||
rotate_cpx_vector(txDataF[aatx], *rotation, txDataF[aatx], fp->N_RB_DL * NR_NB_SC_PER_RB, 15);
|
||||
// FFT Shift
|
||||
const int num_samp_half = fp->N_RB_DL * NR_NB_SC_PER_RB / 2;
|
||||
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
|
||||
memcpy(txdataF_shifted + first_carrier_offset, txDataF[aatx], num_samp_half * sizeof(c16_t));
|
||||
memcpy(txdataF_shifted, txDataF[aatx] + num_samp_half, num_samp_half * sizeof(c16_t));
|
||||
fft_and_cp_insertion(ru->nr_frame_parms,
|
||||
txdataF_shifted,
|
||||
(c16_t *)&ru->common.txdata[aatx][slot_offset + symbol_offset],
|
||||
slot,
|
||||
symbol);
|
||||
}
|
||||
tx_rf_symbols(ru, frame, slot, timestamp, symbol, 1);
|
||||
}
|
||||
|
||||
void *oru_north_read_thread(void *arg)
|
||||
{
|
||||
ORU_t *oru = (ORU_t *)arg;
|
||||
|
||||
RU_t *ru = (RU_t *)oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
|
||||
__attribute__((aligned(64))) c16_t txDataF[ru->nb_tx][fp->N_RB_DL * NR_NB_SC_PER_RB];
|
||||
memset(txDataF, 0, sizeof(txDataF));
|
||||
c16_t *txDataF_ptr[ru->nb_tx];
|
||||
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
|
||||
txDataF_ptr[aatx] = txDataF[aatx];
|
||||
}
|
||||
uint32_t start_frame, start_slot;
|
||||
uint64_t start_hyper_frame;
|
||||
struct timespec utc_anchor_point;
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &start_hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
|
||||
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
|
||||
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
|
||||
// subtract the start_frame and start_slot from the timestamp simplify calculation below.
|
||||
start_timestamp -= (start_frame * fp->samples_per_frame + get_samples_slot_timestamp(fp, start_slot));
|
||||
// Now start_timestamp points to the start sample of the frame 0 slot 0 symbol 0 of hyperframe 0
|
||||
LOG_A(PHY, "DL thread started: start_timestamp %ld, start_frame %d, start_slot %d\n", start_timestamp, start_frame, start_slot);
|
||||
|
||||
while (!oai_exit) {
|
||||
int frame = -1, slot = -1, symbol = -1;
|
||||
uint64_t hyper_frame;
|
||||
int ret = oru_fh_tx_read_symbol(oru->fronthaul, (uint32_t **)txDataF_ptr, ru->nb_tx, &hyper_frame, &frame, &slot, &symbol);
|
||||
if (ret != 0) {
|
||||
LOG_E(PHY, "[RU_thread] read data error: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
|
||||
continue;
|
||||
}
|
||||
if (start_hyper_frame > hyper_frame) {
|
||||
continue;
|
||||
}
|
||||
uint64_t num_frames = (hyper_frame - start_hyper_frame) * 1024 + frame;
|
||||
int64_t timestamp = start_timestamp + num_frames * fp->samples_per_frame + get_samples_slot_timestamp(fp, slot)
|
||||
+ get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
if (timestamp < 0) {
|
||||
continue;
|
||||
}
|
||||
dl_symbol_process(ru, frame, slot, symbol, txDataF_ptr, timestamp);
|
||||
if (frame % 256 == 0 && slot == 0 && symbol == 0) {
|
||||
LOG_I(PHY, "[RU_thread] read data: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns PRACH symbol that was received in current frame, slot and symbol.
|
||||
// If no PRACH symbol was received, returns -1
|
||||
int get_prach_symbol(ORU_t *oru, int frame, int slot, int symbol, int numerology)
|
||||
{
|
||||
uint16_t RA_sfn_index;
|
||||
AssertFatal(oru->ru->nr_frame_parms->frame_type == TDD, "Only supports TDD\n");
|
||||
if (get_nr_prach_sched_from_info(oru->prach_info, oru->prach_config_index, frame, slot, numerology, FR1, &RA_sfn_index, true)) {
|
||||
int format = oru->prach_item.pdu.prach_format;
|
||||
int start_symbol = oru->prach_item.pdu.prach_start_symbol;
|
||||
symbol -= start_symbol;
|
||||
// TODO: Support more PRACH formats
|
||||
AssertFatal(format == 8, "only support format B4\n");
|
||||
// TODO: This is not exactly the case but it is correct
|
||||
if (symbol >= 0 && symbol < 12) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void receive_prach(ORU_t *oru, int frame, int slot, int symbol, int prach_symbol)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
oru->prach_item.frame = frame;
|
||||
oru->prach_item.slot = slot;
|
||||
|
||||
c16_t rxdataF[ru->nb_rx][NR_PRACH_SEQ_LEN_L];
|
||||
memset(rxdataF, 0, sizeof(rxdataF));
|
||||
|
||||
rx_nr_prach_ru_rep(&oru->prach_item,
|
||||
ru->common.rxdata,
|
||||
fp,
|
||||
ru->N_TA_offset,
|
||||
prach_symbol,
|
||||
0, // prachOccasion
|
||||
rxdataF);
|
||||
|
||||
c16_t *rxdataF_ptr[ru->nb_rx];
|
||||
for (int aarx = 0; aarx < ru->nb_rx; aarx++) {
|
||||
rxdataF_ptr[aarx] = rxdataF[aarx];
|
||||
}
|
||||
oru_fh_rx_send_prach(oru->fronthaul, (uint32_t **)rxdataF_ptr, ru->nb_rx, frame, slot, symbol);
|
||||
}
|
||||
|
||||
#define MAX_PENDING_UL_JOBS 64
|
||||
|
||||
typedef struct {
|
||||
ul_job_t job;
|
||||
bool active;
|
||||
int symbols_sent;
|
||||
} ul_pending_t;
|
||||
|
||||
static void receive_pusch(ORU_t *oru, int frame, int slot, int symbol, ul_job_t *job)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
int aarx = job->antenna_id;
|
||||
|
||||
if (aarx < 0 || aarx >= fp->nb_antennas_rx) {
|
||||
LOG_W(PHY, "[ORU south] receive_pusch: invalid antenna_id %d\n", aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
// CP removal + FFT → full ofdm_symbol_size frequency-domain output
|
||||
c16_t rxdataF_fft[fp->ofdm_symbol_size] __attribute__((aligned(32)));
|
||||
nr_symbol_fep_ul(fp, (c16_t *)ru->common.rxdata[aarx], rxdataF_fft, symbol, slot, ru->N_TA_offset);
|
||||
|
||||
// Phase decompensation (conjugate rotation for UL)
|
||||
apply_nr_rotation_symbol_RX(fp->symbols_per_slot,
|
||||
fp->slots_per_subframe,
|
||||
fp->timeshift_symbol_rotation,
|
||||
fp->first_carrier_offset,
|
||||
rxdataF_fft,
|
||||
fp->symbol_rotation[link_type_ul],
|
||||
fp->N_RB_UL,
|
||||
slot,
|
||||
symbol);
|
||||
|
||||
// Inverse FFT shift: split format → contiguous PRB format sent to DU.
|
||||
// DL TX shift: contiguous[0..N/2-1] → FFT_input[first_carrier_offset..] (negative freqs)
|
||||
// contiguous[N/2..N-1] → FFT_input[0..N/2-1] (positive freqs)
|
||||
// UL RX inverse: FFT_out[first_carrier_offset..] → contiguous[0..N/2-1]
|
||||
// FFT_out[0..N/2-1] → contiguous[N/2..N-1]
|
||||
const int num_samp_half = fp->N_RB_UL * NR_NB_SC_PER_RB / 2;
|
||||
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
|
||||
c16_t rxdataF[fp->N_RB_UL * NR_NB_SC_PER_RB];
|
||||
memcpy(rxdataF, rxdataF_fft + first_carrier_offset, num_samp_half * sizeof(c16_t));
|
||||
memcpy(rxdataF + num_samp_half, rxdataF_fft, num_samp_half * sizeof(c16_t));
|
||||
|
||||
oru_fh_rx_send_pusch(oru->fronthaul, (uint32_t *)rxdataF, symbol, job);
|
||||
}
|
||||
|
||||
#define UL_WORK_QUEUE_DEPTH 128
|
||||
|
||||
typedef struct {
|
||||
ORU_t *oru;
|
||||
int frame;
|
||||
int slot;
|
||||
int symbol;
|
||||
ul_job_t job;
|
||||
} ul_work_item_t;
|
||||
|
||||
typedef struct {
|
||||
ul_work_item_t ring[UL_WORK_QUEUE_DEPTH];
|
||||
int head;
|
||||
int tail;
|
||||
int count;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t work_available;
|
||||
pthread_cond_t space_available;
|
||||
bool running;
|
||||
} ul_work_queue_t;
|
||||
|
||||
static void *ul_worker_thread(void *arg)
|
||||
{
|
||||
ul_work_queue_t *q = arg;
|
||||
while (1) {
|
||||
pthread_mutex_lock(&q->lock);
|
||||
while (q->count == 0 && q->running)
|
||||
pthread_cond_wait(&q->work_available, &q->lock);
|
||||
if (!q->running && q->count == 0) {
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
break;
|
||||
}
|
||||
ul_work_item_t item = q->ring[q->head];
|
||||
q->head = (q->head + 1) % UL_WORK_QUEUE_DEPTH;
|
||||
q->count--;
|
||||
pthread_cond_signal(&q->space_available);
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
|
||||
receive_pusch(item.oru, item.frame, item.slot, item.symbol, &item.job);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dispatch_ul_work(ul_work_queue_t *q, ORU_t *oru, int frame, int slot, int symbol, const ul_job_t *job)
|
||||
{
|
||||
pthread_mutex_lock(&q->lock);
|
||||
while (q->count == UL_WORK_QUEUE_DEPTH)
|
||||
pthread_cond_wait(&q->space_available, &q->lock);
|
||||
q->ring[q->tail] = (ul_work_item_t){.oru = oru, .frame = frame, .slot = slot, .symbol = symbol, .job = *job};
|
||||
q->tail = (q->tail + 1) % UL_WORK_QUEUE_DEPTH;
|
||||
q->count++;
|
||||
pthread_cond_signal(&q->work_available);
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
}
|
||||
|
||||
void *oru_south_read_thread(void *arg)
|
||||
{
|
||||
ORU_t *oru = arg;
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
struct timespec utc_anchor_point;
|
||||
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
|
||||
uint32_t start_frame, start_slot;
|
||||
uint64_t hyper_frame;
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
|
||||
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
|
||||
|
||||
const int num_samples = 3000;
|
||||
c16_t throwaway_samples[ru->nb_rx][num_samples];
|
||||
void *rxp[ru->nb_rx];
|
||||
for (int i = 0; i < ru->nb_rx; i++)
|
||||
rxp[i] = throwaway_samples[i];
|
||||
|
||||
openair0_timestamp_t timestamp;
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, rxp, num_samples, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == num_samples, "Unexpected number of samples received\n");
|
||||
openair0_timestamp_t next_timestamp = timestamp + num_samples_read;
|
||||
while (next_timestamp > start_timestamp) {
|
||||
start_timestamp += get_samples_slot_duration(fp, start_slot, 1);
|
||||
start_slot++;
|
||||
if (start_slot == fp->slots_per_frame) {
|
||||
start_slot = 0;
|
||||
start_frame++;
|
||||
if (start_frame == 1024) {
|
||||
start_frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (next_timestamp < start_timestamp) {
|
||||
int num_samples_to_read = min(num_samples, (int)(start_timestamp - next_timestamp));
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, rxp, num_samples_to_read, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == num_samples_to_read, "Unexpected number of samples received\n");
|
||||
next_timestamp += num_samples_read;
|
||||
}
|
||||
|
||||
AssertFatal(next_timestamp == start_timestamp, "O-RU South thread could not sync to UTC anchor point\n");
|
||||
|
||||
int slot = start_slot;
|
||||
int frame = start_frame;
|
||||
|
||||
// Worker pool: one thread per RX antenna so all antennas in a symbol process in parallel.
|
||||
const int num_workers = ru->nb_rx;
|
||||
pthread_t workers[num_workers];
|
||||
ul_work_queue_t work_queue = {
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
.work_available = PTHREAD_COND_INITIALIZER,
|
||||
.space_available = PTHREAD_COND_INITIALIZER,
|
||||
.running = true,
|
||||
};
|
||||
for (int i = 0; i < num_workers; i++) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "ul_worker_%d", i);
|
||||
threadCreate(&workers[i], ul_worker_thread, &work_queue, name, -1, OAI_PRIORITY_RT_MAX);
|
||||
}
|
||||
|
||||
ul_pending_t pending_ul[MAX_PENDING_UL_JOBS] = {0};
|
||||
|
||||
while (!oai_exit) {
|
||||
int rx_slot_type = nr_slot_select(&ru->config, frame, slot);
|
||||
for (int symbol = 0; symbol < 14; symbol++) {
|
||||
int samples_to_read = get_samples_symbol_duration(fp, slot, symbol, 1);
|
||||
size_t offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
c16_t *rxp[fp->nb_antennas_rx];
|
||||
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
|
||||
rxp[aarx] = (c16_t *)&ru->common.rxdata[aarx][offset];
|
||||
}
|
||||
|
||||
openair0_timestamp_t timestamp;
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, (void **)rxp, samples_to_read, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == samples_to_read, "Unexpected number of samples received\n");
|
||||
LOG_D(PHY,
|
||||
"[ORU south] read data: frame %d, slot %d, symbol %d, timestamp %ld num_symbols %d, samples %d\n",
|
||||
frame,
|
||||
slot,
|
||||
symbol,
|
||||
timestamp,
|
||||
1,
|
||||
num_samples_read);
|
||||
|
||||
// Drain the UL job ring
|
||||
ul_job_t new_job;
|
||||
while (oru_fh_poll_ul_job(oru->fronthaul, &new_job) == 0) {
|
||||
bool added = false;
|
||||
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
|
||||
if (!pending_ul[i].active) {
|
||||
pending_ul[i] = (ul_pending_t){.job = new_job, .active = true, .symbols_sent = 0};
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added)
|
||||
LOG_W(PHY, "[ORU south] UL pending queue full, dropping job frame=%d slot=%d sym=%d\n",
|
||||
new_job.frame, new_job.slot_in_frame, new_job.symbol);
|
||||
}
|
||||
|
||||
if (rx_slot_type == NR_UPLINK_SLOT || rx_slot_type == NR_MIXED_SLOT) {
|
||||
|
||||
// Process pending jobs whose next symbol matches the current one
|
||||
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
|
||||
if (!pending_ul[i].active)
|
||||
continue;
|
||||
ul_job_t *j = &pending_ul[i].job;
|
||||
|
||||
// Skip jobs scheduled for a future slot or frame, and drop jobs in the past
|
||||
int slots_per_frame = fp->slots_per_frame;
|
||||
int total_slots = 1024 * slots_per_frame;
|
||||
int diff_slots = (j->frame * slots_per_frame + j->slot_in_frame) - (frame * slots_per_frame + slot);
|
||||
if (diff_slots < -total_slots / 2) {
|
||||
diff_slots += total_slots;
|
||||
} else if (diff_slots > total_slots / 2) {
|
||||
diff_slots -= total_slots;
|
||||
}
|
||||
|
||||
if (diff_slots > 0) {
|
||||
// Future slot: skip
|
||||
continue;
|
||||
}
|
||||
if (diff_slots < 0) {
|
||||
// Past slot: drop
|
||||
LOG_W(PHY, "[ORU south] missed UL slot %d.%d (now %d.%d), dropping job ant=%d\n",
|
||||
j->frame, j->slot_in_frame, frame, slot, j->antenna_id);
|
||||
pending_ul[i].active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same slot: check symbol
|
||||
int expected_symbol = j->symbol + pending_ul[i].symbols_sent;
|
||||
if (expected_symbol < symbol) {
|
||||
LOG_W(PHY, "[ORU south] missed UL symbol %d (now %d), dropping job ant=%d\n",
|
||||
expected_symbol, symbol, j->antenna_id);
|
||||
pending_ul[i].active = false;
|
||||
} else if (expected_symbol == symbol) {
|
||||
dispatch_ul_work(&work_queue, oru, frame, slot, symbol, j);
|
||||
if (++pending_ul[i].symbols_sent == j->num_symbols)
|
||||
pending_ul[i].active = false;
|
||||
}
|
||||
// expected_symbol > symbol: job spans multiple symbols, revisit next iteration
|
||||
}
|
||||
}
|
||||
int prach_symbol = get_prach_symbol(oru, frame, slot, symbol, ru->numerology);
|
||||
if (prach_symbol != -1)
|
||||
receive_prach(oru, frame, slot, symbol, prach_symbol);
|
||||
}
|
||||
slot++;
|
||||
if (slot == fp->slots_per_frame) {
|
||||
slot = 0;
|
||||
frame++;
|
||||
if (frame == 1024) {
|
||||
frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&work_queue.lock);
|
||||
work_queue.running = false;
|
||||
pthread_cond_broadcast(&work_queue.work_available);
|
||||
pthread_mutex_unlock(&work_queue.lock);
|
||||
for (int i = 0; i < num_workers; i++)
|
||||
pthread_join(workers[i], NULL);
|
||||
pthread_cond_destroy(&work_queue.work_available);
|
||||
pthread_cond_destroy(&work_queue.space_available);
|
||||
pthread_mutex_destroy(&work_queue.lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
59
executables/nr-oru.h
Normal file
59
executables/nr-oru.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef __NR_ORU_H__
|
||||
#define __NR_ORU_H__
|
||||
#include "openair1/PHY/defs_RU.h"
|
||||
#include <pthread.h>
|
||||
#include "oru_fh.h"
|
||||
#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h"
|
||||
#include "openair1/PHY/defs_gNB.h"
|
||||
|
||||
typedef struct {
|
||||
RU_t *ru;
|
||||
/// tx carrier
|
||||
uint64_t carrier_freq_tx[MAX_BANDS_PER_RRU];
|
||||
/// rx carrier
|
||||
uint64_t carrier_freq_rx[MAX_BANDS_PER_RRU];
|
||||
/// tx BW in PRBs
|
||||
int bw_tx[MAX_BANDS_PER_RRU];
|
||||
/// rx BW in PRBs
|
||||
int bw_rx[MAX_BANDS_PER_RRU];
|
||||
/// 3GPP FRAME Type FDD/TDD
|
||||
int frame_type;
|
||||
/// 3GPP PRACH configuration index
|
||||
int prach_config_index;
|
||||
/// 3GPP MSG1 Start frequency
|
||||
int prach_msg1_freq;
|
||||
/// 3GPP TDD periodicity (0.5 ms, 1 0.625ms, 2 1ms, 3 1.25ms, 4 2ms,5 2.5ms, 6 5ms, 7 10ms, 8 3ms, 9 4ms
|
||||
int tdd_period;
|
||||
/// number of DL slots
|
||||
int num_DL_slots;
|
||||
/// number of UL slots
|
||||
int num_UL_slots;
|
||||
/// number of DL symbols
|
||||
int num_DL_symbols;
|
||||
/// number of UL symbols
|
||||
int num_UL_symbols;
|
||||
int numerology;
|
||||
|
||||
pthread_t north_read_thread;
|
||||
pthread_t south_read_thread;
|
||||
oru_fh_config_t fh_config;
|
||||
void *fronthaul;
|
||||
|
||||
// PRACH related
|
||||
nr_prach_info_t prach_info;
|
||||
time_stats_t rx_prach;
|
||||
time_stats_t rx;
|
||||
prach_item_t prach_item;
|
||||
} ORU_t;
|
||||
|
||||
int get_oru_options(ORU_t *oru);
|
||||
void oru_init_frame_parms(ORU_t *oru);
|
||||
void *oru_north_read_thread(void *arg);
|
||||
void *oru_south_read_thread(void *arg);
|
||||
void prepare_prach_item(ORU_t *oru);
|
||||
|
||||
#endif
|
||||
@@ -18,11 +18,8 @@
|
||||
#include "common/utils/fsn.h"
|
||||
#include "common/ran_context.h"
|
||||
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/ethernet_lib.h"
|
||||
|
||||
#include "PHY/if4_tools.h"
|
||||
|
||||
#include "PHY/defs_nr_common.h"
|
||||
#include "PHY/phy_extern.h"
|
||||
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
||||
@@ -76,15 +73,6 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
ru->ifdevice.trx_write_func2(&ru->ifdevice, timestamp, buffs, 0, get_samples_per_slot(slot, ru->nr_frame_parms), 0, ru->nb_tx);
|
||||
}
|
||||
|
||||
// southbound IF4p5 fronthaul
|
||||
void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx);
|
||||
|
||||
if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0)
|
||||
send_IF4p5(ru,frame, slot, IF4p5_PDLFFT);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Input Fronthaul from south RCC/RAU */
|
||||
|
||||
@@ -148,196 +136,6 @@ void fh_if5_south_in(RU_t *ru, int *frame, int *tti)
|
||||
rxmeas.tv_nsec);
|
||||
}
|
||||
|
||||
// Synchronous if4p5 from south
|
||||
void fh_if4p5_south_in(RU_t *ru,
|
||||
int *frame,
|
||||
int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
int f,sl;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_number=0;
|
||||
uint32_t symbol_mask_full=0;
|
||||
|
||||
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
|
||||
recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number);
|
||||
|
||||
if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<<symbol_number);
|
||||
else if (packet_type == IF4p5_PULTICK) {
|
||||
if ((proc->first_rx == 0) && (f != *frame))
|
||||
LOG_E(PHY, "rx_fh_if4p5: PULTICK received frame %d != expected %d\n", f, *frame);
|
||||
|
||||
if ((proc->first_rx == 0) && (sl != *slot))
|
||||
LOG_E(PHY, "rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n", sl, *slot, proc->first_rx);
|
||||
|
||||
break;
|
||||
} else if (packet_type == IF4p5_PRACH) {
|
||||
// nothing in RU for RAU
|
||||
}
|
||||
|
||||
LOG_D(PHY,"rx_fh_if4p5: subframe %d symbol mask %x\n",*slot,proc->symbol_mask[sl]);
|
||||
} while(proc->symbol_mask[sl] != symbol_mask_full);
|
||||
|
||||
//caculate timestamp_rx, timestamp_tx based on frame and subframe
|
||||
proc->tti_rx = sl;
|
||||
proc->frame_rx = f;
|
||||
proc->timestamp_rx = (proc->frame_rx * fp->samples_per_subframe * 10) + get_samples_slot_timestamp(fp, proc->tti_rx);
|
||||
// proc->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_subframe);
|
||||
proc->tti_tx = (sl+ru->sl_ahead)%fp->slots_per_frame;
|
||||
proc->frame_tx = (sl > (fp->slots_per_frame - 1 - (ru->sl_ahead))) ? (f + 1) & 1023 : f;
|
||||
|
||||
if (proc->first_rx == 0) {
|
||||
if (proc->tti_rx != *slot) {
|
||||
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",proc->tti_rx,*slot);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
|
||||
if (proc->frame_rx != *frame) {
|
||||
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",proc->frame_rx,*frame);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
} else {
|
||||
proc->first_rx = 0;
|
||||
*frame = proc->frame_rx;
|
||||
*slot = proc->tti_rx;
|
||||
}
|
||||
|
||||
proc->symbol_mask[proc->tti_rx] = 0;
|
||||
LOG_D(PHY,"RU %d: fh_if4p5_south_in sleeping ...\n",ru->idx);
|
||||
}
|
||||
|
||||
// asynchronous inbound if4p5 fronthaul from south
|
||||
void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_number = 0;
|
||||
uint32_t symbol_mask = (1 << fp->symbols_per_slot) - 1;
|
||||
uint32_t prach_rx = 0;
|
||||
|
||||
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
|
||||
recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number);
|
||||
if (proc->first_rx != 0) {
|
||||
*frame = proc->frame_rx;
|
||||
*slot = proc->tti_rx;
|
||||
proc->first_rx = 0;
|
||||
} else {
|
||||
if (proc->frame_rx != *frame) {
|
||||
LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
if (proc->tti_rx != *slot) {
|
||||
LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
if (packet_type == IF4p5_PULFFT)
|
||||
symbol_mask &= ~(1 << symbol_number);
|
||||
else if (packet_type == IF4p5_PRACH)
|
||||
prach_rx &= ~0x1;
|
||||
} while (symbol_mask > 0 || prach_rx > 0); // haven't received all PUSCH symbols and PRACH information
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Input Fronthaul from North RRU */
|
||||
|
||||
// RRU IF4p5 TX fronthaul receiver. Assumes an if_device on input and if or rf device on output
|
||||
// receives one subframe's worth of IF4p5 OFDM symbols and OFDM modulates
|
||||
void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot)
|
||||
{
|
||||
uint32_t symbol_number=0;
|
||||
uint32_t symbol_mask, symbol_mask_full;
|
||||
uint16_t packet_type;
|
||||
/// **** incoming IF4p5 from remote RCC/RAU **** ///
|
||||
symbol_number = 0;
|
||||
symbol_mask = 0;
|
||||
symbol_mask_full = (1<<(ru->nr_frame_parms->symbols_per_slot))-1;
|
||||
|
||||
do {
|
||||
recv_IF4p5(ru, frame, slot, &packet_type, &symbol_number);
|
||||
symbol_mask = symbol_mask | (1<<symbol_number);
|
||||
} while (symbol_mask != symbol_mask_full);
|
||||
}
|
||||
|
||||
void fh_if5_north_asynch_in(RU_t *ru, int *frame, int *slot)
|
||||
{
|
||||
AssertFatal(1 == 0, "Shouldn't get here\n");
|
||||
}
|
||||
|
||||
void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nfapi_nr_config_request_scf_t *cfg = &ru->config;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_mask_full = 0;
|
||||
int slot_tx,frame_tx;
|
||||
LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru);
|
||||
uint32_t symbol_number = 0;
|
||||
uint32_t symbol_mask = 0;
|
||||
|
||||
// symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<<fp->dl_symbols_in_S_subframe) : (1<<fp->symbols_per_slot))-1;
|
||||
do {
|
||||
recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number);
|
||||
|
||||
if (((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0))
|
||||
start_meas(&ru->rx_fhaul);
|
||||
|
||||
LOG_D(PHY,"slot %d (%d): frame %d, slot %d, symbol %d\n",
|
||||
*slot,nr_slot_select(cfg,frame_tx,*slot),frame_tx,slot_tx,symbol_number);
|
||||
|
||||
if (proc->first_tx != 0) {
|
||||
*frame = frame_tx;
|
||||
*slot = slot_tx;
|
||||
proc->first_tx = 0;
|
||||
} else {
|
||||
AssertFatal(frame_tx == *frame,
|
||||
"frame_tx %d is not what we expect %d\n",frame_tx,*frame);
|
||||
AssertFatal(slot_tx == *slot,
|
||||
"slot_tx %d is not what we expect %d\n",slot_tx,*slot);
|
||||
}
|
||||
|
||||
if (packet_type == IF4p5_PDLFFT) {
|
||||
symbol_mask = symbol_mask | (1<<symbol_number);
|
||||
} else
|
||||
AssertFatal(false, "Illegal IF4p5 packet type (should only be IF4p5_PDLFFT%d\n", packet_type);
|
||||
} while (symbol_mask != symbol_mask_full);
|
||||
|
||||
if ((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0)
|
||||
stop_meas(&ru->rx_fhaul);
|
||||
|
||||
proc->tti_tx = slot_tx;
|
||||
proc->frame_tx = frame_tx;
|
||||
|
||||
if (frame_tx == 0 && slot_tx == 0)
|
||||
proc->frame_tx_unwrap += 1024;
|
||||
|
||||
proc->timestamp_tx =
|
||||
((uint64_t)frame_tx + proc->frame_tx_unwrap) * fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, slot_tx);
|
||||
LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx);
|
||||
|
||||
if (ru->feptx_ofdm)
|
||||
ru->feptx_ofdm(ru, frame_tx, slot_tx);
|
||||
|
||||
if (ru->fh_south_out)
|
||||
ru->fh_south_out(ru, frame_tx, slot_tx, proc->timestamp_tx);
|
||||
}
|
||||
|
||||
void fh_if5_north_out(RU_t *ru)
|
||||
{
|
||||
/// **** send_IF5 of rxdata to BBU **** ///
|
||||
AssertFatal(1 == 0, "Shouldn't get here\n");
|
||||
}
|
||||
|
||||
// RRU IF4p5 northbound interface (RX)
|
||||
void fh_if4p5_north_out(RU_t *ru)
|
||||
{
|
||||
RU_proc_t *proc=&ru->proc;
|
||||
start_meas(&ru->tx_fhaul);
|
||||
send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT);
|
||||
stop_meas(&ru->tx_fhaul);
|
||||
}
|
||||
|
||||
static void rx_rf(RU_t *ru, int *frame, int *slot)
|
||||
{
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
@@ -494,7 +292,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
|
||||
return flags_gpio;
|
||||
}
|
||||
|
||||
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols)
|
||||
{
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
@@ -521,22 +319,23 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
txsymb++;
|
||||
}
|
||||
|
||||
AssertFatal(txsymb>0,"illegal txsymb %d\n",txsymb);
|
||||
AssertFatal(txsymb > 0, "illegal txsymb %d\n", txsymb);
|
||||
|
||||
if (fp->slots_per_subframe == 1) {
|
||||
if (txsymb <= 7)
|
||||
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
else
|
||||
siglen = 2 * (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 2) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
} else {
|
||||
if(slot%(fp->slots_per_subframe/2))
|
||||
siglen = txsymb * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
else
|
||||
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
if (txsymb < start_symbol) {
|
||||
// No DL symbols in this transmission
|
||||
return;
|
||||
}
|
||||
|
||||
//+ ru->end_of_burst_delay;
|
||||
flags_burst = TX_BURST_END;
|
||||
int end_symbol = start_symbol + num_symbols - 1;
|
||||
if (end_symbol >= txsymb) {
|
||||
flags_burst = TX_BURST_END;
|
||||
} else {
|
||||
flags_burst = TX_BURST_MIDDLE;
|
||||
}
|
||||
|
||||
int num_symbols_this_transmission = min(txsymb, end_symbol) - start_symbol + 1;
|
||||
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols_this_transmission);
|
||||
} else if (slot_type == NR_DOWNLINK_SLOT) {
|
||||
int prevslot_type = nr_slot_select(cfg,frame,(slot+(fp->slots_per_frame-1))%fp->slots_per_frame);
|
||||
int nextslot_type = nr_slot_select(cfg,frame,(slot+1)%fp->slots_per_frame);
|
||||
@@ -548,9 +347,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
} else {
|
||||
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
|
||||
}
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
|
||||
}
|
||||
} else { // FDD
|
||||
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
|
||||
}
|
||||
|
||||
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
|
||||
@@ -561,8 +362,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
|
||||
int nt = ru->nb_tx;
|
||||
void *txp[nt];
|
||||
uint32_t time_offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, start_symbol);
|
||||
for (int i = 0; i < nt; i++)
|
||||
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
|
||||
txp[i] = (void *)&ru->common.txdata[i][time_offset] - sf_extension * sizeof(int32_t);
|
||||
|
||||
// prepare tx buffer pointers
|
||||
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
|
||||
@@ -586,7 +388,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
|
||||
}
|
||||
|
||||
static void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
tx_rf_symbols(ru, frame, slot, timestamp, 0, 14);
|
||||
}
|
||||
|
||||
void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
{
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index
|
||||
@@ -649,7 +456,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
|
||||
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
|
||||
{
|
||||
const nfapi_nr_prach_config_t *prach_config = &config->prach_config;
|
||||
const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;
|
||||
@@ -833,7 +640,7 @@ void *ru_thread(void *param)
|
||||
ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
|
||||
AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret);
|
||||
|
||||
if (ru->ifdevice.get_internal_parameter != NULL) {
|
||||
if (ru->ifdevice.get_internal_parameter) {
|
||||
/* it seems the device can "overwrite" (request?) to set the callbacks
|
||||
* for fh_south_in()/fh_south_out() differently */
|
||||
void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in");
|
||||
@@ -842,8 +649,6 @@ void *ru_thread(void *param)
|
||||
t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out");
|
||||
if (t != NULL)
|
||||
ru->fh_south_out = t;
|
||||
} else {
|
||||
malloc_IF4p5_buffer(ru);
|
||||
}
|
||||
|
||||
int cpu = sched_getcpu();
|
||||
@@ -1088,94 +893,59 @@ void set_function_spec_param(RU_t *ru)
|
||||
switch (ru->if_south) {
|
||||
case LOCAL_RF: // this is an RU with integrated RF (RRU, gNB)
|
||||
reset_meas(&ru->rx_fhaul);
|
||||
if (ru->function == NGFI_RRU_IF5) { // IF5 RRU
|
||||
ru->do_prach = 0; // no prach processing in RU
|
||||
ru->fh_north_in = NULL; // no shynchronous incoming fronthaul from north
|
||||
ru->fh_north_out = fh_if5_north_out; // need only to do send_IF5 reception
|
||||
ru->fh_south_out = tx_rf; // send output to RF
|
||||
ru->fh_north_asynch_in = fh_if5_north_asynch_in; // TX packets come asynchronously
|
||||
ru->feprx = NULL; // nothing (this is a time-domain signal)
|
||||
ru->feptx_ofdm = NULL; // nothing (this is a time-domain signal)
|
||||
ru->feptx_prec = NULL; // nothing (this is a time-domain signal)
|
||||
ru->nr_start_if = nr_start_if; // need to start the if interface for if5
|
||||
ru->ifdevice.host_type = RRU_HOST;
|
||||
ru->rfdevice.host_type = RRU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
reset_meas(&ru->rx_fhaul);
|
||||
reset_meas(&ru->tx_fhaul);
|
||||
reset_meas(&ru->compression);
|
||||
reset_meas(&ru->transport);
|
||||
} else if (ru->function == NGFI_RRU_IF4p5) {
|
||||
ru->do_prach = 1; // do part of prach processing in RU
|
||||
ru->fh_north_in = NULL; // no synchronous incoming fronthaul from north
|
||||
ru->fh_north_out = fh_if4p5_north_out; // send_IF4p5 on reception
|
||||
ru->fh_south_out = tx_rf; // send output to RF
|
||||
ru->fh_north_asynch_in = fh_if4p5_north_asynch_in; // TX packets come asynchronously
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft only (no precoding in RRU)
|
||||
ru->feptx_prec = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start the if interface for if4p5
|
||||
ru->ifdevice.host_type = RRU_HOST;
|
||||
ru->rfdevice.host_type = RRU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
reset_meas(&ru->tx_fhaul);
|
||||
reset_meas(&ru->compression);
|
||||
reset_meas(&ru->transport);
|
||||
} else if (ru->function == gNodeB_3GPP) {
|
||||
ru->do_prach = 0; // no prach processing in RU
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
|
||||
ru->feptx_prec = NULL;
|
||||
ru->fh_north_in = NULL; // no incoming fronthaul from north
|
||||
ru->fh_north_out = NULL; // no outgoing fronthaul to north
|
||||
ru->nr_start_if = NULL; // no if interface
|
||||
ru->rfdevice.host_type = RAU_HOST;
|
||||
ru->fh_south_in = rx_rf; // local synchronous RF RX
|
||||
ru->fh_south_out = tx_rf; // local synchronous RF TX
|
||||
ru->start_rf = start_rf; // need to start the local RF interface
|
||||
ru->stop_rf = stop_rf;
|
||||
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
|
||||
}
|
||||
AssertFatal(ru->function == gNodeB_3GPP, "ru->function %d not supported for LOCAL_RF\n", ru->function);
|
||||
ru->do_prach = 0; // no prach processing in RU
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
|
||||
ru->feptx_prec = NULL;
|
||||
ru->fh_north_in = NULL; // no incoming fronthaul from north
|
||||
ru->fh_north_out = NULL; // no outgoing fronthaul to north
|
||||
ru->nr_start_if = NULL; // no if interface
|
||||
ru->rfdevice.host_type = RAU_HOST;
|
||||
ru->fh_south_in = rx_rf; // local synchronous RF RX
|
||||
ru->fh_south_out = tx_rf; // local synchronous RF TX
|
||||
ru->start_rf = start_rf; // need to start the local RF interface
|
||||
ru->stop_rf = stop_rf;
|
||||
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
|
||||
break;
|
||||
|
||||
case REMOTE_IF5: // the remote unit is IF5 RRU
|
||||
ru->do_prach = 0;
|
||||
ru->txfh_in_fep = 0;
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_prec = NULL; // need to do transmit Precoding + IDFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // need to do transmit Precoding + IDFTs
|
||||
ru->fh_south_in = fh_if5_south_in; // synchronous IF5 reception
|
||||
ru->fh_south_out = (ru->txfh_in_fep>0) ? NULL : fh_if5_south_out; // synchronous IF5 transmission
|
||||
ru->fh_south_asynch_in = NULL; // no asynchronous UL
|
||||
ru->start_rf = ru->eth_params.transp_preference == ETH_UDP_IF5_ECPRI_MODE ? start_streaming : NULL;
|
||||
ru->stop_rf = NULL;
|
||||
ru->start_write_thread = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start if interface for IF5
|
||||
ru->ifdevice.host_type = RAU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
|
||||
ru->do_prach = 0;
|
||||
ru->txfh_in_fep = 0;
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_prec = NULL; // need to do transmit Precoding + IDFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // need to do transmit Precoding + IDFTs
|
||||
ru->fh_south_in = fh_if5_south_in; // synchronous IF5 reception
|
||||
ru->fh_south_out = (ru->txfh_in_fep > 0) ? NULL : fh_if5_south_out; // synchronous IF5 transmission
|
||||
ru->fh_south_asynch_in = NULL; // no asynchronous UL
|
||||
ru->start_rf = ru->eth_params.transp_preference == ETH_UDP_IF5_ECPRI_MODE ? start_streaming : NULL;
|
||||
ru->stop_rf = NULL;
|
||||
ru->start_write_thread = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start if interface for IF5
|
||||
ru->ifdevice.host_type = RAU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
break;
|
||||
|
||||
case REMOTE_IF4p5:
|
||||
ru->do_prach = 0;
|
||||
ru->feprx = NULL; // DFTs
|
||||
ru->feptx_prec = nr_feptx_prec; // Precoding operation
|
||||
ru->feptx_ofdm = NULL; // no OFDM mod
|
||||
ru->fh_south_in = fh_if4p5_south_in; // synchronous IF4p5 reception
|
||||
ru->fh_south_out = fh_if4p5_south_out; // synchronous IF4p5 transmission
|
||||
ru->fh_south_asynch_in = (ru->if_timing == synch_to_other) ? fh_if4p5_south_in : NULL; // asynchronous UL if synch_to_other
|
||||
ru->fh_north_out = NULL;
|
||||
ru->fh_north_asynch_in = NULL;
|
||||
ru->start_rf = NULL; // no local RF
|
||||
ru->stop_rf = NULL;
|
||||
ru->start_write_thread = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start if interface for IF4p5
|
||||
ru->ifdevice.host_type = RAU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
ru->do_prach = 0;
|
||||
ru->feprx = NULL; // DFTs
|
||||
ru->feptx_prec = nr_feptx_prec; // Precoding operation
|
||||
ru->feptx_ofdm = NULL; // no OFDM mod
|
||||
ru->fh_south_in = NULL;
|
||||
ru->fh_south_out = NULL;
|
||||
ru->fh_south_asynch_in = NULL;
|
||||
ru->fh_north_out = NULL;
|
||||
ru->fh_north_asynch_in = NULL;
|
||||
ru->start_rf = NULL; // no local RF
|
||||
ru->stop_rf = NULL;
|
||||
ru->start_write_thread = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start if interface for IF4p5
|
||||
ru->ifdevice.host_type = RAU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_E(PHY,"RU with invalid or unknown southbound interface type %d\n",ru->if_south);
|
||||
LOG_E(PHY, "RU with invalid or unknown southbound interface type %d\n", ru->if_south);
|
||||
break;
|
||||
} // switch on interface type
|
||||
}
|
||||
@@ -1369,47 +1139,14 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
|
||||
ru->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset;
|
||||
|
||||
if (strcmp(*param[RU_LOCAL_RF_IDX].strptr, "yes") == 0) {
|
||||
if (!config_isparamset(param, RU_LOCAL_IF_NAME_IDX)) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = gNodeB_3GPP;
|
||||
LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j);
|
||||
} else {
|
||||
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
|
||||
ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr);
|
||||
ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr);
|
||||
ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr;
|
||||
ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr;
|
||||
ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr;
|
||||
ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr;
|
||||
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
|
||||
if (strcmp(str, "udp") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j);
|
||||
} else if (strcmp(str, "raw") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j);
|
||||
} else if (strcmp(str, "udp_if4p5") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j);
|
||||
} else if (strcmp(str, "raw_if4p5") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j);
|
||||
}
|
||||
}
|
||||
|
||||
AssertFatal(!config_isparamset(param, RU_LOCAL_IF_NAME_IDX), "RU_TRANSPORT_PREFERENCE not supported for local RF\n");
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = gNodeB_3GPP;
|
||||
LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j);
|
||||
ru->max_pdschReferenceSignalPower = *param[RU_MAX_RS_EPRE_IDX].uptr;
|
||||
ru->max_rxgain = *param[RU_MAX_RXGAIN_IDX].uptr;
|
||||
ru->sf_extension = *param[RU_SF_EXTENSION_IDX].uptr;
|
||||
} // strcmp(local_rf, "yes") == 0
|
||||
else {
|
||||
} else { // strcmp(local_rf, "yes") == 0
|
||||
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
|
||||
LOG_D(PHY, "RU %d: Transport %s\n", j, str);
|
||||
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
|
||||
@@ -1432,10 +1169,6 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
|
||||
ru->if_south = REMOTE_IF5;
|
||||
ru->function = NGFI_RAU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_MODE;
|
||||
} else if (strcmp(str, "udp_if4p5") == 0) {
|
||||
ru->if_south = REMOTE_IF4p5;
|
||||
ru->function = NGFI_RAU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
|
||||
} else if (strcmp(str, "raw_if4p5") == 0) {
|
||||
ru->if_south = REMOTE_IF4p5;
|
||||
ru->function = NGFI_RAU_IF4p5;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "assertions.h"
|
||||
|
||||
/* help strings definition for command line options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */
|
||||
|
||||
@@ -19,6 +19,11 @@ unsigned short config_frames[4] = {2,9,11,13};
|
||||
#include "openair2/E2AP/flexric/src/agent/e2_agent_api.h"
|
||||
#include "openair2/E2AP/RAN_FUNCTION/init_ran_func.h"
|
||||
#endif
|
||||
|
||||
#ifdef E3_AGENT
|
||||
#include "openair2/E3AP/e3_agent.h"
|
||||
#endif
|
||||
|
||||
#include "nr-softmodem.h"
|
||||
#include <common/utils/assertions.h>
|
||||
#include <openair2/GNB_APP/gnb_app.h>
|
||||
@@ -60,7 +65,6 @@ unsigned short config_frames[4] = {2,9,11,13};
|
||||
#include "nr-softmodem-common.h"
|
||||
#include "openair2/E1AP/e1ap_common.h"
|
||||
#include "pdcp.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "s1ap_eNB.h"
|
||||
#include "sctp_eNB_task.h"
|
||||
#include "system.h"
|
||||
@@ -521,6 +525,13 @@ int main( int argc, char **argv ) {
|
||||
|
||||
softmodem_verify_mode(get_softmodem_params());
|
||||
|
||||
//////////////////////////////////
|
||||
//// Init the E3 Agent
|
||||
#ifdef E3_AGENT
|
||||
printf("Init E3 Agent\n");
|
||||
e3_init();
|
||||
#endif // E3_AGENT
|
||||
|
||||
#if T_TRACER
|
||||
T_Config_Init();
|
||||
#endif
|
||||
@@ -727,6 +738,11 @@ int main( int argc, char **argv ) {
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
#ifdef E3_AGENT
|
||||
printf("Destroy E3 Agent\n");
|
||||
e3_destroy();
|
||||
#endif // E3_AGENT
|
||||
|
||||
free(pckg);
|
||||
logClean();
|
||||
printf("Bye.\n");
|
||||
|
||||
@@ -64,4 +64,6 @@ void init_eNB_afterRU(void);
|
||||
|
||||
void init_pdcp(void);
|
||||
|
||||
void fill_rf_config(RU_t *ru, char *rf_config_file);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -215,9 +215,8 @@ static void UE_synch(void *arg) {
|
||||
((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe)
|
||||
+ round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0);
|
||||
|
||||
if (get_nrUE_params()->cont_fo_comp) {
|
||||
UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
|
||||
} else {
|
||||
UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
|
||||
if (!get_nrUE_params()->cont_fo_comp) {
|
||||
// rerun with new cell parameters and frequency-offset
|
||||
nrue_ru_set_freq(UE, ul_carrier, dl_carrier, freq_offset);
|
||||
}
|
||||
@@ -711,6 +710,18 @@ static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp)
|
||||
return rem_samples + next_slot_first_symbol;
|
||||
}
|
||||
|
||||
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
|
||||
{
|
||||
if (abs(cfo) > TRS_CFO_THRESH) {
|
||||
LOG_A(PHY, "CFO estimated (%d) from TRS exceeded threshold (%d). Adjusting radio CF\n", cfo, TRS_CFO_THRESH);
|
||||
ue->freq_offset += cfo;
|
||||
uint64_t dl_carrier;
|
||||
uint64_t ul_carrier;
|
||||
nr_get_carrier_frequencies(ue, &dl_carrier, &ul_carrier);
|
||||
nrue_ru_set_freq(ue, ul_carrier, dl_carrier, ue->freq_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void *UE_thread(void *arg)
|
||||
{
|
||||
//this thread should be over the processing thread to keep in real time
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/if_defs.h"
|
||||
#include "openair1/PHY/MODULATION/nr_modulation.h"
|
||||
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "PHY/types.h"
|
||||
|
||||
#include "PHY/defs_common.h"
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/ethernet_lib.h"
|
||||
|
||||
#include "PHY/if4_tools.h"
|
||||
@@ -562,7 +561,8 @@ void* ru_thread_control( void* param )
|
||||
|
||||
//if (ru->is_slave == 1) lte_sync_time_init(&ru->frame_parms);
|
||||
|
||||
if (ru->rfdevice.is_init != 1) openair0_device_load(&ru->rfdevice,&ru->openair0_cfg);
|
||||
int ret = openair0_device_load(&ru->rfdevice, &ru->openair0_cfg);
|
||||
AssertFatal(ret == 0, "could not load device library\n");
|
||||
|
||||
if (ru->rfdevice.trx_config_func) AssertFatal((ru->rfdevice.trx_config_func(&ru->rfdevice,&ru->openair0_cfg)==0),
|
||||
"Failed to configure RF device for RU %d\n",ru->idx);
|
||||
|
||||
@@ -18,7 +18,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
void fh_timer_tick(fh_timer_t *timer)
|
||||
@@ -31,8 +31,17 @@ void fh_timer_tick(fh_timer_t *timer)
|
||||
typedef __int128_t int128;
|
||||
uint64_t total_syms_per_ms = 14 << timer->numerology;
|
||||
|
||||
// Boundary(ies) crossed
|
||||
while (now >= timer->target_gps_ns && timer->running) {
|
||||
// 1. Calculate how many symbols *should* have elapsed up to 'now'
|
||||
// This catches up if the clock jumped forward, or processes at least 1 symbol.
|
||||
uint64_t expected_s_abs = (uint64_t)((int128)now * total_syms_per_ms / NS_PER_MS);
|
||||
|
||||
// PTP sync backwards adjustment - don't run callbacks until we catch up to the previously expected symbol index
|
||||
if (expected_s_abs < timer->next_s_abs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Drive the loop using the absolute symbol counter, not the volatile 'now' time
|
||||
while (timer->next_s_abs <= expected_s_abs && timer->running) {
|
||||
uint64_t s_abs = timer->next_s_abs;
|
||||
rte_atomic64_set(&timer->s_abs, s_abs);
|
||||
|
||||
@@ -43,8 +52,10 @@ void fh_timer_tick(fh_timer_t *timer)
|
||||
|
||||
// Prepare for next symbol
|
||||
timer->next_s_abs++;
|
||||
timer->target_gps_ns = (uint64_t)((int128)timer->next_s_abs * NS_PER_MS / total_syms_per_ms);
|
||||
}
|
||||
|
||||
// 3. Recalculate the next target timestamp based purely on the new symbol index
|
||||
timer->target_gps_ns = (uint64_t)((int128)timer->next_s_abs * NS_PER_MS / total_syms_per_ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ typedef void (*fh_timer_cb)(uint64_t s_abs, void *user_data);
|
||||
|
||||
#define MAX_FH_TIMER_CBS 8
|
||||
#define GPS_EPOCH_OFFSET_UNIX 315964800ULL
|
||||
#define GPS_LEAP_SECONDS 18
|
||||
|
||||
typedef struct {
|
||||
fh_timer_cb fn;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
set(DPDK_TEST_ARGS "--no-huge" "--iova-mode=va" "--no-pci")
|
||||
|
||||
add_executable(test_fh_timer test_fh_timer.c)
|
||||
@@ -7,6 +9,13 @@ add_dependencies(tests test_fh_timer)
|
||||
add_test(NAME test_fh_timer COMMAND test_fh_timer ${DPDK_TEST_ARGS} "--file-prefix=test_fh_timer")
|
||||
set_property(TEST test_fh_timer PROPERTY LABELS fronthaul)
|
||||
|
||||
add_executable(test_fh_timer_jumps test_fh_timer_jumps.c)
|
||||
target_link_libraries(test_fh_timer_jumps PRIVATE fh_timer ${dpdk_LIBRARIES} "-Wl,--wrap=clock_gettime")
|
||||
target_include_directories(test_fh_timer_jumps PRIVATE ${dpdk_INCLUDE_DIRS})
|
||||
add_dependencies(tests test_fh_timer_jumps)
|
||||
add_test(NAME test_fh_timer_jumps COMMAND test_fh_timer_jumps ${DPDK_TEST_ARGS} "--file-prefix=test_fh_timer_jumps")
|
||||
set_property(TEST test_fh_timer_jumps PROPERTY LABELS fronthaul)
|
||||
|
||||
add_executable(test_fh_timer_drift test_fh_timer_drift.c)
|
||||
target_link_libraries(test_fh_timer_drift PRIVATE fh_timer ${dpdk_LIBRARIES} m)
|
||||
target_include_directories(test_fh_timer_drift PRIVATE ${dpdk_INCLUDE_DIRS})
|
||||
|
||||
@@ -25,7 +25,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -20,7 +20,7 @@ static uint64_t get_gps_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX) * NS_PER_SEC + ts.tv_nsec;
|
||||
return ((uint64_t)ts.tv_sec - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
179
fronthaul/core/tests/test_fh_timer_jumps.c
Normal file
179
fronthaul/core/tests/test_fh_timer_jumps.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "fh_timer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define GPS_EPOCH_OFFSET_UNIX 315964800ULL
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
|
||||
// Mock clock state
|
||||
static struct timespec mock_time = {0, 0};
|
||||
|
||||
// Mock implementation of clock_gettime
|
||||
int __wrap_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
if (clk_id == CLOCK_REALTIME) {
|
||||
*tp = mock_time;
|
||||
return 0;
|
||||
}
|
||||
extern int __real_clock_gettime(clockid_t clk_id, struct timespec * tp);
|
||||
return __real_clock_gettime(clk_id, tp);
|
||||
}
|
||||
|
||||
static struct timespec gps_ns_to_timespec(uint64_t gps_ns)
|
||||
{
|
||||
struct timespec ts;
|
||||
uint64_t unix_ns = gps_ns + (GPS_EPOCH_OFFSET_UNIX - GPS_LEAP_SECONDS) * NS_PER_SEC;
|
||||
ts.tv_sec = unix_ns / NS_PER_SEC;
|
||||
ts.tv_nsec = unix_ns % NS_PER_SEC;
|
||||
return ts;
|
||||
}
|
||||
|
||||
#define MAX_CALLBACKS 100
|
||||
typedef struct {
|
||||
uint64_t symbols[MAX_CALLBACKS];
|
||||
int count;
|
||||
} cb_tracker_t;
|
||||
|
||||
static void test_cb(uint64_t s_abs, void *user_data)
|
||||
{
|
||||
cb_tracker_t *tracker = (cb_tracker_t *)user_data;
|
||||
if (tracker->count < MAX_CALLBACKS) {
|
||||
tracker->symbols[tracker->count++] = s_abs;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// 1. Initialize timer under mock clock
|
||||
// Set mock time to UNIX 1718000000 (June 2024)
|
||||
mock_time.tv_sec = 1718000000;
|
||||
mock_time.tv_nsec = 0;
|
||||
|
||||
fh_timer_t timer;
|
||||
int numerology = 1; // 30kHz
|
||||
if (fh_timer_init(&timer, numerology) < 0) {
|
||||
fprintf(stderr, "Timer init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
cb_tracker_t tracker = {0};
|
||||
if (fh_timer_register_cb(&timer, test_cb, &tracker) < 0) {
|
||||
fprintf(stderr, "Callback registration failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify initial state
|
||||
// Expected target is 35714 ns past the initial time (Unix 1718000000s)
|
||||
printf("Initial symbol abs: %lu, expected next_s_abs: %lu\n", rte_atomic64_read(&timer.s_abs), timer.next_s_abs);
|
||||
assert(timer.next_s_abs == rte_atomic64_read(&timer.s_abs) + 1);
|
||||
|
||||
// --- Test Case 1: No tick if target not reached ---
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callback triggered before target time\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 1 passed: No premature callback.\n");
|
||||
|
||||
// --- Test Case 2: Normal Tick when target is reached ---
|
||||
// Advance mock time by 36000 ns (slightly more than 35714 ns symbol duration)
|
||||
mock_time.tv_nsec = 36000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 1) {
|
||||
fprintf(stderr, "FAIL: callback not triggered when target reached (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
if (tracker.symbols[0] != rte_atomic64_read(&timer.s_abs)) {
|
||||
fprintf(stderr,
|
||||
"FAIL: callback symbol %lu does not match current timer symbol %lu\n",
|
||||
tracker.symbols[0],
|
||||
rte_atomic64_read(&timer.s_abs));
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 2 passed: Normal tick called successfully.\n");
|
||||
|
||||
// --- Test Case 3: Clock Jump Forward (Catching Up) ---
|
||||
// Reset tracker
|
||||
tracker.count = 0;
|
||||
// Jump clock forward by 250,000 ns (roughly 7 symbol durations)
|
||||
mock_time.tv_nsec = 260000;
|
||||
fh_timer_tick(&timer);
|
||||
// We expected it to catch up by running callbacks for all missed symbols
|
||||
// Let's verify the number of symbols it executed.
|
||||
printf("After forward jump, processed %d callbacks.\n", tracker.count);
|
||||
if (tracker.count < 5) {
|
||||
fprintf(stderr, "FAIL: forward jump did not trigger catch up callbacks (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
// Let's verify the sequence of symbols is contiguous
|
||||
for (int i = 0; i < tracker.count; i++) {
|
||||
printf("Callback %d: Symbol %lu\n", i, tracker.symbols[i]);
|
||||
if (i > 0 && tracker.symbols[i] != tracker.symbols[i - 1] + 1) {
|
||||
fprintf(stderr, "FAIL: non-contiguous symbol sequence in catch up\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("Test Case 3 passed: Forward jump catch up works.\n");
|
||||
|
||||
// --- Test Case 4: Clock Jump Backward (PTP Sync Backward) ---
|
||||
// Reset tracker
|
||||
tracker.count = 0;
|
||||
// Get current state
|
||||
uint64_t pre_jump_next = timer.next_s_abs;
|
||||
uint64_t pre_jump_target = timer.target_gps_ns;
|
||||
printf("Before backward jump: next_s_abs=%lu, target_gps_ns=%lu\n", pre_jump_next, pre_jump_target);
|
||||
|
||||
// Jump mock time backward to tv_nsec = 100000
|
||||
mock_time.tv_nsec = 100000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callbacks executed during backward clock jump (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
// Move time forward a bit but still below target
|
||||
mock_time.tv_nsec = 200000;
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count != 0) {
|
||||
fprintf(stderr, "FAIL: callbacks executed before catching up to previous state (count=%d)\n", tracker.count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now move time past the old target
|
||||
mock_time = gps_ns_to_timespec(pre_jump_target + 1000);
|
||||
|
||||
printf("Setting mock time past old target: tv_sec=%ld, tv_nsec=%ld\n", mock_time.tv_sec, mock_time.tv_nsec);
|
||||
fh_timer_tick(&timer);
|
||||
if (tracker.count == 0) {
|
||||
fprintf(stderr, "FAIL: timer did not resume after clock caught up to previous target\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Resumed successfully with symbol %lu\n", tracker.symbols[0]);
|
||||
if (tracker.symbols[0] != pre_jump_next) {
|
||||
fprintf(stderr, "FAIL: resumed symbol %lu does not match expected %lu\n", tracker.symbols[0], pre_jump_next);
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 4 passed: Backward jump (PTP sync backward) handling works\n");
|
||||
|
||||
// --- Test Case 5: fh_timer_get_current_symbol ---
|
||||
mock_time.tv_sec = 1718000000;
|
||||
mock_time.tv_nsec = 500000; // 0.5 ms
|
||||
uint64_t cur_sym = fh_timer_get_current_symbol(&timer);
|
||||
typedef __int128_t int128;
|
||||
uint64_t expected_sym =
|
||||
(uint64_t)((((int128)1718000000 - GPS_EPOCH_OFFSET_UNIX + GPS_LEAP_SECONDS) * NS_PER_SEC + 500000) * 28 / 1000000);
|
||||
if (cur_sym != expected_sym) {
|
||||
fprintf(stderr, "FAIL: fh_timer_get_current_symbol returned %lu, expected %lu\n", cur_sym, expected_sym);
|
||||
return 1;
|
||||
}
|
||||
printf("Test Case 5 passed: fh_timer_get_current_symbol works.\n");
|
||||
|
||||
printf("ALL TESTS PASSED SUCCESSFULLY!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -223,12 +223,12 @@ void oru_fh_cleanup(void *handle)
|
||||
free(fh);
|
||||
}
|
||||
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
if (!handle)
|
||||
return -1;
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, frame, slot, symbol);
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, hyper_frame, frame, slot, symbol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ int oru_fh_get_ready_jobs(void *handle)
|
||||
return get_ready_job_count(fh->packet_processor);
|
||||
}
|
||||
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
{
|
||||
if (!handle || !frame || !slot || !ts)
|
||||
return -1;
|
||||
@@ -249,13 +249,14 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, s
|
||||
absolute_gps_symbol -= absolute_gps_symbol % NR_SYMBOLS_PER_SLOT; // Round down to start of current slot
|
||||
uint64_t absolute_slot = absolute_gps_symbol / NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t slots_per_frame = 10 << fh->cfg.numerology;
|
||||
*hyper_frame = (absolute_slot / slots_per_frame) / 1024;
|
||||
*frame = (absolute_slot / slots_per_frame) % 1024;
|
||||
*slot = absolute_slot % slots_per_frame;
|
||||
|
||||
uint64_t total_syms_per_sec = (NR_SYMBOLS_PER_SLOT * 1000) << fh->cfg.numerology;
|
||||
uint64_t ns_per_symbol = 1000000000 / total_syms_per_sec;
|
||||
uint64_t leftover_syms = absolute_gps_symbol % total_syms_per_sec;
|
||||
ts->tv_sec = (absolute_gps_symbol / total_syms_per_sec) + GPS_EPOCH_OFFSET_UNIX;
|
||||
ts->tv_sec = (absolute_gps_symbol / total_syms_per_sec) + GPS_EPOCH_OFFSET_UNIX - GPS_LEAP_SECONDS;
|
||||
ts->tv_nsec = leftover_syms * ns_per_symbol;
|
||||
return 0;
|
||||
}
|
||||
@@ -267,11 +268,17 @@ void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame,
|
||||
write_prach_iq(fh->packet_processor, prachF, nb_rx, frame, slot, symbol);
|
||||
}
|
||||
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol)
|
||||
int oru_fh_poll_ul_job(void *handle, ul_job_t *job) {
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
AssertFatal(fh, "Invalid handle\n");
|
||||
return poll_ul_job(fh->packet_processor, job);
|
||||
}
|
||||
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol_index, const ul_job_t *job)
|
||||
{
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
AssertFatal(fh, "Invalid handle\n");
|
||||
write_ul_iq(fh->packet_processor, puschF, nb_rx, frame, slot, symbol);
|
||||
write_ul_iq(fh->packet_processor, puschF, symbol_index, job);
|
||||
}
|
||||
|
||||
int oru_fh_start(void *handle)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "oru_io.h"
|
||||
#include <stdint.h>
|
||||
#include "oru_packet_processor.h"
|
||||
|
||||
typedef struct {
|
||||
char *dpdk_devices[MAX_RU_PORTS];
|
||||
@@ -74,23 +75,25 @@ int oru_fh_get_ready_jobs(void *handle);
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param txdataF Array of pointers to buffers to store the received frequency-domain IQ samples (per TX antenna).
|
||||
* @param nb_tx Number of TX antennas.
|
||||
* @param hyper_frame Absolute GPS hyper-frame number
|
||||
* @param frame Pointer to store the frame number of the read symbol.
|
||||
* @param slot Pointer to store the slot number of the read symbol.
|
||||
* @param symbol Pointer to store the symbol number.
|
||||
* @return 0 on success or -1 on failure
|
||||
*/
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
|
||||
/**
|
||||
* @brief Get the UTC anchor point mapping between 5G time and system time.
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param frame Pointer to store the reference frame number.
|
||||
* @param hyper_frame Pointer to store the reference hyperframe number (1024 frames each)
|
||||
* @param slot Pointer to store the reference slot number.
|
||||
* @param ts Pointer to a timespec structure to store the corresponding system time.
|
||||
* @return 0 on success, negative on error.
|
||||
*/
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
|
||||
/**
|
||||
* @brief Send PRACH symbol data (U-Plane) over the Fronthaul interface.
|
||||
@@ -104,17 +107,25 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, s
|
||||
*/
|
||||
void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame, int slot, int symbol);
|
||||
|
||||
/**
|
||||
* @brief Poll for pending UL jobs
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param job Job descriptor
|
||||
*
|
||||
* @returns 0 if successful, -1 otherwise
|
||||
*/
|
||||
int oru_fh_poll_ul_job(void *handle, ul_job_t *job);
|
||||
|
||||
/**
|
||||
* @brief Send PUSCH symbol data (U-Plane) over the Fronthaul interface.
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param puschF Array of pointers to buffers containing the frequency-domain PUSCH IQ samples.
|
||||
* @param nb_rx Number of RX antennas.
|
||||
* @param frame Target frame number.
|
||||
* @param slot Target slot number.
|
||||
* @param symbol Target symbol number.
|
||||
* @param symbol Absolute symbol index within slot (0–13); must be within the job's symbol range
|
||||
* @param job Job descriptor
|
||||
*/
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol);
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol, const ul_job_t *job);
|
||||
|
||||
/**
|
||||
* @brief Start the O-RU Fronthaul processing threads and loops.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "common/platform_types.h"
|
||||
#include "xran_pkt_api.h"
|
||||
#include "oru_packet_processor.h"
|
||||
@@ -5,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "assertions.h"
|
||||
#include "log.h"
|
||||
#include <rte_ring.h>
|
||||
@@ -12,8 +17,21 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define PRACH_ERR_LOG_RATELIMIT 10000
|
||||
|
||||
#define RATELIMIT(n, block) \
|
||||
do { \
|
||||
static _Atomic unsigned long counter = 0; \
|
||||
unsigned long current = atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed); \
|
||||
if (current % (n) == 0) { \
|
||||
block \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DL_JOB_RING_SIZE 128
|
||||
#define UL_JOB_RING_SIZE 128
|
||||
#define MAX_CONCURRENT_DL_JOBS (DL_JOB_RING_SIZE - 1)
|
||||
#define NUM_CONCURRENT_DL_SYMBOL_WINDOWS MAX_CONCURRENT_DL_JOBS
|
||||
#define NUM_CONCURRENT_UL_SYMBOL_WINDOWS 128
|
||||
@@ -24,13 +42,7 @@
|
||||
#define SYMBOL_BITMASK_SIZE ((NR_SYMBOLS_PER_SLOT * MAX_TDD_PATTERN_LENGTH_MS * MAX_SLOTS_PER_MS + 7) / 8)
|
||||
#define MAX_RX_FRAGMENTS 4
|
||||
#define MAX_MBUFS_PER_SYMBOL 64
|
||||
|
||||
typedef enum {
|
||||
SYM_UL_IDLE,
|
||||
SYM_UL_ACTIVE,
|
||||
SYM_UL_READY,
|
||||
SYM_UL_EMPTY,
|
||||
} ul_symbol_job_state_t;
|
||||
#define MAX_SLOTS_PER_FRAME 160
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
@@ -50,43 +62,43 @@ typedef struct {
|
||||
} dl_symbol_job_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
bool cplane_received;
|
||||
int section_id;
|
||||
int num_prb;
|
||||
int start_prb;
|
||||
} per_antenna[MAX_ANTENNAS];
|
||||
uint64_t absolute_symbol;
|
||||
ul_symbol_job_state_t state;
|
||||
} ul_symbol_job_t;
|
||||
typedef struct {
|
||||
struct {
|
||||
bool cplane_received;
|
||||
int section_id;
|
||||
int num_prb;
|
||||
int start_prb;
|
||||
int filter_id;
|
||||
} per_antenna[MAX_ANTENNAS];
|
||||
uint64_t absolute_symbol;
|
||||
} prach_symbol_job_t;
|
||||
bool active;
|
||||
uint64_t start_absolute_symbol;
|
||||
uint32_t num_symbols;
|
||||
int section_id;
|
||||
int num_prb;
|
||||
int start_prb;
|
||||
int filter_id;
|
||||
} prach_job_t;
|
||||
typedef struct {
|
||||
_Atomic(uint64_t) dl_tdd_mismatch;
|
||||
_Atomic(uint64_t) ul_tdd_mismatch;
|
||||
_Atomic(uint64_t) ul_cplane_missing;
|
||||
_Atomic(uint64_t) prach_cplane_missing;
|
||||
_Atomic(uint64_t) prach_cplane_missing_ant;
|
||||
_Atomic(uint64_t) prach_cplane_missing_inactive;
|
||||
_Atomic(uint64_t) prach_cplane_missing_stale;
|
||||
_Atomic(uint64_t) prach_cplane_missing_early;
|
||||
_Atomic(uint64_t) prach_out_of_mbufs;
|
||||
_Atomic(uint64_t) prach_jobs_pool_exhausted;
|
||||
_Atomic(uint64_t) out_of_mbufs;
|
||||
_Atomic(uint64_t) total_uplane_sent;
|
||||
_Atomic(int64_t) ul_uplane_ota_delay_sum;
|
||||
_Atomic(uint64_t) ul_uplane_ota_delay_count;
|
||||
} thread_safe_stats_t;
|
||||
|
||||
typedef struct {
|
||||
dl_symbol_job_t dl_symbol_jobs[MAX_CONCURRENT_DL_JOBS];
|
||||
dl_symbol_job_t *dl_symbol_rx_window[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
|
||||
bool was_dl_symbol_completed[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
|
||||
ul_symbol_job_t ul_symbol_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
prach_symbol_job_t prach_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
prach_job_t prach_jobs[MAX_SLOTS_PER_FRAME][MAX_ANTENNAS];
|
||||
uint64_t current_absolute_symbol;
|
||||
uint64_t last_pushed_symbol;
|
||||
struct rte_ring *dl_free_jobs;
|
||||
struct rte_ring *dl_ready_jobs;
|
||||
struct rte_ring *ul_free_jobs;
|
||||
struct rte_ring *ul_ready_jobs;
|
||||
ul_job_t ul_jobs_pool[UL_JOB_RING_SIZE];
|
||||
uint32_t T2a_min_cp_sym_diff;
|
||||
uint32_t T2a_max_cp_sym_diff;
|
||||
uint32_t T2a_min_up_dl_sym_diff;
|
||||
@@ -165,6 +177,13 @@ void *init_packet_processor(int numerology,
|
||||
for (int i = 0; i < MAX_CONCURRENT_DL_JOBS; i++) {
|
||||
rte_ring_enqueue(ctx->dl_free_jobs, (void *)&ctx->dl_symbol_jobs[i]);
|
||||
}
|
||||
ctx->ul_ready_jobs = rte_ring_create("ul_ready_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
|
||||
AssertFatal(ctx->ul_ready_jobs != NULL, "Failed to create ring ul_ready_jobs\n");
|
||||
ctx->ul_free_jobs = rte_ring_create("ul_free_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
|
||||
AssertFatal(ctx->ul_free_jobs != NULL, "Failed to create ring ul_free_jobs\n");
|
||||
for (int i = 0; i < UL_JOB_RING_SIZE - 1; i++) {
|
||||
rte_ring_enqueue(ctx->ul_free_jobs, (void *)&ctx->ul_jobs_pool[i]);
|
||||
}
|
||||
|
||||
ctx->eaxcid_config = (struct xran_eaxcid_config){.mask_cuPortId = 0xF000,
|
||||
.mask_bandSectorId = 0x0F00,
|
||||
@@ -197,6 +216,12 @@ void cleanup_packet_processor(void *context)
|
||||
if (ctx->dl_free_jobs) {
|
||||
rte_ring_free(ctx->dl_free_jobs);
|
||||
}
|
||||
if (ctx->ul_ready_jobs) {
|
||||
rte_ring_free(ctx->ul_ready_jobs);
|
||||
}
|
||||
if (ctx->ul_free_jobs) {
|
||||
rte_ring_free(ctx->ul_free_jobs);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
@@ -461,6 +486,7 @@ static void handle_dl_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
}
|
||||
if (job->per_antenna[ant_id].cplane_received) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
ctx->stats.cplane_err_dup_dl++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -503,30 +529,25 @@ static void handle_ul_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
ctx->stats.ul_tdd_mismatch++;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < num_symbols; i++) {
|
||||
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[job_index];
|
||||
if (job->state == SYM_UL_IDLE) {
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
job->state = SYM_UL_ACTIVE;
|
||||
for (int j = 0; j < MAX_ANTENNAS; j++) {
|
||||
job->per_antenna[j].cplane_received = false;
|
||||
}
|
||||
} else if (job->state == SYM_UL_READY) {
|
||||
ctx->stats.application_too_slow++;
|
||||
return;
|
||||
}
|
||||
if (job->per_antenna[ant_id].cplane_received) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
return;
|
||||
}
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
if (!job->per_antenna[ant_id].cplane_received) {
|
||||
job->per_antenna[ant_id].cplane_received = true;
|
||||
job->per_antenna[ant_id].section_id = section->hdr.u1.common.sectionId;
|
||||
job->per_antenna[ant_id].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->per_antenna[ant_id].start_prb = section->hdr.u1.common.startPrbc;
|
||||
}
|
||||
ul_job_t *ul_job = NULL;
|
||||
if (rte_ring_dequeue(ctx->ul_free_jobs, (void **)&ul_job) == 0) {
|
||||
memset(ul_job, 0, sizeof(*ul_job));
|
||||
ul_job->response_payload.section_id = section->hdr.u1.common.sectionId;
|
||||
ul_job->response_payload.comp_method = hdr->udComp.udCompMeth;
|
||||
ul_job->response_payload.iq_width = hdr->udComp.udIqWidth == 0 ? 16 : hdr->udComp.udIqWidth;
|
||||
uint64_t absolute_gps_symbol = target_absolute_symbol;
|
||||
ul_job->hyper_frame = absolute_gps_symbol / (1024 * (10 * (1 << ctx->numerology) * 14));
|
||||
ul_job->frame = (absolute_gps_symbol / (10 * (1 << ctx->numerology) * 14)) % 1024;
|
||||
ul_job->slot_in_frame = (absolute_gps_symbol % (10 * (1 << ctx->numerology) * 14)) / 14;
|
||||
ul_job->symbol = absolute_gps_symbol % 14;
|
||||
ul_job->num_symbols = num_symbols;
|
||||
ul_job->antenna_id = ant_id;
|
||||
ul_job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
ul_job->start_prb = section->hdr.u1.common.startPrbc;
|
||||
int ret = rte_ring_enqueue(ctx->ul_ready_jobs, (void *)ul_job);
|
||||
AssertFatal(ret == 0, "Failed to enqueue ul_job to ul_ready_jobs ring\n");
|
||||
} else {
|
||||
ctx->stats.application_too_slow++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,6 +558,8 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
{
|
||||
if (hdr->cmnhdr.numOfSections != 1) {
|
||||
ctx->stats.cplane_err_hdr++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH CP: Invalid numOfSections %d (expected 1)\n", hdr->cmnhdr.numOfSections); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -546,11 +569,14 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
struct xran_cp_radioapp_section3 *section = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_cp_radioapp_section3_header));
|
||||
if (section == NULL) {
|
||||
ctx->stats.cplane_err_hdr++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Failed to adjust mbuf for section3 header\n"); });
|
||||
return;
|
||||
}
|
||||
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
|
||||
int aarx = ant_id - ctx->prach_eaxc_offset;
|
||||
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH CP: Invalid aarx %d (ant_id %d, eaxc_offset %d)\n", aarx, ant_id, ctx->prach_eaxc_offset); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -570,26 +596,34 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
txrx_window_histogram_count(&ctx->stats.prach_cplane_hist, diff);
|
||||
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
|
||||
|
||||
for (int i = 0; i < num_symbols; i++) {
|
||||
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
|
||||
|
||||
if (job->absolute_symbol != target_absolute_symbol + i) {
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
for (int j = 0; j < MAX_ANTENNAS; j++) {
|
||||
job->per_antenna[j].cplane_received = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (job->per_antenna[aarx].cplane_received) {
|
||||
continue;
|
||||
}
|
||||
job->per_antenna[aarx].cplane_received = true;
|
||||
job->per_antenna[aarx].section_id = section->hdr.u1.common.sectionId;
|
||||
job->per_antenna[aarx].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->per_antenna[aarx].start_prb = section->hdr.u1.common.startPrbc;
|
||||
job->per_antenna[aarx].filter_id = hdr->cmnhdr.field.filterIndex;
|
||||
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Invalid slot_in_frame %d\n", slot_in_frame); });
|
||||
return;
|
||||
}
|
||||
|
||||
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
|
||||
if (job->active && job->start_absolute_symbol == target_absolute_symbol) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
ctx->stats.cplane_err_dup_prach++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW, "PRACH CP: Duplicate packet for slot %d, aarx %d, start_symbol %lu\n", slot_in_frame, aarx, target_absolute_symbol);
|
||||
});
|
||||
return;
|
||||
}
|
||||
job->active = true;
|
||||
job->start_absolute_symbol = target_absolute_symbol;
|
||||
job->num_symbols = num_symbols;
|
||||
job->section_id = section->hdr.u1.common.sectionId;
|
||||
job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->start_prb = section->hdr.u1.common.startPrbc;
|
||||
job->filter_id = hdr->cmnhdr.field.filterIndex;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_A(HW,
|
||||
"PRACH JOB added slot_in_frame %d, aarx %d target_absolute_symbol %lu\n",
|
||||
slot_in_frame,
|
||||
aarx,
|
||||
target_absolute_symbol);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_cplane_packet(void *context, void *pkt)
|
||||
@@ -629,20 +663,24 @@ void handle_cplane_packet(void *context, void *pkt)
|
||||
}
|
||||
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
|
||||
if (hdr->cmnhdr.field.dataDirection == XRAN_DIR_DL) {
|
||||
ctx->stats.cplane_received_dl++;
|
||||
handle_dl_cplane_packet(ctx, pkt, hdr, section, ant_id);
|
||||
} else {
|
||||
ctx->stats.cplane_received_ul++;
|
||||
handle_ul_cplane_packet(ctx, pkt, hdr, section, ant_id);
|
||||
}
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
case XRAN_CP_SECTIONTYPE_3: {
|
||||
ctx->stats.cplane_received_prach++;
|
||||
struct xran_cp_radioapp_section3_header *hdr = (struct xran_cp_radioapp_section3_header *)apphdr;
|
||||
handle_prach_cplane_packet(ctx, pkt, hdr, ant_id);
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
ctx->stats.cplane_received_other++;
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
@@ -652,19 +690,38 @@ static void print_histogram(const char *name, txrx_histogram_t *hist, uint32_t w
|
||||
{
|
||||
if (hist->count == 0)
|
||||
return;
|
||||
printf(" %s (mean: %.2f symbols) window start: %u, end %u:\n", name, (double)hist->sum / hist->count, window_start, window_end);
|
||||
char buf[4096];
|
||||
int len = snprintf(buf,
|
||||
sizeof(buf),
|
||||
" %s (mean: %.2f symbols) window [%u, %u]:",
|
||||
name,
|
||||
(double)hist->sum / hist->count,
|
||||
window_start,
|
||||
window_end);
|
||||
bool first = true;
|
||||
for (int i = 0; i < HIST_SIZE; i++) {
|
||||
if (hist->hist[i] > 0) {
|
||||
int bucket = i - HIST_SIZE / 2;
|
||||
char bin_str[64];
|
||||
int bin_len = 0;
|
||||
if (i == 0) {
|
||||
printf(" <= %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s<=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
} else if (i == HIST_SIZE - 1) {
|
||||
printf(" >= %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s>=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
} else {
|
||||
printf(" %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
}
|
||||
first = false;
|
||||
if (len + bin_len < sizeof(buf)) {
|
||||
strcpy(buf + len, bin_str);
|
||||
len += bin_len;
|
||||
} else {
|
||||
break; // buffer full
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_I(HW, "%s\n", buf);
|
||||
memset(hist, 0, sizeof(*hist));
|
||||
}
|
||||
|
||||
void print_packet_processor_stats(void *context)
|
||||
@@ -673,39 +730,73 @@ void print_packet_processor_stats(void *context)
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
printf("ORU Packet Processor Stats:\n");
|
||||
printf(" Total C-Plane Packets received: %lu\n", ctx->stats.total_cplane);
|
||||
printf(" Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
|
||||
printf(" Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
|
||||
LOG_I(HW, "ORU Packet Processor Stats:\n");
|
||||
LOG_I(HW,
|
||||
" Total C-Plane Packets received: %lu (DL: %lu, UL: %lu, PRACH: %lu, Other: %lu)\n",
|
||||
ctx->stats.total_cplane,
|
||||
ctx->stats.cplane_received_dl,
|
||||
ctx->stats.cplane_received_ul,
|
||||
ctx->stats.cplane_received_prach,
|
||||
ctx->stats.cplane_received_other);
|
||||
LOG_I(HW, " Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
|
||||
LOG_I(HW, " Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
|
||||
if (ctx->thread_safe_stats.ul_uplane_ota_delay_count > 0)
|
||||
LOG_I(HW,
|
||||
" UL U-Plane OTA delay (mean symbols): %.2f (%lu packets)\n",
|
||||
(double)(int64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_sum
|
||||
/ (double)ctx->thread_safe_stats.ul_uplane_ota_delay_count,
|
||||
(uint64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_count);
|
||||
|
||||
if (ctx->stats.cplane_err_hdr > 0)
|
||||
printf(" C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
|
||||
LOG_I(HW, " C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
|
||||
if (ctx->stats.cplane_err_ver > 0)
|
||||
printf(" C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
|
||||
LOG_I(HW, " C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
|
||||
if (ctx->stats.cplane_err_early > 0)
|
||||
printf(" C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
|
||||
LOG_I(HW, " C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
|
||||
if (ctx->stats.cplane_err_late > 0)
|
||||
printf(" C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
|
||||
LOG_I(HW, " C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
|
||||
if (ctx->stats.cplane_err_dup > 0)
|
||||
printf(" C-Plane Duplicate Packet Errors: %lu\n", ctx->stats.cplane_err_dup);
|
||||
LOG_I(HW,
|
||||
" C-Plane Duplicate Packet Errors: %lu (DL: %lu, UL: %lu, PRACH: %lu)\n",
|
||||
ctx->stats.cplane_err_dup,
|
||||
ctx->stats.cplane_err_dup_dl,
|
||||
ctx->stats.cplane_err_dup_ul,
|
||||
ctx->stats.cplane_err_dup_prach);
|
||||
if (ctx->stats.uplane_err_early > 0)
|
||||
printf(" U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
|
||||
LOG_I(HW, " U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
|
||||
if (ctx->stats.uplane_err_late > 0)
|
||||
printf(" U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
|
||||
LOG_I(HW, " U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
|
||||
if (ctx->stats.uplane_err_dup > 0)
|
||||
printf(" U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
|
||||
LOG_I(HW, " U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
|
||||
if (ctx->stats.uplane_missing_cplane > 0)
|
||||
printf(" U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
|
||||
LOG_I(HW, " U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
|
||||
if (ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch > 0)
|
||||
printf(" DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
|
||||
LOG_I(HW, " DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
|
||||
if (ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch > 0)
|
||||
printf(" UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
|
||||
LOG_I(HW, " UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
|
||||
if (ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing > 0)
|
||||
printf(" UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
|
||||
LOG_I(HW, " UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
|
||||
if (ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH C-Plane Missing Errors: %lu (Never Received: %lu, Stale: %lu, Early: %lu)\n",
|
||||
ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing,
|
||||
ctx->stats.prach_cplane_missing_inactive + ctx->thread_safe_stats.prach_cplane_missing_inactive,
|
||||
ctx->stats.prach_cplane_missing_stale + ctx->thread_safe_stats.prach_cplane_missing_stale,
|
||||
ctx->stats.prach_cplane_missing_early + ctx->thread_safe_stats.prach_cplane_missing_early);
|
||||
if (ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH Ant C-Plane Missing Errors: %lu\n",
|
||||
ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant);
|
||||
if (ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs > 0)
|
||||
LOG_I(HW, " PRACH Out Of Mbufs Errors: %lu\n", ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs);
|
||||
if (ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH Jobs Pool Exhausted Errors: %lu\n",
|
||||
ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted);
|
||||
if (ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs > 0)
|
||||
printf(" Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
|
||||
LOG_I(HW, " Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
|
||||
if (ctx->stats.application_too_slow > 0)
|
||||
printf(" Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
|
||||
LOG_I(HW, " Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
|
||||
|
||||
print_histogram("DL C-Plane", &ctx->stats.dl_cplane_hist, ctx->T2a_max_cp_sym_diff, ctx->T2a_min_cp_sym_diff);
|
||||
print_histogram("DL U-Plane", &ctx->stats.dl_uplane_hist, ctx->T2a_max_up_dl_sym_diff, ctx->T2a_min_up_dl_sym_diff);
|
||||
@@ -721,8 +812,17 @@ void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out
|
||||
out_stats->dl_tdd_mismatch += ctx->thread_safe_stats.dl_tdd_mismatch;
|
||||
out_stats->ul_tdd_mismatch += ctx->thread_safe_stats.ul_tdd_mismatch;
|
||||
out_stats->ul_cplane_missing += ctx->thread_safe_stats.ul_cplane_missing;
|
||||
out_stats->prach_cplane_missing += ctx->thread_safe_stats.prach_cplane_missing;
|
||||
out_stats->prach_cplane_missing_ant += ctx->thread_safe_stats.prach_cplane_missing_ant;
|
||||
out_stats->prach_cplane_missing_inactive += ctx->thread_safe_stats.prach_cplane_missing_inactive;
|
||||
out_stats->prach_cplane_missing_stale += ctx->thread_safe_stats.prach_cplane_missing_stale;
|
||||
out_stats->prach_cplane_missing_early += ctx->thread_safe_stats.prach_cplane_missing_early;
|
||||
out_stats->prach_out_of_mbufs += ctx->thread_safe_stats.prach_out_of_mbufs;
|
||||
out_stats->prach_jobs_pool_exhausted += ctx->thread_safe_stats.prach_jobs_pool_exhausted;
|
||||
out_stats->out_of_mbufs += ctx->thread_safe_stats.out_of_mbufs;
|
||||
out_stats->total_uplane_sent = ctx->thread_safe_stats.total_uplane_sent;
|
||||
out_stats->ul_uplane_ota_delay_sum += ctx->thread_safe_stats.ul_uplane_ota_delay_sum;
|
||||
out_stats->ul_uplane_ota_delay_count += ctx->thread_safe_stats.ul_uplane_ota_delay_count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,7 +835,7 @@ static void unpack_iq(c16_t *txdataF, void *iqdata, int start_prb, int num_prb)
|
||||
}
|
||||
}
|
||||
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
@@ -750,6 +850,7 @@ void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *s
|
||||
uint64_t absolute_gps_symbol = job->absolute_symbol;
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
*hyper_frame = (absolute_gps_symbol / num_symbols_per_frame) / 1024;
|
||||
*frame = (absolute_gps_symbol / num_symbols_per_frame) % 1024;
|
||||
*slot = (absolute_gps_symbol % num_symbols_per_frame) / NR_SYMBOLS_PER_SLOT;
|
||||
*symbol = absolute_gps_symbol % NR_SYMBOLS_PER_SLOT;
|
||||
@@ -830,11 +931,142 @@ void fill_data_section_header(struct data_section_hdr *data_section_hdr, int num
|
||||
data_section_hdr->fields.all_bits = rte_cpu_to_be_32(data_section_hdr->fields.all_bits);
|
||||
}
|
||||
|
||||
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
|
||||
int poll_ul_job(void *context, ul_job_t *job)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL || job == NULL) {
|
||||
return -1;
|
||||
}
|
||||
ul_job_t *dequeued_job = NULL;
|
||||
if (rte_ring_dequeue(ctx->ul_ready_jobs, (void **)&dequeued_job) == 0) {
|
||||
*job = *dequeued_job;
|
||||
rte_ring_enqueue(ctx->ul_free_jobs, (void *)dequeued_job);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL || job == NULL)
|
||||
return;
|
||||
AssertFatal(symbol >= job->symbol && symbol < job->symbol + job->num_symbols && symbol < NR_SYMBOLS_PER_SLOT,
|
||||
"Symbol %d outside of job range [%d, %d)\n",
|
||||
symbol,
|
||||
job->symbol,
|
||||
job->symbol + job->num_symbols);
|
||||
|
||||
// Delay from OTA symbol to packet send: current timer symbol minus the absolute symbol of this UL symbol.
|
||||
const int slots_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << ctx->numerology);
|
||||
const uint64_t ota_absolute_symbol = (uint64_t)job->hyper_frame * 1024ULL * slots_per_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)job->frame * slots_per_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)job->slot_in_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)symbol;
|
||||
int64_t delay = (int64_t)ctx->current_absolute_symbol - (int64_t)ota_absolute_symbol;
|
||||
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_sum, delay, memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_count, 1, memory_order_relaxed);
|
||||
|
||||
int aarx = job->antenna_id;
|
||||
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
|
||||
LOG_W(HW, "ORU: Invalid antenna index %d\n", aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_comp = (job->response_payload.comp_method != 0);
|
||||
|
||||
int section_id = job->response_payload.section_id;
|
||||
int total_ul_rbs = job->num_prb;
|
||||
int start_prb_base = job->start_prb;
|
||||
int frame = job->frame;
|
||||
int slot_in_frame = job->slot_in_frame;
|
||||
int mu = ctx->numerology;
|
||||
|
||||
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
|
||||
uint32_t num_mbufs = 0;
|
||||
|
||||
int rbs_sent = 0;
|
||||
size_t overhead = sizeof(struct rte_ether_hdr) + sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr);
|
||||
if (use_comp) {
|
||||
overhead += sizeof(struct data_section_compression_hdr);
|
||||
}
|
||||
int max_prb_per_packet = (int)((ctx->mtu - overhead) / (NR_NB_SC_PER_RB * sizeof(int32_t)));
|
||||
|
||||
while (rbs_sent < total_ul_rbs) {
|
||||
int num_ul_rbs = total_ul_rbs - rbs_sent;
|
||||
if (num_ul_rbs > max_prb_per_packet) {
|
||||
num_ul_rbs = max_prb_per_packet;
|
||||
}
|
||||
|
||||
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
|
||||
if (pkt == NULL) {
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr);
|
||||
if (use_comp) {
|
||||
header_length += sizeof(struct data_section_compression_hdr);
|
||||
}
|
||||
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
|
||||
size_t data_len = sizeof(int32_t) * num_sc;
|
||||
|
||||
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
|
||||
if (buf == NULL) {
|
||||
LOG_W(HW, "ORU: Failed to append data to mbuf (insufficient space)\n");
|
||||
rte_pktmbuf_free(pkt);
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_mbufs == (MAX_MBUFS_PER_SYMBOL - 1)) {
|
||||
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
|
||||
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
|
||||
num_mbufs = 0;
|
||||
}
|
||||
mbufs[num_mbufs++] = pkt;
|
||||
|
||||
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)buf;
|
||||
uint16_t ecpri_payload_size = (uint16_t)(header_length - 4 + data_len);
|
||||
fill_ecpri_header(ecpri_header, &ctx->eaxcid_config, ECPRI_IQ_DATA, ecpri_payload_size, 0, aarx, ctx->pusch_seq_id[aarx]++, 0);
|
||||
|
||||
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
|
||||
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
|
||||
|
||||
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
|
||||
fill_data_section_header(data_section_header, num_ul_rbs, start_prb_base + rbs_sent, section_id);
|
||||
|
||||
void *iq_data_start;
|
||||
if (use_comp) {
|
||||
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
|
||||
compression_header->ud_comp_hdr.ud_comp_meth = job->response_payload.comp_method;
|
||||
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(job->response_payload.iq_width);
|
||||
compression_header->rsrvd = 0;
|
||||
iq_data_start = (void *)(compression_header + 1);
|
||||
} else {
|
||||
iq_data_start = (void *)(data_section_header + 1);
|
||||
}
|
||||
|
||||
uint16_t *src = (uint16_t *)&rxdataF[(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
|
||||
uint16_t *dst = (uint16_t *)iq_data_start;
|
||||
for (int i = 0; i < num_sc * 2; i++) {
|
||||
*dst++ = rte_cpu_to_be_16(*src++);
|
||||
}
|
||||
rbs_sent += num_ul_rbs;
|
||||
}
|
||||
|
||||
if (num_mbufs > 0) {
|
||||
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
|
||||
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
|
||||
}
|
||||
}
|
||||
|
||||
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t current_symbol_in_frame = ctx->current_absolute_symbol % num_symbols_per_frame;
|
||||
@@ -846,135 +1078,56 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
|
||||
diff -= num_symbols_per_frame;
|
||||
}
|
||||
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
|
||||
bool is_ul_symbol = test_bit(ctx->ul_symbol_bitmask, target_absolute_symbol % ctx->symbol_bitmask_length);
|
||||
if (!is_ul_symbol) {
|
||||
ctx->thread_safe_stats.ul_tdd_mismatch++;
|
||||
return;
|
||||
}
|
||||
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[target_absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
if (job->state == SYM_UL_IDLE) {
|
||||
ctx->thread_safe_stats.ul_cplane_missing++;
|
||||
return;
|
||||
}
|
||||
int mu = ctx->numerology;
|
||||
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
|
||||
uint32_t num_mbufs = 0;
|
||||
for (int aarx = 0; aarx < nb_rx; aarx++) {
|
||||
if (!job->per_antenna[aarx].cplane_received) {
|
||||
ctx->thread_safe_stats.ul_cplane_missing++;
|
||||
continue;
|
||||
}
|
||||
int section_id = job->per_antenna[aarx].section_id;
|
||||
int total_ul_rbs = job->per_antenna[aarx].num_prb;
|
||||
int start_prb_base = job->per_antenna[aarx].start_prb;
|
||||
|
||||
int rbs_sent = 0;
|
||||
size_t overhead = sizeof(struct rte_ether_hdr) + sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
|
||||
int max_prb_per_packet = (int)((ctx->mtu - overhead) / (NR_NB_SC_PER_RB * sizeof(int32_t)));
|
||||
|
||||
while (rbs_sent < total_ul_rbs) {
|
||||
int num_ul_rbs = total_ul_rbs - rbs_sent;
|
||||
if (num_ul_rbs > max_prb_per_packet) {
|
||||
num_ul_rbs = max_prb_per_packet;
|
||||
}
|
||||
|
||||
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
|
||||
if (pkt == NULL) {
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr)
|
||||
+ sizeof(struct data_section_compression_hdr);
|
||||
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
|
||||
size_t data_len = sizeof(int32_t) * num_sc;
|
||||
|
||||
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
|
||||
if (buf == NULL) {
|
||||
LOG_W(HW, "ORU: Failed to append data to mbuf (insufficient space)\n");
|
||||
rte_pktmbuf_free(pkt);
|
||||
// Do not increment rbs_sent, just break and let the next symbol or antenna be processed,
|
||||
// or we could reduce max_prb_per_packet. Breaking is safer to prevent infinite loop.
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_mbufs == (MAX_MBUFS_PER_SYMBOL - 1)) {
|
||||
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
|
||||
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
|
||||
num_mbufs = 0;
|
||||
}
|
||||
mbufs[num_mbufs++] = pkt;
|
||||
|
||||
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)buf;
|
||||
// eCPRI payload size excludes the 4-byte common header
|
||||
uint16_t ecpri_payload_size = (uint16_t)(header_length - 4 + data_len);
|
||||
fill_ecpri_header(ecpri_header,
|
||||
&ctx->eaxcid_config,
|
||||
ECPRI_IQ_DATA,
|
||||
ecpri_payload_size,
|
||||
0,
|
||||
aarx,
|
||||
ctx->pusch_seq_id[aarx]++,
|
||||
0);
|
||||
|
||||
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
|
||||
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
|
||||
|
||||
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
|
||||
fill_data_section_header(data_section_header, num_ul_rbs, start_prb_base + rbs_sent, section_id);
|
||||
|
||||
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
|
||||
compression_header->ud_comp_hdr.ud_comp_meth = 0;
|
||||
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(16);
|
||||
compression_header->rsrvd = 0;
|
||||
|
||||
void *iq_data_start = (void *)(compression_header + 1);
|
||||
uint16_t *src = (uint16_t *)&txdataF[aarx][(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
|
||||
uint16_t *dst = (uint16_t *)iq_data_start;
|
||||
for (int i = 0; i < num_sc * 2; i++) {
|
||||
*dst++ = rte_cpu_to_be_16(*src++);
|
||||
}
|
||||
rbs_sent += num_ul_rbs;
|
||||
}
|
||||
}
|
||||
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
|
||||
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
|
||||
for (int i = 0; i < MAX_ANTENNAS; i++) {
|
||||
job->per_antenna[i].cplane_received = false;
|
||||
}
|
||||
job->state = SYM_UL_IDLE;
|
||||
}
|
||||
|
||||
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
int mu = ctx->numerology;
|
||||
uint64_t absolute_symbol = (frame * NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << mu) + slot_in_frame) * NR_SYMBOLS_PER_SLOT + symbol;
|
||||
uint32_t job_index = absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
|
||||
|
||||
if (job->absolute_symbol != absolute_symbol)
|
||||
return;
|
||||
|
||||
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
|
||||
uint32_t num_mbufs = 0;
|
||||
|
||||
for (int aarx = 0; aarx < nb_rx; aarx++) {
|
||||
if (!job->per_antenna[aarx].cplane_received)
|
||||
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Invalid slot_in_frame %d\n", slot_in_frame); });
|
||||
continue;
|
||||
}
|
||||
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
|
||||
if (!job->active || target_absolute_symbol < job->start_absolute_symbol
|
||||
|| target_absolute_symbol >= job->start_absolute_symbol + job->num_symbols) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing++;
|
||||
if (!job->active) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_inactive++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH UP: Missing C-Plane - Inactive job for slot %d, aarx %d\n", slot_in_frame, aarx); });
|
||||
} else if (target_absolute_symbol < job->start_absolute_symbol) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_early++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW,
|
||||
"PRACH UP: Missing C-Plane - Early symbol %lu (job start %lu) for slot %d, aarx %d\n",
|
||||
target_absolute_symbol,
|
||||
job->start_absolute_symbol,
|
||||
slot_in_frame,
|
||||
aarx);
|
||||
});
|
||||
} else {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_stale++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW,
|
||||
"PRACH UP: Missing C-Plane - Stale symbol %lu (job end %lu) for slot %d, aarx %d\n",
|
||||
target_absolute_symbol,
|
||||
job->start_absolute_symbol + job->num_symbols,
|
||||
slot_in_frame,
|
||||
aarx);
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int section_id = job->per_antenna[aarx].section_id;
|
||||
int num_ul_rbs = job->per_antenna[aarx].num_prb;
|
||||
int start_prb = job->per_antenna[aarx].start_prb;
|
||||
int filter_id = job->per_antenna[aarx].filter_id;
|
||||
int section_id = job->section_id;
|
||||
int num_ul_rbs = job->num_prb;
|
||||
int start_prb = job->start_prb;
|
||||
int filter_id = job->filter_id;
|
||||
|
||||
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
|
||||
if (pkt == NULL) {
|
||||
ctx->thread_safe_stats.prach_out_of_mbufs++;
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to allocate mbuf\n"); });
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -984,7 +1137,10 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
|
||||
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
|
||||
if (buf == NULL) {
|
||||
ctx->thread_safe_stats.prach_out_of_mbufs++;
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
rte_pktmbuf_free(pkt);
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to append data to mbuf\n"); });
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1003,7 +1159,7 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
0);
|
||||
|
||||
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
|
||||
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
|
||||
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, numerology);
|
||||
|
||||
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
|
||||
fill_data_section_header(data_section_header, num_ul_rbs, start_prb, section_id);
|
||||
@@ -1014,8 +1170,6 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
for (int i = 0; i < prach_length * 2; i++) {
|
||||
*dst++ = rte_cpu_to_be_16(*src++);
|
||||
}
|
||||
|
||||
job->per_antenna[aarx].cplane_received = false;
|
||||
}
|
||||
|
||||
if (num_mbufs > 0) {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -13,8 +19,30 @@ typedef struct {
|
||||
uint64_t count;
|
||||
} txrx_histogram_t;
|
||||
|
||||
typedef struct {
|
||||
int section_id;
|
||||
int comp_method;
|
||||
int iq_width;
|
||||
} opaque_response_data_t;
|
||||
|
||||
typedef struct {
|
||||
opaque_response_data_t response_payload;
|
||||
uint64_t hyper_frame;
|
||||
int frame;
|
||||
int slot_in_frame;
|
||||
int symbol;
|
||||
int num_symbols;
|
||||
int antenna_id;
|
||||
int start_prb;
|
||||
int num_prb;
|
||||
} ul_job_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t total_cplane;
|
||||
uint64_t cplane_received_dl;
|
||||
uint64_t cplane_received_ul;
|
||||
uint64_t cplane_received_prach;
|
||||
uint64_t cplane_received_other;
|
||||
uint64_t total_uplane_received;
|
||||
uint64_t total_uplane_sent;
|
||||
uint64_t cplane_err_hdr; // apphdr or section extraction
|
||||
@@ -22,6 +50,9 @@ typedef struct {
|
||||
uint64_t cplane_err_early;
|
||||
uint64_t cplane_err_late;
|
||||
uint64_t cplane_err_dup; // duplicate cplane
|
||||
uint64_t cplane_err_dup_dl;
|
||||
uint64_t cplane_err_dup_ul;
|
||||
uint64_t cplane_err_dup_prach;
|
||||
uint64_t uplane_err_late;
|
||||
uint64_t uplane_err_early;
|
||||
uint64_t uplane_err_dup;
|
||||
@@ -31,10 +62,19 @@ typedef struct {
|
||||
uint64_t ul_tdd_mismatch;
|
||||
uint64_t out_of_mbufs;
|
||||
uint64_t ul_cplane_missing;
|
||||
uint64_t prach_cplane_missing;
|
||||
uint64_t prach_cplane_missing_ant;
|
||||
uint64_t prach_cplane_missing_inactive;
|
||||
uint64_t prach_cplane_missing_stale;
|
||||
uint64_t prach_cplane_missing_early;
|
||||
uint64_t prach_out_of_mbufs;
|
||||
uint64_t prach_jobs_pool_exhausted;
|
||||
txrx_histogram_t dl_uplane_hist;
|
||||
txrx_histogram_t dl_cplane_hist;
|
||||
txrx_histogram_t ul_cplane_hist;
|
||||
txrx_histogram_t prach_cplane_hist;
|
||||
int64_t ul_uplane_ota_delay_sum;
|
||||
uint64_t ul_uplane_ota_delay_count;
|
||||
} oru_packet_processor_stats_t;
|
||||
|
||||
typedef void *(*alloc_func_t)(void *io_controller);
|
||||
@@ -56,7 +96,7 @@ void *init_packet_processor(int numerology,
|
||||
void *io_controller,
|
||||
size_t mtu,
|
||||
int prach_eaxc_offset);
|
||||
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
|
||||
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job);
|
||||
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
|
||||
void cleanup_packet_processor(void *context);
|
||||
void handle_absolute_symbol_tick(void *context, uint64_t absolute_symbol);
|
||||
@@ -64,8 +104,9 @@ void handle_uplane_packet(void *context, void *pkt);
|
||||
void handle_cplane_packet(void *context, void *pkt);
|
||||
void print_packet_processor_stats(void *context);
|
||||
void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out_stats);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
int get_ready_job_count(void *context);
|
||||
int poll_ul_job(void *context, ul_job_t *job);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ target_link_libraries(test_oru_pcap PRIVATE oru_packet_processor log_headers ${d
|
||||
target_include_directories(test_oru_pcap PRIVATE ${dpdk_INCLUDE_DIRS} ${PCAP_INCLUDE_DIRS})
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/run_oru_pcap_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_test(NAME test_oru_pcap_1 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 9600 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_1")
|
||||
add_test(NAME test_oru_pcap_1 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://github.com/bpodrygajlo/openairinterface5g/releases/download/fhi72-pcaps/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 9600 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_1")
|
||||
set_property(TEST test_oru_pcap_1 PROPERTY LABELS fronthaul)
|
||||
|
||||
add_test(NAME test_oru_pcap_frag COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 1500 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_frag")
|
||||
add_test(NAME test_oru_pcap_frag COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://github.com/bpodrygajlo/openairinterface5g/releases/download/fhi72-pcaps/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 1500 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_frag")
|
||||
set_property(TEST test_oru_pcap_frag PROPERTY LABELS fronthaul)
|
||||
|
||||
add_dependencies(tests test_oru_pcap)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -e
|
||||
|
||||
# This script downloads a PCAP file, unarchives it, and then runs a test executable.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user