mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-14 05:00:31 +00:00
Compare commits
270 Commits
dragonwing
...
develop
| 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 | ||
|
|
f3589d97c2 | ||
|
|
05a2fdf095 | ||
|
|
945fe9a756 | ||
|
|
443f580153 | ||
|
|
43fc8c084c | ||
|
|
4b32f4d161 | ||
|
|
f19b44639d | ||
|
|
48806d032a | ||
|
|
d75e99af63 | ||
|
|
b9fc7cc5d2 | ||
|
|
f1e5d1fb4f | ||
|
|
885e5e8895 | ||
|
|
11b8ba54ea | ||
|
|
8eeb1b125f | ||
|
|
ad44ee75f6 | ||
|
|
1360217cae | ||
|
|
b7cba9d266 | ||
|
|
ad12c4f9a8 | ||
|
|
31084d6698 | ||
|
|
0bcc3b3229 | ||
|
|
8b04c038f1 | ||
|
|
9163c8b2bd | ||
|
|
5fae4b3c29 | ||
|
|
29d31cde8e | ||
|
|
dfb0c61c62 | ||
|
|
854090632f | ||
|
|
a1162f9fd3 | ||
|
|
096a107704 | ||
|
|
63af7fcfb8 | ||
|
|
911f069709 | ||
|
|
2c7530e0bf | ||
|
|
4b79c55ce6 | ||
|
|
2ff86cfb27 | ||
|
|
0a0de88ff8 | ||
|
|
fa237ffab4 | ||
|
|
8c255558f0 | ||
|
|
460cbf954d | ||
|
|
80451a917f | ||
|
|
d2bbf09121 | ||
|
|
039b008676 | ||
|
|
1b77adcb10 | ||
|
|
7db4dfec73 | ||
|
|
e2676b5159 | ||
|
|
ce4a9da3ca | ||
|
|
79171c19af | ||
|
|
0a9d817d9c | ||
|
|
33e09d7e3e | ||
|
|
d6e9492b6f | ||
|
|
29be39948c | ||
|
|
d4d494d0d3 | ||
|
|
62846a9311 | ||
|
|
db5c84e864 | ||
|
|
acab173950 | ||
|
|
19a8a0d1d4 | ||
|
|
8e30ec8031 |
@@ -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/
|
||||
|
||||
143
.github/workflows/jenkins-dispatch.yml
vendored
143
.github/workflows/jenkins-dispatch.yml
vendored
@@ -1,14 +1,15 @@
|
||||
name: Duranta Jenkins Dispatch
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
branches: [develop]
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
|
||||
group: ${{ github.event_name }}-${{ github.event.action }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
@@ -16,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 }}
|
||||
@@ -39,12 +100,18 @@ jobs:
|
||||
- 'ci-scripts/**'
|
||||
- '.github/workflows/**'
|
||||
- '.github/actions/**'
|
||||
- 'docker/**'
|
||||
- 'charts/**'
|
||||
- 'openshift/**'
|
||||
- 'cmake_targets/build_oai'
|
||||
- 'cmake_targets/tools/build_helper'
|
||||
|
||||
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:
|
||||
@@ -55,6 +122,7 @@ jobs:
|
||||
|
||||
trigger-jenkins:
|
||||
needs:
|
||||
- check-labels
|
||||
- detect-changes
|
||||
- require-maintainer-approval
|
||||
|
||||
@@ -69,6 +137,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.ref }}
|
||||
|
||||
- name: Trigger Jenkins
|
||||
run: |
|
||||
@@ -80,54 +150,41 @@ jobs:
|
||||
# Filter and send
|
||||
jq -c 'del(.pull_request.body)' /tmp/payload.json > /tmp/filtered_payload.json
|
||||
|
||||
# Remap pull_request_target -> pull_request for Jenkins compatibility
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
if [ "$EVENT_NAME" = "pull_request_target" ]; then
|
||||
EVENT_NAME="pull_request"
|
||||
fi
|
||||
|
||||
curl -X POST "https://${{ secrets.J_USER }}:${{ secrets.J_PASS }}@${{ secrets.J_URL }}" \
|
||||
-H "Accept: */*" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: GitHub-Hookshot/${{ secrets.H_AGENT }}" \
|
||||
-H "X-Github-Delivery: ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-Github-Event: ${{ github.event_name }}" \
|
||||
-H "X-Github-Event: $EVENT_NAME" \
|
||||
-H "X-Github-Hook-Id: ${{ secrets.H_ID }}" \
|
||||
-H "X-Github-Hook-Installation-Target-Id: ${{ secrets.H_TARGET }}" \
|
||||
-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 }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
welcome-first-time-contributor:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'opened'
|
||||
|
||||
steps:
|
||||
- name: Post welcome message
|
||||
uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pr-message: |
|
||||
Welcome @${{ github.event.pull_request.user.login }}! Thanks for opening your first PR in this project!
|
||||
|
||||
Here are a few things to help you get started:
|
||||
- Read the [Contributing Guide](https://github.com/${{ github.repository }}/blob/develop/CONTRIBUTING.md) to understand our development process.
|
||||
- Check the [issues page](https://github.com/duranta-project/openairinterface5g/issues) to see if your PR is related to an existing issue.
|
||||
- Add a [label](https://github.com/duranta-project/openairinterface5g/labels/) to categorize your PR so reviewers can triage it faster.
|
||||
|
||||
pr-retrigger-ci-instructions:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'opened'
|
||||
|
||||
steps:
|
||||
- name: Post CI retrigger instructions
|
||||
run: |
|
||||
gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --body "$(cat <<'EOF'
|
||||
The CI pipeline runs automatically when this pull request is opened or updated.
|
||||
|
||||
To manually retrigger the CI pipeline without making any changes to the pull request, add the https://github.com/duranta-project/openairinterface5g/labels/retrigger-ci label to this PR.
|
||||
|
||||
The label will be removed automatically after the pipeline is triggered.
|
||||
EOF
|
||||
)"
|
||||
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 }}
|
||||
|
||||
@@ -153,15 +153,6 @@ endif()
|
||||
eval_boolean(AUTODETECT_AVX512 DEFINED CPUFLAGS AND CPUFLAGS MATCHES "avx512")
|
||||
add_boolean_option(AVX512 ${AUTODETECT_AVX512} "Whether AVX512 intrinsics is available on the host processor" ON)
|
||||
|
||||
# When the native-tools build (Step 1) is preparing generators for a
|
||||
# cross-compile targeting aarch64, pass -DAVOID_SIGN=ON so that the LDPC
|
||||
# code generators emit mm_xor_si128 instead of mm_sign_epi8. SIMDe's
|
||||
# translation of mm_sign_epi8 is incorrect for the sign-extraction pattern
|
||||
# used in cnProc; mm_xor_si128 is equivalent for the MSB-only use case and
|
||||
# translates correctly. Has no effect when CROSS_COMPILE is set (Step 2
|
||||
# does not build the generators).
|
||||
add_boolean_option(AVOID_SIGN OFF "Use mm_xor_si128 instead of mm_sign_epi8 in LDPC generators (required when cross-compiling for aarch64)" OFF)
|
||||
|
||||
eval_boolean(AUTODETECT_AVX2 DEFINED CPUFLAGS AND CPUFLAGS MATCHES "avx2")
|
||||
add_boolean_option(AVX2 ${AUTODETECT_AVX2} "Whether AVX2 intrinsics is available on the host processor" ON)
|
||||
|
||||
@@ -170,11 +161,7 @@ add_boolean_option(GFNI ${AUTODETECT_GFNI} "Whether GFNI intrinsics is available
|
||||
|
||||
message(STATUS "CPU architecture is ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
if (CROSS_COMPILE)
|
||||
if (QUALCOMM_DRAGONWING)
|
||||
message(STATUS "Target: Qualcomm DragonWing IQ-9/X (Cortex-A78 / ARMv8.2-A)")
|
||||
else()
|
||||
message(STATUS "setting as aarch64")
|
||||
endif()
|
||||
message(STATUS "setting as aarch64")
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -lgcc -lrt")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
# The following intrinsics are assumed to be available on any x86 system
|
||||
@@ -215,8 +202,6 @@ elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n1 -lgcc -lrt")
|
||||
elseif (CPUPART MATCHES "0xd49") # Neoverse-N2
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n2 -ftree-vectorize -lgcc -lrt")
|
||||
elseif (CPUPART MATCHES "0xd08") # Cortex-A72 (e.g. NXP LS2160A)
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=cortex-a72 -lgcc -lrt")
|
||||
elseif (CPUPART MATCHES "0xd03") # Cortex-A53
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=cortex-a53 -lgcc -lrt")
|
||||
else ()
|
||||
@@ -227,23 +212,14 @@ else()
|
||||
endif()
|
||||
|
||||
if (NOT CROSS_COMPILE)
|
||||
if (CPUPART MATCHES "0xd08")
|
||||
# Cortex-A72 is ARMv8.0-A only (no ARMv8.2-A); hardware includes crypto (aes, pmull, sha1, sha2)
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8-a+simd+crypto")
|
||||
elseif (CPUPART MATCHES "0xd03")
|
||||
# Cortex-A53 is ARMv8.0-A only; crypto extension is optional and not assumed present
|
||||
if (CPUPART MATCHES "0xd03")
|
||||
# Cortex-A53 does not support ARMv8.2-A extensions
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8-a+simd")
|
||||
else()
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=native")
|
||||
endif()
|
||||
else ()
|
||||
if (QUALCOMM_DRAGONWING)
|
||||
# DragonWing IQ-9/X: Kryo 780 Gold = Cortex-A78 (ARMv8.2-A + fp16 + dotprod)
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -mcpu=cortex-a78")
|
||||
#set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8.2-a")
|
||||
else()
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8.2-a")
|
||||
endif()
|
||||
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8.2-a")
|
||||
endif()
|
||||
|
||||
# we need -rdynamic to incorporate all symbols in shared objects, see man page
|
||||
@@ -407,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 #
|
||||
##################################################
|
||||
@@ -879,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
|
||||
@@ -948,7 +929,6 @@ set(PHY_SRC_UE
|
||||
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch.c
|
||||
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c
|
||||
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach.c
|
||||
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c
|
||||
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c
|
||||
${OPENAIR1_DIR}/PHY/NR_REFSIG/ul_ref_seq_nr.c
|
||||
${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_dmrs_rx.c
|
||||
@@ -1231,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
|
||||
@@ -1793,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
|
||||
###################################################
|
||||
|
||||
@@ -1854,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">
|
||||
@@ -14,8 +14,8 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu18-Image-Builder/"><img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fjenkins-oai.eurecom.fr%2Fjob%2FRAN-Ubuntu18-Image-Builder%2F&label=build-Ubuntu-x86%20Images"></a>
|
||||
<a href="https://jenkins-oai.eurecom.fr/job/RAN-RHEL8-Cluster-Image-Builder/"><img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fjenkins-oai.eurecom.fr%2Fjob%2FRAN-RHEL8-Cluster-Image-Builder%2F&label=build-UBI-x86%20Images"></a>
|
||||
<a href="https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu-Image-Builder/"><img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fjenkins-oai.eurecom.fr%2Fjob%2FRAN-Ubuntu-Image-Builder%2F&label=build-Ubuntu-x86%20Images"></a>
|
||||
<a href="https://jenkins-oai.eurecom.fr/job/RAN-RHEL-Cluster-Image-Builder/"><img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fjenkins-oai.eurecom.fr%2Fjob%2FRAN-RHEL-Cluster-Image-Builder%2F&label=build-RHEL-Cluster%20Images"></a>
|
||||
<a href="https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu-ARM-Image-Builder/"><img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fjenkins-oai.eurecom.fr%2Fjob%2FRAN-Ubuntu-ARM-Image-Builder%2F&label=build-Ubuntu-ARM%20Images"></a>
|
||||
</p>
|
||||
|
||||
|
||||
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.
|
||||
@@ -35,4 +35,4 @@ sources:
|
||||
|
||||
maintainers:
|
||||
- name: OPENAIRINTERFACE
|
||||
email: contact@openairinterface.org
|
||||
email: oaicicdteam@openairinterface.org
|
||||
|
||||
@@ -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.
|
||||
@@ -34,4 +34,4 @@ sources:
|
||||
|
||||
maintainers:
|
||||
- name: OPENAIRINTERFACE
|
||||
email: contact@openairinterface.org
|
||||
email: oaicicdteam@openairinterface.org
|
||||
|
||||
@@ -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.
|
||||
@@ -35,4 +35,4 @@ sources:
|
||||
|
||||
maintainers:
|
||||
- name: OPENAIRINTERFACE
|
||||
email: contact@openairinterface.org
|
||||
email: oaicicdteam@openairinterface.org
|
||||
|
||||
@@ -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.
|
||||
@@ -34,4 +34,4 @@ sources:
|
||||
|
||||
maintainers:
|
||||
- name: OPENAIRINTERFACE
|
||||
email: contact@openairinterface.org
|
||||
email: oaicicdteam@openairinterface.org
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -55,8 +55,17 @@ done
|
||||
# ----------------------------
|
||||
# Merged commits
|
||||
# ----------------------------
|
||||
mergeCommits=$(git rev-list --merges --abbrev-commit "$TARGET_BRANCH".."$SOURCE_BRANCH")
|
||||
if [[ ! "$SOURCE_BRANCH" =~ ^(origin/)?integration_[0-9]{4}_w[0-9]{2}$ ]]; then
|
||||
if [[ "$SOURCE_BRANCH" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
# note: if no branch could be found, it will result in "" and git rev-list
|
||||
# will use the commit ID. Exclude "HEAD detached at", then use first branch
|
||||
# name.
|
||||
BRANCH_NAME=$(git branch -a --points-at $SOURCE_BRANCH --format='%(refname:short)' | grep -v detached | head -n1)
|
||||
echo "SHA recognized in $SOURCE_BRANCH, using \"$BRANCH_NAME\" as branch name"
|
||||
else
|
||||
BRANCH_NAME="$SOURCE_BRANCH"
|
||||
fi
|
||||
if [[ ! "$BRANCH_NAME" =~ ^(origin/)?integration_[0-9]{4}_w[0-9]{2}$ ]]; then
|
||||
mergeCommits=$(git rev-list --merges --abbrev-commit "$TARGET_BRANCH".."$SOURCE_BRANCH")
|
||||
if [[ -n "$mergeCommits" ]]; then
|
||||
message="Error: Following merge commits are found in the source branch history. Please rebase your branch.\n\n"
|
||||
message+="$(echo "$mergeCommits" | paste -sd ',' | sed 's/,/, /g')\n"
|
||||
|
||||
@@ -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**
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ ru_sdr:
|
||||
srate: 23.04
|
||||
tx_gain: 25
|
||||
rx_gain: 25
|
||||
amplitude_control:
|
||||
tx_gain_backoff: 22
|
||||
|
||||
cell_cfg:
|
||||
dl_arfcn: 632628
|
||||
@@ -42,8 +44,8 @@ cell_cfg:
|
||||
nof_cell_csi_res: 0
|
||||
|
||||
log:
|
||||
filename: gnb.log
|
||||
all_level: debug
|
||||
filename: stdout
|
||||
all_level: info
|
||||
|
||||
pcap:
|
||||
mac_enable: false
|
||||
|
||||
@@ -20,6 +20,8 @@ ru_sdr:
|
||||
srate: 61.44
|
||||
tx_gain: 25
|
||||
rx_gain: 25
|
||||
amplitude_control:
|
||||
tx_gain_backoff: 22
|
||||
|
||||
cell_cfg:
|
||||
dl_arfcn: 632628
|
||||
@@ -44,8 +46,8 @@ cell_cfg:
|
||||
nof_cell_csi_res: 0
|
||||
|
||||
log:
|
||||
filename: gnb.log
|
||||
all_level: debug
|
||||
filename: stdout
|
||||
all_level: info
|
||||
|
||||
pcap:
|
||||
mac_enable: false
|
||||
|
||||
@@ -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;;
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
# CMake toolchain for Qualcomm DragonWing IQ-9/X (ARM component, no Hexagon DSP).
|
||||
# Uses the GCC aarch64 cross-compiler bundled with the Hexagon SDK 6.4.x.
|
||||
#
|
||||
# Note: the SDK's clang (hexagon-clang) targets only the Hexagon DSP; the ARM
|
||||
# cross-compiler is the Arm GNU Toolchain GCC (aarch64-none-linux-gnu-gcc).
|
||||
#
|
||||
# Prerequisites – same as the generic cross-arm.cmake, install arm64 target libs:
|
||||
# sudo dpkg --add-architecture arm64
|
||||
# sudo apt-get install -y libgnutls28-dev:arm64 libconfig-dev:arm64 \
|
||||
# libsctp-dev:arm64 libssl-dev:arm64 zlib1g-dev:arm64 libreadline-dev:arm64
|
||||
#
|
||||
# Two-step build (see doc/cross-compile.md for details):
|
||||
#
|
||||
# Step 1 – native host tools (ldpc generators, T-tracer id generator):
|
||||
# mkdir -p ran_build/build ran_build/build-dragonwing
|
||||
# cd ran_build/build
|
||||
# cmake ../../..
|
||||
# make -j$(nproc) ldpc_generators generate_T
|
||||
#
|
||||
# Step 2 – cross-compiled ARM binaries:
|
||||
# cd ../build-dragonwing
|
||||
# cmake ../../.. -GNinja \
|
||||
# -DCMAKE_TOOLCHAIN_FILE=../../../cmake_targets/cross-arm-dragonwing.cmake \
|
||||
# -DNATIVE_DIR=../build
|
||||
# ninja nr-softmodem nr-cuup nr-uesoftmodem params_libconfig coding rfsimulator
|
||||
#
|
||||
# Deploy via adb:
|
||||
# adb push ran_build/build-dragonwing/nr-softmodem /data/oai/
|
||||
#
|
||||
# Optional overrides:
|
||||
# -DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 (if SDK is in a different location)
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
if(NOT DEFINED HEXAGON_SDK_ROOT)
|
||||
set(HEXAGON_SDK_ROOT "/opt/Hexagon_SDK/6.4.0.2")
|
||||
endif()
|
||||
set(_DW_TC "${HEXAGON_SDK_ROOT}/tools/gcc_tools_64/bin/aarch64-none-linux-gnu")
|
||||
|
||||
set(CMAKE_C_COMPILER "${_DW_TC}-gcc")
|
||||
set(CMAKE_CXX_COMPILER "${_DW_TC}-g++")
|
||||
set(CMAKE_AR "${_DW_TC}-ar")
|
||||
|
||||
# The SDK GCC's built-in sysroot has an incomplete glibc (missing L_tmpnam and
|
||||
# similar symbols in stdio.h). Override with the Ubuntu system headers by
|
||||
# prepending them via -I, which takes priority over the compiler's sysroot
|
||||
# search paths. /usr/include is the host glibc (architecture-neutral API);
|
||||
# /usr/include/aarch64-linux-gnu has the arm64-specific bits/ and sys/ headers.
|
||||
set(CMAKE_C_FLAGS_INIT "-I/usr/include/aarch64-linux-gnu -I/usr/include")
|
||||
set(CMAKE_CXX_FLAGS_INIT "-I/usr/include/aarch64-linux-gnu -I/usr/include")
|
||||
|
||||
# Linker: search the arm64 multiarch directory so arm64 .so files are found
|
||||
# before any host x86 libraries.
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "-L/usr/lib/aarch64-linux-gnu")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-L/usr/lib/aarch64-linux-gnu")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-L/usr/lib/aarch64-linux-gnu")
|
||||
|
||||
# Direct pkg-config at the arm64 multiarch .pc files
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/aarch64-linux-gnu/pkgconfig")
|
||||
|
||||
# Tell cmake's find_library() where Ubuntu multiarch arm64 libs live
|
||||
set(CMAKE_LIBRARY_PATH "/usr/lib/aarch64-linux-gnu")
|
||||
|
||||
set(CROSS_COMPILE 1)
|
||||
set(QUALCOMM_DRAGONWING 1)
|
||||
|
||||
# Paths to host-compiled code-generation tools (built in Step 1 above)
|
||||
set(bnProc_gen_128_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(bnProc_gen_avx2_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(bnProc_gen_avx512_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(cnProc_gen_128_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(cnProc_gen_avx2_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(cnProc_gen_avx512_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(genids_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
set(_check_vcd_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NATIVE_DIR})
|
||||
@@ -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
|
||||
|
||||
@@ -70,7 +70,8 @@ static __attribute__((always_inline)) inline int count_bits64(uint64_t v)
|
||||
|
||||
static __attribute__((always_inline)) inline int count_bits64_with_mask(uint64_t v, int start, int num)
|
||||
{
|
||||
uint64_t mask = ((1LL << num) - 1) << start;
|
||||
AssertFatal(start >= 0 && num > 0 && start + num <= 64, "Invalid range: start=%d num=%d for 64 bits mask\n", start, num);
|
||||
uint64_t mask = (num == 64 ? ~0ULL : (1ULL << num) - 1) << start;
|
||||
return count_bits64(v & mask);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
#
|
||||
# CMakeLists.txt — Hexagon DSP offload template.
|
||||
#
|
||||
# Builds two targets from a single file depending on OS_TYPE:
|
||||
#
|
||||
# OS_TYPE=HLOS → libhexdsp_offload.so (ARM stub, loaded by OAI)
|
||||
# (default) → libhexdsp_offload_skel.so (DSP skeleton, pushed to device)
|
||||
#
|
||||
# ── ARM (HLOS) build ────────────────────────────────────────────────────────
|
||||
#
|
||||
# cmake -B build-arm \
|
||||
# -DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 \
|
||||
# -DCMAKE_TOOLCHAIN_FILE=../../cmake_targets/cross-arm-dragonwing.cmake \
|
||||
# -DOS_TYPE=HLOS \
|
||||
# common/utils/hexagon_offload_template
|
||||
# cmake --build build-arm
|
||||
#
|
||||
# ── DSP (Hexagon) build ──────────────────────────────────────────────────────
|
||||
#
|
||||
# cmake -B build-dsp \
|
||||
# -DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 \
|
||||
# -DCMAKE_TOOLCHAIN_FILE=/opt/Hexagon_SDK/6.4.0.2/build/cmake/hexagon_toolchain.cmake \
|
||||
# -DDSP_VERSION=v73 \
|
||||
# common/utils/hexagon_offload_template
|
||||
# cmake --build build-dsp
|
||||
#
|
||||
# ── Deploy ──────────────────────────────────────────────────────────────────
|
||||
#
|
||||
# # ARM stub: push alongside the other OAI shared libs
|
||||
# adb push build-arm/libhexdsp_offload.so /data/oai/lib/
|
||||
#
|
||||
# # DSP skel: FastRPC loads it from the adsp library path
|
||||
# adb push build-dsp/libhexdsp_offload_skel.so /vendor/lib/rfsa/adsp/
|
||||
#
|
||||
# ── OAI integration ──────────────────────────────────────────────────────────
|
||||
#
|
||||
# OAI loads the ARM stub via load_module_shlib (common/utils/load_module_shlib.c):
|
||||
#
|
||||
# loader_shlibfunc_t fdesc[] = {
|
||||
# {.fname = "hexdsp_offload_init"},
|
||||
# {.fname = "hexdsp_offload_shutdown"},
|
||||
# {.fname = "hexdsp_offload_process"},
|
||||
# };
|
||||
# load_module_version_shlib("hexdsp_offload", "", fdesc, 3, NULL);
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
project(hexdsp_offload C)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hexagon SDK root — defaults to the standard install path.
|
||||
# Override with -DHEXAGON_SDK_ROOT=<path> if needed.
|
||||
# ---------------------------------------------------------------------------
|
||||
if(NOT HEXAGON_SDK_ROOT)
|
||||
set(HEXAGON_SDK_ROOT "/opt/Hexagon_SDK/6.4.0.2")
|
||||
endif()
|
||||
|
||||
include(${HEXAGON_SDK_ROOT}/build/cmake/hexagon_fun.cmake)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Include paths shared by both ARM stub and DSP skel.
|
||||
# ---------------------------------------------------------------------------
|
||||
set(common_incs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/inc
|
||||
${CMAKE_CURRENT_BINARY_DIR} # qaic writes generated .h here
|
||||
${HEXAGON_SDK_ROOT}/incs
|
||||
${HEXAGON_SDK_ROOT}/incs/stddef
|
||||
${HEXAGON_SDK_ROOT}/ipc/fastrpc/rpcmem/inc
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ARM (HLOS) side — stub shared library loaded by OAI via dlopen.
|
||||
# ---------------------------------------------------------------------------
|
||||
if(${OS_TYPE} MATCHES "HLOS")
|
||||
|
||||
add_library(hexdsp_offload SHARED
|
||||
# qaic-generated stub (file created at configure time by build_idl)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/hexdsp_offload_stub
|
||||
# ARM glue: rpcmem bounce buffers + OAI-compatible function exports
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hexdsp_offload_arm
|
||||
)
|
||||
|
||||
# Run qaic on the IDL file; adds hexdsp_offload_stub.c and hexdsp_offload.h
|
||||
# to the build as side-effects.
|
||||
build_idl(inc/hexdsp_offload.idl hexdsp_offload)
|
||||
|
||||
include_directories(${common_incs})
|
||||
|
||||
# Pick the right cdsprpc library for the target OS (Android vs Ubuntu ARM).
|
||||
choose_dsprpc(cdsp dsprpc)
|
||||
link_custom_library(hexdsp_offload ${dsprpc})
|
||||
|
||||
# Do NOT link rpcmem.a here.
|
||||
# The SDK's rpcmem.a contains only deprecated no-op stubs for
|
||||
# rpcmem_init / rpcmem_deinit. If linked, they shadow the real
|
||||
# implementations in the device's libcdsprpc.so, leaving the DMA heap
|
||||
# uninitialised so rpcmem_alloc() silently fails (error -104 on open).
|
||||
# All rpcmem symbols are satisfied at runtime by libcdsprpc.so.
|
||||
|
||||
# Applies SDK-standard compile flags (PIC, warnings, …).
|
||||
set_common_compile_and_link_options(hexdsp_offload)
|
||||
|
||||
install(TARGETS hexdsp_offload LIBRARY DESTINATION lib)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DSP (Hexagon) side — skel shared library pushed to /vendor/lib/rfsa/adsp/.
|
||||
# ---------------------------------------------------------------------------
|
||||
else()
|
||||
|
||||
# DSP_VERSION selects the Hexagon ISA revision; must match the hardware.
|
||||
# Known values: v73 (SM8550 era), v75, v79, v81.
|
||||
# For the DragonWing SA9000P confirm the exact version with:
|
||||
# adb shell cat /sys/devices/soc0/soc_id
|
||||
# and cross-reference with Qualcomm product documentation.
|
||||
if(NOT DSP_VERSION)
|
||||
set(DSP_VERSION "v73")
|
||||
message(STATUS "DSP_VERSION not set; defaulting to ${DSP_VERSION}. "
|
||||
"Override with -DDSP_VERSION=v75 etc.")
|
||||
endif()
|
||||
|
||||
add_library(hexdsp_offload_skel SHARED
|
||||
# qaic-generated skel dispatch layer
|
||||
${CMAKE_CURRENT_BINARY_DIR}/hexdsp_offload_skel
|
||||
# DSP-side compute implementation (student fills this in)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hexdsp_offload_imp
|
||||
)
|
||||
|
||||
build_idl(inc/hexdsp_offload.idl hexdsp_offload_skel)
|
||||
|
||||
include_directories(${common_incs})
|
||||
|
||||
# copy_binaries puts the .so into the SDK ship/ staging directory.
|
||||
copy_binaries(hexdsp_offload_skel)
|
||||
|
||||
endif()
|
||||
@@ -1,427 +0,0 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Hexagon DSP Offload Template
|
||||
|
||||
This directory is a starting point for offloading computation from the OAI ARM
|
||||
cores to the Qualcomm Hexagon DSP on the DragonWing IQ-9075.
|
||||
|
||||
The pattern is the same as `libldpc.so` or `libdfts.so`: OAI loads a shared
|
||||
library at runtime via `dlopen`, calls functions through typed function
|
||||
pointers, and is unaware of how the work is performed inside. Here the .so
|
||||
is a thin glue layer on the ARM side; the actual computation runs on the
|
||||
Compute DSP (cDSP) via Qualcomm's **FastRPC** mechanism.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
---
|
||||
|
||||
## 1 How FastRPC works
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARM cores (aarch64, Linux/HLOS) │
|
||||
│ │
|
||||
│ OAI process │
|
||||
│ ├─ dlopen("libhexdsp_offload.so") │
|
||||
│ └─ hexdsp_offload_process(in, out) │
|
||||
│ │ │
|
||||
│ │ 1. (ION buffers pre-allocated in hexdsp_offload_init) │
|
||||
│ │ 2. memcpy(in → g_rpc_in) │
|
||||
│ │ 3. hexdsp_offload_run() ────── FastRPC syscall ──► │
|
||||
│ │ │ │
|
||||
│ libhexdsp_offload.so │ │
|
||||
│ ├─ hexdsp_offload_stub.c (qaic-generated marshalling) │ │
|
||||
│ └─ hexdsp_offload_arm.c (rpcmem + OAI glue) │ │
|
||||
└────────────────────────────────────────────────────────────────│────┘
|
||||
│
|
||||
kernel: ion_map / smmu_map │
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Hexagon cDSP (QuRT RTOS, protected domain) │
|
||||
│ │
|
||||
│ FastRPC dispatcher │
|
||||
│ └─ unmarshal args → call hexdsp_offload_process() │
|
||||
│ │
|
||||
│ libhexdsp_offload_skel.so (loaded from /vendor/lib/rfsa/adsp/) │
|
||||
│ ├─ hexdsp_offload_skel.c (qaic-generated dispatch) │
|
||||
│ └─ hexdsp_offload_imp.c (your compute kernel — fill this in) │
|
||||
│ │
|
||||
│ 4. compute on in_buf[], write out_buf[] │
|
||||
│ 5. return 0 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
kernel: flush cache / unmap │
|
||||
▼
|
||||
ARM side resumes:
|
||||
│ 6. memcpy(g_rpc_out → out)
|
||||
│ (ION buffers remain allocated for the next call)
|
||||
```
|
||||
|
||||
### 1.1 The IDL and qaic
|
||||
|
||||
The interface between ARM and DSP is declared in a single `.idl` file
|
||||
(`inc/hexdsp_offload.idl`). The SDK tool `qaic` compiles it into three
|
||||
files that are never hand-edited:
|
||||
|
||||
| Generated file | Used by | Purpose |
|
||||
|---------------------------|----------|----------------------------------------------|
|
||||
| `hexdsp_offload_stub.c` | ARM .so | Marshals arguments into a FastRPC message |
|
||||
| `hexdsp_offload_skel.c` | DSP .so | Unmarshals the message, calls `_imp.c` |
|
||||
| `hexdsp_offload.h` | both | C function prototypes visible on both sides |
|
||||
|
||||
The IDL method is named `run` (generating `hexdsp_offload_run()`) to avoid a
|
||||
name collision with the public ARM API function `hexdsp_offload_process()`
|
||||
that OAI resolves via `dlsym`. Both live in the same `.so`; identical names
|
||||
would cause the ARM glue to call itself recursively.
|
||||
|
||||
IDL types map to C as follows:
|
||||
|
||||
| IDL type | C type (ARM stub) | C type (DSP skel) |
|
||||
|---------------------|---------------------------------|--------------------------|
|
||||
| `in sequence<uint8>`| `const uint8_t *, int len` | `const uint8_t *, int` |
|
||||
| `rout sequence<uint8>`| `uint8_t *, int len` | `uint8_t *, int` |
|
||||
| `long` | `int` | `int` |
|
||||
| `remote_handle64` | opaque handle (open/close) | `remote_handle64` |
|
||||
|
||||
`in` means ARM → DSP (read-only on DSP). `rout` means DSP → ARM
|
||||
(written by DSP, read by ARM after the call returns).
|
||||
|
||||
### 1.2 Memory: ION and rpcmem
|
||||
|
||||
The DSP has its own MMU (SMMU). Ordinary `malloc` buffers on the ARM side
|
||||
are **not visible** to the DSP. Data must be in an ION buffer — a
|
||||
physically-contiguous allocation that both the ARM MMU and the DSP SMMU can
|
||||
map.
|
||||
|
||||
`rpcmem_alloc()` (from the Hexagon SDK) handles this transparently:
|
||||
|
||||
```c
|
||||
uint8_t *buf = rpcmem_alloc(RPCMEM_HEAP_ID_SYSTEM, RPCMEM_DEFAULT_FLAGS, size);
|
||||
// use buf normally from ARM ...
|
||||
// FastRPC maps it into DSP address space automatically when passed as
|
||||
// an in/rout sequence argument.
|
||||
rpcmem_free(buf);
|
||||
```
|
||||
|
||||
If you pass a plain `malloc` buffer to a FastRPC call the kernel will make
|
||||
an extra copy; for large blocks (e.g. a full LDPC codeblock) this kills
|
||||
performance. The ARM glue in `hexdsp_offload_arm.c` therefore allocates
|
||||
rpcmem bounce buffers internally and copies once on each side, keeping the
|
||||
OAI caller agnostic of this requirement.
|
||||
|
||||
For zero-copy operation the caller can allocate rpcmem directly and skip
|
||||
the bounce; this requires exposing `rpcmem_alloc` to the OAI layer.
|
||||
|
||||
### 1.3 FastRPC domains
|
||||
|
||||
The SA9000P has several DSP subsystems. For compute offload the relevant
|
||||
one is the **cDSP** (Compute DSP). The domain is encoded in the URI passed
|
||||
to `hexdsp_offload_open()`:
|
||||
|
||||
```c
|
||||
const char *uri = hexdsp_offload_URI "&_dom=cdsp";
|
||||
```
|
||||
|
||||
`hexdsp_offload_URI` is a string constant generated by `qaic` from the
|
||||
interface name. Use the string form `"&_dom=cdsp"` rather than `"&_dom=3"`;
|
||||
the named form is more readable and robust across SDK versions.
|
||||
|
||||
### 1.4 Init ordering and unsigned-PD mode
|
||||
|
||||
Two ordering constraints apply on Linux HLOS with fastrpc ≥ 1.0.4:
|
||||
|
||||
1. **Unsigned-PD mode** must be enabled *before* `hexdsp_offload_open()`.
|
||||
By default, fastrpc uses the secure cDSP domain which requires a signed
|
||||
skel library. Custom skels (not signed by Qualcomm) need unsigned-PD mode:
|
||||
|
||||
```c
|
||||
struct remote_rpc_control_unsigned_module um = { .domain = 3, .enable = 1 };
|
||||
remote_session_control(DSPRPC_CONTROL_UNSIGNED_MODULE, &um, sizeof(um));
|
||||
// then: hexdsp_offload_open(uri, &handle);
|
||||
```
|
||||
|
||||
Include `remote.h` from `${HEXAGON_SDK_ROOT}/incs/`.
|
||||
|
||||
2. **`rpcmem_init()` must be called before `hexdsp_offload_open()`.**
|
||||
`hexdsp_offload_open()` triggers a DMA transfer of the FastRPC shell
|
||||
(`fastrpc_shell_unsigned_3`, ~1.26 MB) to the DSP. The DMA heap
|
||||
(`/dev/dma_heap/system`) must already be open for this to succeed.
|
||||
|
||||
### 1.5 rpcmem pre-allocation
|
||||
|
||||
`rpcmem_alloc` / `rpcmem_free` on every `hexdsp_offload_process()` call
|
||||
exhausts the rpcmem pool within a few hundred invocations (e.g. in a
|
||||
per-codeword test loop). The correct pattern is to pre-allocate ION bounce
|
||||
buffers once in `hexdsp_offload_init()` and reuse them across all calls.
|
||||
|
||||
Do **not** link `rpcmem.a` from the Hexagon SDK. It contains only no-op
|
||||
stubs for `rpcmem_init` / `rpcmem_deinit` that shadow the real
|
||||
implementations in `libcdsprpc.so`, leaving the DMA heap uninitialised so
|
||||
`rpcmem_alloc()` fails silently. All rpcmem symbols are provided by the
|
||||
device's `libcdsprpc.so` at runtime.
|
||||
|
||||
### 1.4 The skel library on the device
|
||||
|
||||
The DSP side (`libhexdsp_offload_skel.so`) is a Hexagon shared library
|
||||
loaded by the FastRPC runtime from a fixed search path on the device:
|
||||
|
||||
```
|
||||
/vendor/lib/rfsa/adsp/
|
||||
```
|
||||
|
||||
The ARM stub looks for it there automatically; no path configuration is
|
||||
needed. The library runs inside a **protected domain** (PD) on the cDSP —
|
||||
a sandboxed QuRT process. A crash in the skel library does not crash the
|
||||
ARM process; FastRPC returns an error code.
|
||||
|
||||
---
|
||||
|
||||
## 2 Directory structure
|
||||
|
||||
```
|
||||
hexagon_offload_template/
|
||||
├── CMakeLists.txt ← single file, builds ARM stub or DSP skel
|
||||
├── inc/
|
||||
│ └── hexdsp_offload.idl ← interface definition (qaic input)
|
||||
└── src/
|
||||
├── hexdsp_offload_arm.c ← ARM glue: rpcmem bounce + OAI dlopen exports
|
||||
└── hexdsp_offload_imp.c ← DSP compute kernel (fill this in)
|
||||
```
|
||||
|
||||
### 2.1 `inc/hexdsp_offload.idl`
|
||||
|
||||
Declares the `process()` method. Edit this to add new methods or change
|
||||
the argument types. Run `qaic` (or let cmake do it via `build_idl()`) after
|
||||
any change.
|
||||
|
||||
### 2.2 `src/hexdsp_offload_arm.c`
|
||||
|
||||
Compiled into **`libhexdsp_offload.so`** together with the qaic-generated
|
||||
`hexdsp_offload_stub.c`. Exports three symbols that OAI resolves via
|
||||
`dlsym`:
|
||||
|
||||
| Symbol | Called by OAI when |
|
||||
|--------------------------|-----------------------------|
|
||||
| `hexdsp_offload_init` | module is first loaded |
|
||||
| `hexdsp_offload_shutdown`| OAI exits or unloads module |
|
||||
| `hexdsp_offload_process` | each compute invocation |
|
||||
|
||||
`hexdsp_offload_init` is idempotent: if a test harness calls it before every
|
||||
work unit, the first call opens the session and pre-allocates ION buffers;
|
||||
subsequent calls return 0 immediately (guarded by `dsp_handle != -1`).
|
||||
|
||||
### 2.3 `src/hexdsp_offload_imp.c`
|
||||
|
||||
Compiled into **`libhexdsp_offload_skel.so`** with hexagon-clang. This is
|
||||
the file a student replaces with the actual Hexagon kernel. It has access
|
||||
to:
|
||||
|
||||
- **HAP_farf** — DSP-side logging (`FARF(RUNTIME_HIGH, "...")`), visible
|
||||
via `adb logcat -s adsprpc`
|
||||
- **HVX intrinsics** — 128-byte SIMD vectors; enable with
|
||||
`-mhvx -mhvx-length=128B` and `#include <hexagon_types.h>`
|
||||
- **HAP_mem** — DSP heap if local scratch is needed
|
||||
- **QuRT** — threading, mutexes, timers (advanced use)
|
||||
|
||||
---
|
||||
|
||||
## 3 Build
|
||||
|
||||
Both builds are invoked from the OAI repository root. They are independent
|
||||
and can run in any order.
|
||||
|
||||
### 3.1 Prerequisites
|
||||
|
||||
```shell
|
||||
# Hexagon SDK (already installed for DragonWing ARM build)
|
||||
source /opt/Hexagon_SDK/6.4.0.2/setup_sdk_env.source
|
||||
```
|
||||
|
||||
### 3.2 ARM stub (libhexdsp_offload.so)
|
||||
|
||||
Cross-compiled with the DragonWing ARM toolchain.
|
||||
|
||||
```shell
|
||||
cmake -B build-hexdsp-arm \
|
||||
-DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake_targets/cross-arm-dragonwing.cmake \
|
||||
-DOS_TYPE=HLOS \
|
||||
common/utils/hexagon_offload_template
|
||||
cmake --build build-hexdsp-arm
|
||||
```
|
||||
|
||||
Output: `build-hexdsp-arm/libhexdsp_offload.so`
|
||||
|
||||
### 3.3 DSP skel (libhexdsp_offload_skel.so)
|
||||
|
||||
Compiled with hexagon-clang targeting the cDSP instruction set.
|
||||
|
||||
```shell
|
||||
cmake -B build-hexdsp-dsp \
|
||||
-DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/opt/Hexagon_SDK/6.4.0.2/build/cmake/hexagon_toolchain.cmake \
|
||||
-DDSP_VERSION=v73 \
|
||||
common/utils/hexagon_offload_template
|
||||
cmake --build build-hexdsp-dsp
|
||||
```
|
||||
|
||||
Output: `build-hexdsp-dsp/libhexdsp_offload_skel.so`
|
||||
|
||||
> **DSP_VERSION:** must match the Hexagon ISA version of the cDSP on the
|
||||
> device. To find it:
|
||||
> ```shell
|
||||
> adb shell cat /sys/devices/soc0/soc_id
|
||||
> ```
|
||||
> Cross-reference the SoC ID with the Qualcomm product documentation.
|
||||
> Known values: `v73` (SM8550 era), `v75`, `v79`, `v81`.
|
||||
|
||||
### 3.4 Enabling HVX in the DSP build
|
||||
|
||||
Add the following to the DSP cmake invocation:
|
||||
|
||||
```shell
|
||||
-DCMAKE_C_FLAGS="-mhvx -mhvx-length=128B"
|
||||
```
|
||||
|
||||
Then in `hexdsp_offload_imp.c`:
|
||||
|
||||
```c
|
||||
#include <hexagon_types.h>
|
||||
|
||||
// HVX_Vector is 128 bytes wide. Pointers must be 128-byte aligned.
|
||||
HVX_Vector *vin = (HVX_Vector *)in_buf;
|
||||
HVX_Vector *vout = (HVX_Vector *)out_buf;
|
||||
int nvec = in_bufLen / sizeof(HVX_Vector);
|
||||
for (int i = 0; i < nvec; i++)
|
||||
vout[i] = Q6_Vub_vsat_VhVh(vin[i], vin[i]); // example: saturate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4 Deploy
|
||||
|
||||
```shell
|
||||
# ARM stub: alongside the other OAI shared libs
|
||||
adb push build-hexdsp-arm/libhexdsp_offload.so /data/oai/lib/
|
||||
|
||||
# DSP skel: FastRPC searches this path automatically
|
||||
adb shell mkdir -p /vendor/lib/rfsa/adsp
|
||||
adb push build-hexdsp-dsp/libhexdsp_offload_skel.so /vendor/lib/rfsa/adsp/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5 OAI integration
|
||||
|
||||
OAI's `load_module_shlib` infrastructure (`common/utils/load_module_shlib.c`)
|
||||
loads offload libraries at runtime using `dlopen` + `dlsym`. To wire in
|
||||
the hexdsp offload, add a loader similar to `nrLDPC_load.c`:
|
||||
|
||||
```c
|
||||
#include "load_module_shlib.h"
|
||||
|
||||
// Function pointer types matching hexdsp_offload_arm.c exports
|
||||
typedef int (*hexdsp_init_f)(void);
|
||||
typedef int (*hexdsp_shutdown_f)(void);
|
||||
typedef int (*hexdsp_process_f)(const uint8_t *, uint32_t, uint8_t *, uint32_t);
|
||||
|
||||
typedef struct {
|
||||
hexdsp_init_f init;
|
||||
hexdsp_shutdown_f shutdown;
|
||||
hexdsp_process_f process;
|
||||
} hexdsp_interface_t;
|
||||
|
||||
int load_hexdsp(hexdsp_interface_t *itf)
|
||||
{
|
||||
loader_shlibfunc_t fdesc[] = {
|
||||
{.fname = "hexdsp_offload_init"},
|
||||
{.fname = "hexdsp_offload_shutdown"},
|
||||
{.fname = "hexdsp_offload_process"},
|
||||
};
|
||||
int ret = load_module_version_shlib("hexdsp_offload", "", fdesc,
|
||||
sizeofArray(fdesc), NULL);
|
||||
if (ret) return ret;
|
||||
itf->init = (hexdsp_init_f) fdesc[0].fptr;
|
||||
itf->shutdown = (hexdsp_shutdown_f)fdesc[1].fptr;
|
||||
itf->process = (hexdsp_process_f) fdesc[2].fptr;
|
||||
return itf->init();
|
||||
}
|
||||
```
|
||||
|
||||
The loader searches for `libhexdsp_offload.so` on `LD_LIBRARY_PATH` or the
|
||||
path configured under `loader.hexdsp_offload.shlibpath` in the OAI config
|
||||
file.
|
||||
|
||||
---
|
||||
|
||||
## 6 Adapting the template
|
||||
|
||||
To implement a specific kernel (e.g. LDPC decoding):
|
||||
|
||||
1. **Rename** — replace `hexdsp_offload` with your module name throughout
|
||||
(IDL, source files, CMakeLists.txt).
|
||||
|
||||
2. **Extend the IDL** — add methods matching the ARM function signatures OAI
|
||||
expects. Each `in sequence<T>` argument becomes a `(const T*, int)` pair
|
||||
in C. Each `rout sequence<T>` becomes a `(T*, int)` pair.
|
||||
|
||||
3. **ARM glue** — update `hexdsp_offload_arm.c`:
|
||||
- Set `HEXDSP_MAX_IN_LEN` / `HEXDSP_MAX_OUT_LEN` to the maximum buffer
|
||||
sizes your workload needs.
|
||||
- Call `remote_session_control(DSPRPC_CONTROL_UNSIGNED_MODULE)` before
|
||||
`hexdsp_offload_open()`.
|
||||
- Call `rpcmem_init()` before `hexdsp_offload_open()`.
|
||||
- Do not link `rpcmem.a`; all rpcmem symbols come from `libcdsprpc.so`.
|
||||
- The IDL method and the public API function must have different names to
|
||||
avoid a recursive call (the IDL uses `run`, the public API uses `process`).
|
||||
|
||||
4. **DSP kernel** — implement the compute in `hexdsp_offload_imp.c`. Start
|
||||
scalar, verify correctness, then vectorise with HVX.
|
||||
|
||||
5. **OAI loader** — write a `load_<module>.c` following the pattern in §5
|
||||
above, using the same function-pointer types OAI already uses for LDPC or
|
||||
DFT offload.
|
||||
|
||||
---
|
||||
|
||||
## 7 Debugging
|
||||
|
||||
### 7.1 DSP logs
|
||||
|
||||
`FARF()` messages from the skel library appear in the Android/Linux log:
|
||||
|
||||
```shell
|
||||
adb logcat -s adsprpc
|
||||
```
|
||||
|
||||
Log levels: `RUNTIME_HIGH` (always on), `HIGH`, `MED`, `LOW`.
|
||||
|
||||
### 7.2 FastRPC error codes
|
||||
|
||||
Negative return values from `hexdsp_offload_open()` or `_process()` are
|
||||
FastRPC error codes. Common ones:
|
||||
|
||||
| Code | Meaning |
|
||||
|--------|--------------------------------------------------------------|
|
||||
| `-1` | Generic failure (check logcat for detail) |
|
||||
| `3` | Skel library not found in `/vendor/lib/rfsa/adsp/` |
|
||||
| `6` | DSP subsystem not available or not powered |
|
||||
| `-14` | Bad address — buffer not allocated with `rpcmem_alloc` |
|
||||
|
||||
### 7.3 Simulator
|
||||
|
||||
The DSP skel can be tested on the build host without a device using the
|
||||
Hexagon simulator:
|
||||
|
||||
```shell
|
||||
# Build with the simulator target (no device needed)
|
||||
cmake -B build-hexdsp-sim \
|
||||
-DHEXAGON_SDK_ROOT=/opt/Hexagon_SDK/6.4.0.2 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/opt/Hexagon_SDK/6.4.0.2/build/cmake/hexagon_toolchain.cmake \
|
||||
-DDSP_VERSION=v73 \
|
||||
-DNO_QURT_INC=1 \
|
||||
common/utils/hexagon_offload_template
|
||||
cmake --build build-hexdsp-sim
|
||||
# The SDK's hexagon_fun.cmake adds a runHexagonSim() target automatically.
|
||||
cmake --build build-hexdsp-sim --target run
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
// SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
//
|
||||
// hexdsp_offload.idl — FastRPC interface between ARM host and Hexagon DSP.
|
||||
//
|
||||
// qaic compiles this into:
|
||||
// hexdsp_offload_stub.c (linked into the ARM .so)
|
||||
// hexdsp_offload_skel.c (linked into the DSP .so)
|
||||
// hexdsp_offload.h (shared header, included by both sides)
|
||||
//
|
||||
// Naming rule: every function below becomes hexdsp_offload_<name>() in C.
|
||||
//
|
||||
// The method is named "run" (not "process") to avoid a name collision with
|
||||
// the public ARM API function hexdsp_offload_process() that OAI resolves via
|
||||
// dlsym. Both live in the same .so; identical names would cause the ARM glue
|
||||
// to call itself recursively instead of dispatching to the DSP.
|
||||
|
||||
#include "AEEStdDef.idl"
|
||||
#include "remote.idl"
|
||||
|
||||
// IDL_VERSION is checked at runtime when stub and skel versions are mismatched.
|
||||
const string IDL_VERSION = "1.0.0";
|
||||
|
||||
interface hexdsp_offload : remote_handle64 {
|
||||
|
||||
// Run one block of computation on the DSP.
|
||||
//
|
||||
// in_buf — input data, must be allocated with rpcmem_alloc() on ARM.
|
||||
// out_buf — output data, must be allocated with rpcmem_alloc() on ARM.
|
||||
//
|
||||
// The DSP receives physically-contiguous ION buffers via DMA mapping;
|
||||
// they arrive as ordinary pointers in hexdsp_offload_imp.c.
|
||||
//
|
||||
// Returns 0 on success, negative errno on failure.
|
||||
long run(in sequence<uint8> in_buf,
|
||||
rout sequence<uint8> out_buf);
|
||||
};
|
||||
@@ -1,150 +0,0 @@
|
||||
// SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
//
|
||||
// hexdsp_offload_arm.c — ARM-side glue library.
|
||||
//
|
||||
// This .c file is compiled into libhexdsp_offload.so (with the qaic-generated
|
||||
// hexdsp_offload_stub.c). OAI loads it at runtime via load_module_shlib /
|
||||
// dlopen, resolving the three symbols below:
|
||||
//
|
||||
// hexdsp_offload_init() — open FastRPC session, initialise rpcmem
|
||||
// hexdsp_offload_shutdown() — close session, release rpcmem
|
||||
// hexdsp_offload_process() — copy to ION buffers, call DSP, copy result back
|
||||
//
|
||||
// The caller owns the in/out memory; this glue handles the rpcmem bounce
|
||||
// buffers internally so the DSP always sees physically-contiguous ION memory
|
||||
// regardless of how OAI allocated the working buffers.
|
||||
//
|
||||
// Validated on DragonWing SA9000P / IQ-9075 with fastrpc 1.0.4 (Linux HLOS).
|
||||
//
|
||||
// FastRPC docs: Hexagon SDK → ipc/fastrpc/
|
||||
// RPCMEM docs: Hexagon SDK → ipc/fastrpc/rpcmem/
|
||||
|
||||
#include "hexdsp_offload.h" // generated by qaic from hexdsp_offload.idl
|
||||
#include "remote.h" // DSPRPC_CONTROL_UNSIGNED_MODULE, remote_session_control
|
||||
#include "rpcmem.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Maximum buffer sizes for the pre-allocated ION bounce buffers.
|
||||
// Set these to the largest buffers your workload will ever pass in a single
|
||||
// hexdsp_offload_process() call. Pre-allocating at init time avoids
|
||||
// per-call rpcmem_alloc/free cycles that exhaust the pool on high-call-rate
|
||||
// workloads (e.g. per-codeword LDPC decode loops).
|
||||
#define HEXDSP_MAX_IN_LEN (64 * 1024) // TODO: adjust for your workload
|
||||
#define HEXDSP_MAX_OUT_LEN (64 * 1024) // TODO: adjust for your workload
|
||||
|
||||
static remote_handle64 dsp_handle = (remote_handle64)-1;
|
||||
static int rpcmem_ready = 0;
|
||||
static uint8_t *g_rpc_in = NULL;
|
||||
static uint8_t *g_rpc_out = NULL;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// hexdsp_offload_init — open FastRPC session and pre-allocate ION buffers.
|
||||
// ---------------------------------------------------------------------------
|
||||
int hexdsp_offload_init(void)
|
||||
{
|
||||
// Guard: test harnesses often call init() before every work unit to reset
|
||||
// state. Re-opening the session and re-allocating ION buffers on every
|
||||
// call exhausts the rpcmem pool within a few hundred invocations.
|
||||
if (dsp_handle != (remote_handle64)-1)
|
||||
return 0;
|
||||
|
||||
// On Linux HLOS with fastrpc >= 1.0.4 the secure cDSP device is used by
|
||||
// default, which requires signed skels. Enable unsigned-PD mode so that
|
||||
// custom skels can be loaded without a hardware signature.
|
||||
// MUST be called before the first remote_handle64_open() call.
|
||||
struct remote_rpc_control_unsigned_module um = { .domain = 3, .enable = 1 };
|
||||
int rc = remote_session_control(DSPRPC_CONTROL_UNSIGNED_MODULE, &um, sizeof(um));
|
||||
if (rc)
|
||||
fprintf(stderr, "hexdsp_offload: remote_session_control(UNSIGNED_MODULE) returned %d (continuing)\n", rc);
|
||||
|
||||
// rpcmem_init() opens /dev/dma_heap/system so libcdsprpc.so can DMA-transfer
|
||||
// the FastRPC shell to the DSP during remote_handle64_open() below.
|
||||
// MUST be called before hexdsp_offload_open().
|
||||
// NOTE: rpcmem.a must NOT be linked (see CMakeLists.txt); rpcmem symbols
|
||||
// come from the device's libcdsprpc.so.
|
||||
rpcmem_init();
|
||||
rpcmem_ready = 1;
|
||||
|
||||
// "&_dom=cdsp" selects the Compute DSP on Linux HLOS (fastrpc >= 1.0.4).
|
||||
// Use the string form rather than "&_dom=3" — more readable and robust
|
||||
// across SDK versions.
|
||||
const char *uri = hexdsp_offload_URI "&_dom=cdsp";
|
||||
|
||||
int err = hexdsp_offload_open(uri, &dsp_handle);
|
||||
if (err) {
|
||||
fprintf(stderr, "hexdsp_offload_open failed: %d\n", err);
|
||||
rpcmem_deinit();
|
||||
rpcmem_ready = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Pre-allocate ION-backed bounce buffers once; reuse across all process() calls.
|
||||
g_rpc_in = rpcmem_alloc(RPCMEM_HEAP_ID_SYSTEM, RPCMEM_DEFAULT_FLAGS, HEXDSP_MAX_IN_LEN);
|
||||
g_rpc_out = rpcmem_alloc(RPCMEM_HEAP_ID_SYSTEM, RPCMEM_DEFAULT_FLAGS, HEXDSP_MAX_OUT_LEN);
|
||||
if (!g_rpc_in || !g_rpc_out) {
|
||||
fprintf(stderr, "hexdsp_offload: rpcmem_alloc for pre-allocated buffers failed\n");
|
||||
if (g_rpc_in) { rpcmem_free(g_rpc_in); g_rpc_in = NULL; }
|
||||
if (g_rpc_out) { rpcmem_free(g_rpc_out); g_rpc_out = NULL; }
|
||||
hexdsp_offload_close(dsp_handle);
|
||||
dsp_handle = (remote_handle64)-1;
|
||||
rpcmem_deinit();
|
||||
rpcmem_ready = 0;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// hexdsp_offload_shutdown — tear down.
|
||||
// ---------------------------------------------------------------------------
|
||||
int hexdsp_offload_shutdown(void)
|
||||
{
|
||||
if (dsp_handle != (remote_handle64)-1) {
|
||||
hexdsp_offload_close(dsp_handle);
|
||||
dsp_handle = (remote_handle64)-1;
|
||||
}
|
||||
if (g_rpc_in) { rpcmem_free(g_rpc_in); g_rpc_in = NULL; }
|
||||
if (g_rpc_out) { rpcmem_free(g_rpc_out); g_rpc_out = NULL; }
|
||||
if (rpcmem_ready) {
|
||||
rpcmem_deinit();
|
||||
rpcmem_ready = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// hexdsp_offload_process — main offload entry point.
|
||||
//
|
||||
// in/out are OAI-allocated buffers (e.g. stack or malloc). We copy through
|
||||
// pre-allocated ION bounce buffers so FastRPC can DMA-map them to the DSP
|
||||
// without an extra kernel copy. The OAI caller sees ordinary pointers.
|
||||
// ---------------------------------------------------------------------------
|
||||
int hexdsp_offload_process(const uint8_t *in, uint32_t in_len,
|
||||
uint8_t *out, uint32_t out_len)
|
||||
{
|
||||
if (!g_rpc_in || !g_rpc_out) {
|
||||
fprintf(stderr, "hexdsp_offload: buffers not ready — call hexdsp_offload_init() first\n");
|
||||
return -1;
|
||||
}
|
||||
if ((int)in_len > HEXDSP_MAX_IN_LEN || (int)out_len > HEXDSP_MAX_OUT_LEN) {
|
||||
fprintf(stderr, "hexdsp_offload: request too large (in=%u max=%d, out=%u max=%d)\n",
|
||||
in_len, HEXDSP_MAX_IN_LEN, out_len, HEXDSP_MAX_OUT_LEN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(g_rpc_in, in, in_len);
|
||||
|
||||
// FastRPC call: DMA-maps g_rpc_in/g_rpc_out to the DSP address space.
|
||||
int err = hexdsp_offload_run(dsp_handle,
|
||||
g_rpc_in, (int)in_len,
|
||||
g_rpc_out, (int)out_len);
|
||||
if (err) {
|
||||
fprintf(stderr, "hexdsp_offload_run RPC failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
memcpy(out, g_rpc_out, out_len);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
//
|
||||
// hexdsp_offload_imp.c — DSP-side skeleton implementation.
|
||||
//
|
||||
// This file is compiled with hexagon-clang and loaded by FastRPC into the
|
||||
// Compute DSP (cDSP) protected domain at runtime. The ARM stub calls
|
||||
// hexdsp_offload_process(); FastRPC unmarshalls the call and invokes
|
||||
// hexdsp_offload_run() here on the DSP.
|
||||
//
|
||||
// Useful DSP headers:
|
||||
// HAP_farf.h — logging (FARF macro, visible via: adb logcat -s adsprpc)
|
||||
// HAP_mem.h — DSP-side heap allocation
|
||||
// hexagon_types.h / hexagon_protos.h — HVX intrinsics
|
||||
//
|
||||
// To enable HVX vector processing, compile with:
|
||||
// -mhvx -mhvx-length=128B
|
||||
// and #include <hexagon_types.h>.
|
||||
|
||||
#include "hexdsp_offload.h" // generated by qaic
|
||||
#include "HAP_farf.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Session lifecycle — called by FastRPC on remote_handle64 open/close.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int hexdsp_offload_open(const char *uri, remote_handle64 *handle)
|
||||
{
|
||||
// Allocate a small context token; the value is opaque to FastRPC.
|
||||
void *ctx = malloc(sizeof(int));
|
||||
if (!ctx)
|
||||
return -1;
|
||||
*handle = (remote_handle64)(uintptr_t)ctx;
|
||||
FARF(RUNTIME_HIGH, "hexdsp_offload: session opened (handle=%p)", ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hexdsp_offload_close(remote_handle64 handle)
|
||||
{
|
||||
free((void *)(uintptr_t)handle);
|
||||
FARF(RUNTIME_HIGH, "hexdsp_offload: session closed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main compute entry point.
|
||||
//
|
||||
// in_buf / out_buf are DMA-mapped by FastRPC from the ARM rpcmem buffers;
|
||||
// they arrive as ordinary pointers here — no extra mapping needed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int hexdsp_offload_run(remote_handle64 handle,
|
||||
const uint8_t *in_buf, int in_bufLen,
|
||||
uint8_t *out_buf, int out_bufLen)
|
||||
{
|
||||
FARF(RUNTIME_HIGH,
|
||||
"hexdsp_offload_run: in=%d out=%d bytes", in_bufLen, out_bufLen);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// TODO: replace this passthrough with the actual Hexagon kernel.
|
||||
//
|
||||
// Scalar example:
|
||||
// for (int i = 0; i < out_bufLen && i < in_bufLen; i++)
|
||||
// out_buf[i] = in_buf[i] ^ 0xff;
|
||||
//
|
||||
// HVX vector example (requires -mhvx -mhvx-length=128B):
|
||||
// #include <hexagon_types.h>
|
||||
// HVX_Vector *vin = (HVX_Vector *)in_buf;
|
||||
// HVX_Vector *vout = (HVX_Vector *)out_buf;
|
||||
// int nvec = in_bufLen / VLEN; // VLEN = 128 for 128B HVX
|
||||
// for (int i = 0; i < nvec; i++)
|
||||
// vout[i] = Q6_V_vnot_V(vin[i]); // bitwise NOT as placeholder
|
||||
// -----------------------------------------------------------------------
|
||||
int len = (out_bufLen < in_bufLen) ? out_bufLen : in_bufLen;
|
||||
memcpy(out_buf, in_buf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
|
||||
@@ -9,3 +9,7 @@ add_executable(test_fsn test_fsn.cpp)
|
||||
add_dependencies(tests test_fsn)
|
||||
target_link_libraries(test_fsn PRIVATE utils GTest::gtest)
|
||||
add_test(NAME test_fsn COMMAND ./test_fsn)
|
||||
|
||||
add_executable(test_bits test_bits.c)
|
||||
add_dependencies(tests test_bits)
|
||||
add_test(NAME test_bits COMMAND ./test_bits)
|
||||
|
||||
224
common/utils/tests/test_bits.c
Normal file
224
common/utils/tests/test_bits.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <bits.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ── test harness ── */
|
||||
static int g_failures = 0;
|
||||
#define ARRSIZE 9
|
||||
#define BITS_PER_WORD 32
|
||||
#define TOTAL_BITS (ARRSIZE * BITS_PER_WORD)
|
||||
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline void set_bit_in_array(uint32_t *arr, int bit_pos)
|
||||
{
|
||||
arr[bit_pos / BITS_PER_WORD] |= 1u << (bit_pos % BITS_PER_WORD);
|
||||
}
|
||||
|
||||
static void run_test(const char *name, int result, int expected)
|
||||
{
|
||||
if (result != expected) {
|
||||
fprintf(stderr, "FAIL [%s]: got %d, want %d\n", name, result, expected);
|
||||
g_failures++;
|
||||
} else {
|
||||
printf("PASS [%s]\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_get_first_bit_index_mask(void)
|
||||
{
|
||||
/* --- no bits set --- */
|
||||
uint32_t zeros[ARRSIZE] = {0};
|
||||
run_test("firstbit_all_zeros", get_first_bit_index_mask(zeros, ARRSIZE, 0, TOTAL_BITS), -1);
|
||||
|
||||
uint32_t arr[ARRSIZE];
|
||||
/* --- random single hit at a random position --- */
|
||||
int num_trials = 5;
|
||||
for (int t = 0; t < num_trials; t++) {
|
||||
int bit_pos = rand() % (TOTAL_BITS); /* random bit in [0, 128) */
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "firstbit_single_hit_bit%d", bit_pos);
|
||||
memset(arr, 0, sizeof(arr));
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
run_test(test_name, get_first_bit_index_mask(arr, ARRSIZE, 0, TOTAL_BITS), bit_pos);
|
||||
}
|
||||
|
||||
/* --- random multi-hit with random window --- */
|
||||
int multi_trials = 5;
|
||||
for (int t = 0; t < multi_trials; t++) {
|
||||
memset(arr, 0, sizeof(arr));
|
||||
int from_bit = rand() % (TOTAL_BITS);
|
||||
int num_bits = 1 + rand() % (TOTAL_BITS - from_bit); /* at least 1 bit wide */
|
||||
int window_end = from_bit + num_bits;
|
||||
int min_pos = -1;
|
||||
int num_bits_set = 5 + rand() % 20;
|
||||
for (int b = 0; b < num_bits_set; b++) {
|
||||
int bit_pos = from_bit + rand() % num_bits; /* confined to the window */
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
if (min_pos == -1 || bit_pos < min_pos)
|
||||
min_pos = bit_pos;
|
||||
}
|
||||
/* put some bits outside the window to test they are ignored in some trial */
|
||||
if (from_bit > 0 && t % 2) {
|
||||
int bit_pos = rand() % from_bit; /* before the window */
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
}
|
||||
if (window_end < TOTAL_BITS && t % 2) {
|
||||
int bit_pos = window_end + rand() % (TOTAL_BITS - window_end); /* after */
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
}
|
||||
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "firstbit_multi_hit_trial%d_from%d_num%d_min%d", t, from_bit, num_bits, min_pos);
|
||||
run_test(test_name, get_first_bit_index_mask(arr, ARRSIZE, from_bit, num_bits), min_pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_get_last_bit_index(void)
|
||||
{
|
||||
/* --- no bits set --- */
|
||||
uint32_t zeros[ARRSIZE] = {0};
|
||||
run_test("lastbit_all_zeros", get_last_bit_index(zeros, ARRSIZE), -1);
|
||||
|
||||
uint32_t arr[ARRSIZE];
|
||||
|
||||
/* --- random single hit at a random position --- */
|
||||
int num_trials = 5;
|
||||
for (int t = 0; t < num_trials; t++) {
|
||||
int bit_pos = rand() % (TOTAL_BITS);
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "lastbit_single_hit_bit%d", bit_pos);
|
||||
memset(arr, 0, sizeof(arr));
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
run_test(test_name, get_last_bit_index(arr, ARRSIZE), bit_pos);
|
||||
}
|
||||
|
||||
/* --- random multi-hit: scattered bits, must return the maximum --- */
|
||||
int multi_trials = 5;
|
||||
for (int t = 0; t < multi_trials; t++) {
|
||||
memset(arr, 0, sizeof(arr));
|
||||
int max_pos = -1;
|
||||
int num_bits_set = 5 + rand() % 20;
|
||||
for (int b = 0; b < num_bits_set; b++) {
|
||||
int bit_pos = rand() % (TOTAL_BITS);
|
||||
set_bit_in_array(arr, bit_pos);
|
||||
if (bit_pos > max_pos)
|
||||
max_pos = bit_pos;
|
||||
}
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "lastbit_multi_hit_trial%d_max%d", t, max_pos);
|
||||
run_test(test_name, get_last_bit_index(arr, ARRSIZE), max_pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void fisher_yates_shuffle(int *positions, int total, int n)
|
||||
{
|
||||
/* partial shuffle: pick n distinct random positions from [0, total) */
|
||||
/* see https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm */
|
||||
for (int i = 0; i < n; i++) {
|
||||
int j = i + rand() % (total - i);
|
||||
int tmp = positions[i];
|
||||
positions[i] = positions[j];
|
||||
positions[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_count_bits(void)
|
||||
{
|
||||
uint32_t arr[ARRSIZE];
|
||||
|
||||
/* --- no bits set --- */
|
||||
uint32_t zeros[ARRSIZE] = {0};
|
||||
run_test("countbits_all_zeros", count_bits(zeros, ARRSIZE), 0);
|
||||
|
||||
/* --- all bits set --- */
|
||||
uint32_t all_set[ARRSIZE];
|
||||
memset(all_set, 0xFF, sizeof(all_set));
|
||||
run_test("countbits_all_set", count_bits(all_set, ARRSIZE), TOTAL_BITS);
|
||||
|
||||
/* --- random trials: set exactly N random bits, expect count N --- */
|
||||
int positions[TOTAL_BITS];
|
||||
for (int i = 0; i < TOTAL_BITS; i++)
|
||||
positions[i] = i;
|
||||
|
||||
int num_trials = 5;
|
||||
for (int t = 0; t < num_trials; t++) {
|
||||
memset(arr, 0, sizeof(arr));
|
||||
int n = 1 + rand() % TOTAL_BITS;
|
||||
fisher_yates_shuffle(positions, TOTAL_BITS, n);
|
||||
for (int i = 0; i < n; i++)
|
||||
set_bit_in_array(arr, positions[i]);
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "countbits_trial%d_n%d", t, n);
|
||||
run_test(test_name, count_bits(arr, ARRSIZE), n);
|
||||
}
|
||||
}
|
||||
|
||||
void test_count_bits64_with_mask(void)
|
||||
{
|
||||
/* --- zero value --- */
|
||||
run_test("countbits64_zero_value", count_bits64_with_mask(0ULL, 0, 64), 0);
|
||||
|
||||
/* --- all bits set, full window --- */
|
||||
run_test("countbits64_all_set_full_window", count_bits64_with_mask(0xFFFFFFFFFFFFFFFFULL, 0, 64), 64);
|
||||
|
||||
/* --- all bits set, window filters correctly --- */
|
||||
run_test("countbits64_all_set_window_16_from_8", count_bits64_with_mask(0xFFFFFFFFFFFFFFFFULL, 8, 16), 16);
|
||||
|
||||
/* --- random trials: set exactly N bits within window, expect count N --- */
|
||||
int num_trials = 5;
|
||||
for (int t = 0; t < num_trials; t++) {
|
||||
int start = rand() % 64;
|
||||
int num = 1 + rand() % (64 - start);
|
||||
int window_end = start + num;
|
||||
|
||||
/* initialise positions to the window [start, start+num) */
|
||||
int positions[64];
|
||||
for (int i = 0; i < num; i++)
|
||||
positions[i] = start + i;
|
||||
|
||||
int n = 1 + rand() % num;
|
||||
fisher_yates_shuffle(positions, num, n);
|
||||
|
||||
uint64_t v = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
v |= 1ULL << positions[i];
|
||||
|
||||
/* put bits outside the window on odd trials */
|
||||
if (t % 2) {
|
||||
if (start > 0)
|
||||
v |= 1ULL << (rand() % start);
|
||||
if (window_end < 64)
|
||||
v |= 1ULL << (window_end + rand() % (64 - window_end));
|
||||
}
|
||||
|
||||
char test_name[64];
|
||||
snprintf(test_name, sizeof(test_name), "countbits64_trial%d_start%d_num%d_n%d", t, start, num, n);
|
||||
run_test(test_name, count_bits64_with_mask(v, start, num), n);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
srand((unsigned)time(NULL));
|
||||
test_get_first_bit_index_mask();
|
||||
test_get_last_bit_index();
|
||||
test_count_bits();
|
||||
test_count_bits64_with_mask();
|
||||
|
||||
if (g_failures > 0) {
|
||||
fprintf(stderr, "\n%d test(s) FAILED.\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -13,7 +13,7 @@ sudo apt-get update
|
||||
sudo apt-get install git
|
||||
```
|
||||
|
||||
## Clone the Git repository (for OAI Users without login to gitlab server)
|
||||
## Clone the Git repository (for OAI Users without login to github server)
|
||||
|
||||
The [openairinterface5g repository](https://github.com/duranta-project/openairinterface5g.git)
|
||||
holds the source code for the RAN (4G and 5G).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,718 +0,0 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Building OAI for Qualcomm DragonWing IQ-9/X (ARM)
|
||||
|
||||
This document covers cross-compiling OAI for the ARM cores of the Qualcomm
|
||||
DragonWing IQ-9/X platform (SA9000P SoC, Kryo 780 / Cortex-A78).
|
||||
The Hexagon DSP component is not covered here.
|
||||
|
||||
The toolchain file is `cmake_targets/cross-arm-dragonwing.cmake`, which uses the
|
||||
GCC cross-compiler bundled with the Hexagon SDK rather than the Ubuntu package.
|
||||
The build procedure otherwise follows the same two-step pattern as the generic
|
||||
ARM64 cross-compile described in `cross-compile.md`.
|
||||
|
||||
> **Note on the compiler:** The Hexagon SDK includes two compilers.
|
||||
> `hexagon-clang` (LLVM 19) targets the **Hexagon DSP only** and cannot produce
|
||||
> ARM binaries. The ARM cross-compiler is `aarch64-none-linux-gnu-gcc`
|
||||
> (Arm GNU Toolchain 11.3.1) located under
|
||||
> `tools/gcc_tools_64/bin/` in the SDK tree.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
---
|
||||
|
||||
## 1 Prerequisites on the build host
|
||||
|
||||
### 1.1 Hexagon SDK
|
||||
|
||||
The SDK must be installed, defaulting to `/opt/Hexagon_SDK/6.4.0.2`.
|
||||
Run the SDK's environment script once per shell session (or add to your profile):
|
||||
|
||||
```shell
|
||||
source /opt/Hexagon_SDK/6.4.0.2/setup_sdk_env.source
|
||||
```
|
||||
|
||||
If the SDK is installed elsewhere, pass `-DHEXAGON_SDK_ROOT=<path>` to every
|
||||
`cmake` invocation in this guide.
|
||||
|
||||
### 1.2 OAI native build dependencies
|
||||
|
||||
The host machine needs OAI's standard build tools. If not installed yet:
|
||||
|
||||
```shell
|
||||
cmake_targets/build_oai -I
|
||||
```
|
||||
|
||||
### 1.3 ARM64 target libraries
|
||||
|
||||
OAI links against several libraries that must be available for the `aarch64`
|
||||
architecture at link time. The standard way is Ubuntu multiarch.
|
||||
|
||||
> **Caveat:** The `dpkg --add-architecture` approach requires that your
|
||||
> apt sources serve `aarch64` packages. Ubuntu 22.04 / 24.04 on a standard
|
||||
> x86 host works out of the box via `ports.ubuntu.com`. If you are on a
|
||||
> non-standard host, inside a corporate mirror, or inside a container without
|
||||
> network access, you may need to adapt the sources list below — or provide
|
||||
> the libraries from the DragonWing device filesystem instead (see
|
||||
> [Section 4](#4-alternative-sysroot-from-device)).
|
||||
|
||||
#### 1.3.1 Enable the arm64 architecture in apt
|
||||
|
||||
**Ubuntu 24.04 (Noble)** — the sources are in `ubuntu.sources` format:
|
||||
|
||||
```shell
|
||||
sudo dpkg --add-architecture arm64
|
||||
|
||||
# Restrict the existing ubuntu.sources to amd64 only
|
||||
sudo sed -i '/^Components:/a Architectures: amd64' \
|
||||
/etc/apt/sources.list.d/ubuntu.sources
|
||||
|
||||
# Add a separate arm64 source pointing to ports.ubuntu.com
|
||||
sudo tee /etc/apt/sources.list.d/arm-cross-compile-sources.list <<'EOF'
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble main restricted
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble-updates main restricted
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble universe
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble-updates universe
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble multiverse
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble-updates multiverse
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ noble-backports main restricted universe multiverse
|
||||
EOF
|
||||
```
|
||||
|
||||
**Ubuntu 22.04 (Jammy)** — sources are still in the old `sources.list` format:
|
||||
|
||||
```shell
|
||||
sudo dpkg --add-architecture arm64
|
||||
|
||||
sudo cp /etc/apt/sources.list "/etc/apt/sources.list.$(date).backup"
|
||||
sudo sed -i -E "s/(deb)\ (http:.+)/\1\ [arch=amd64]\ \2/" \
|
||||
/etc/apt/sources.list
|
||||
|
||||
sudo tee /etc/apt/sources.list.d/arm-cross-compile-sources.list <<'EOF'
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy main restricted
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy-updates main restricted
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy universe
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy-updates universe
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy multiverse
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy-updates multiverse
|
||||
deb [arch=arm64] http://ports.ubuntu.com/ jammy-backports main restricted universe multiverse
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 1.3.2 Install the arm64 packages
|
||||
|
||||
```shell
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes \
|
||||
libc6-dev-i386 \
|
||||
libreadline-dev:arm64 \
|
||||
libgnutls28-dev:arm64 \
|
||||
libconfig-dev:arm64 \
|
||||
libsctp-dev:arm64 \
|
||||
libssl-dev:arm64 \
|
||||
libtool:arm64 \
|
||||
zlib1g-dev:arm64 \
|
||||
libyaml-cpp-dev:arm64
|
||||
```
|
||||
|
||||
> **Note:** `libc6-dev-i386` is for the host (code-generation tools), not the
|
||||
> target. All other packages with `:arm64` suffix are for the cross-linked
|
||||
> ARM binaries.
|
||||
|
||||
> The list above is believed to be complete but may grow as new OAI features
|
||||
> are enabled. If cmake reports a missing package, install `<package>:arm64`
|
||||
> and re-run cmake.
|
||||
|
||||
---
|
||||
|
||||
## 2 Build
|
||||
|
||||
Both build directories live directly under the OAI repository root.
|
||||
**Each directory must be created fresh** — `CMAKE_TOOLCHAIN_FILE` is only
|
||||
honoured on the very first cmake run in a directory. If a `CMakeCache.txt`
|
||||
already exists, cmake ignores the toolchain file and silently produces a
|
||||
native x86 build.
|
||||
|
||||
### 2.1 Step 1 — native host tools
|
||||
|
||||
These are x86 binaries that cmake runs during the cross-compile step to
|
||||
generate LDPC processing code and the T-tracer event IDs.
|
||||
|
||||
```shell
|
||||
cd <oai-root>
|
||||
|
||||
mkdir build # must not contain a prior CMakeCache.txt
|
||||
cd build
|
||||
cmake .. -DAVOID_SIGN=ON
|
||||
make -j$(nproc) ldpc_generators generate_T
|
||||
cd ..
|
||||
```
|
||||
|
||||
> **Note:** `-DAVOID_SIGN=ON` tells the LDPC code generator to emit
|
||||
> `simde_mm_xor_si128` instead of `simde_mm_sign_epi8` for sign extraction.
|
||||
> SIMDe's translation of `mm_sign_epi8` is incorrect for the MSB-only pattern
|
||||
> used in the CN processing kernel; `mm_xor_si128` is equivalent and
|
||||
> translates correctly on aarch64. This flag has no effect on Step 2.
|
||||
|
||||
### 2.2 Step 2 — cross-compile for DragonWing
|
||||
|
||||
```shell
|
||||
# Remove any previous attempt first — a stale CMakeCache.txt will cause
|
||||
# CMAKE_TOOLCHAIN_FILE to be ignored with a warning but no error.
|
||||
rm -rf dragonwing_build
|
||||
mkdir dragonwing_build
|
||||
cd dragonwing_build
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../cmake_targets/cross-arm-dragonwing.cmake \
|
||||
-DNATIVE_DIR=../build
|
||||
|
||||
# Example targets — add or remove as needed
|
||||
make -j$(nproc) nr-softmodem nr-cuup nr-uesoftmodem \
|
||||
params_libconfig coding rfsimulator
|
||||
```
|
||||
|
||||
If the Hexagon SDK is not at the default path, add:
|
||||
|
||||
```
|
||||
-DHEXAGON_SDK_ROOT=/your/path/to/Hexagon_SDK/6.4.0.2
|
||||
```
|
||||
|
||||
The `QUALCOMM_DRAGONWING=1` flag is set automatically by the toolchain file and
|
||||
causes CMake to use `-mcpu=cortex-a78` instead of the generic `-march=armv8.2-a`.
|
||||
|
||||
### 2.3 Deploy via adb
|
||||
|
||||
```shell
|
||||
# Verify the device is reachable
|
||||
adb devices
|
||||
|
||||
# Create a destination directory on the device
|
||||
adb shell mkdir -p /data/oai
|
||||
|
||||
# Push the binaries
|
||||
adb push dragonwing_build/nr-softmodem /data/oai/
|
||||
adb push dragonwing_build/nr-cuup /data/oai/
|
||||
adb push dragonwing_build/nr-uesoftmodem /data/oai/
|
||||
|
||||
# Push shared libraries that OAI loads at runtime
|
||||
adb push dragonwing_build/libparams_libconfig.so /data/oai/
|
||||
adb push dragonwing_build/libcoding.so /data/oai/
|
||||
adb push dragonwing_build/librfsimulator.so /data/oai/
|
||||
|
||||
# On the device, set LD_LIBRARY_PATH before running
|
||||
adb shell "export LD_LIBRARY_PATH=/data/oai && /data/oai/nr-softmodem --help"
|
||||
```
|
||||
|
||||
If the standard system libraries (libgnutls, libssl, libconfig, …) are not
|
||||
present on the device, they must be pushed alongside the OAI binaries:
|
||||
|
||||
```shell
|
||||
# Example: find and push the arm64 shared libs from the build host
|
||||
for lib in libgnutls libssl libcrypto libconfig libsctp; do
|
||||
find /usr/lib/aarch64-linux-gnu -name "${lib}.so*" -exec \
|
||||
adb push {} /data/oai/ \;
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3 Building for O-RAN FHI 7.2 (DPDK + libxran + armral)
|
||||
|
||||
This section covers cross-compiling the three additional libraries needed for
|
||||
the O-RAN 7.2 fronthaul interface (`-DOAI_FHI72=ON`). They must be built in
|
||||
order: DPDK → libxran → armral → OAI.
|
||||
|
||||
All libraries are installed into a common staging directory (`~/dw-sysroot`)
|
||||
so they stay isolated from the host system and can be pushed to the device
|
||||
together with the OAI binaries.
|
||||
|
||||
```shell
|
||||
export DW_TC=/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/bin/aarch64-none-linux-gnu
|
||||
export DW_SYSROOT=$HOME/dw-sysroot
|
||||
mkdir -p $DW_SYSROOT
|
||||
```
|
||||
|
||||
### 3.1 DPDK
|
||||
|
||||
OAI supports two xran release tracks:
|
||||
|
||||
| xran release | xran version | DPDK version |
|
||||
|---|---|---|
|
||||
| F | 6.1.9 | 20.11.9 |
|
||||
| K | 11.1.1 | 24.11.4 (minimum 22) |
|
||||
|
||||
The K release is recommended for new deployments. The commands below show
|
||||
the K release; swap version strings for F where noted.
|
||||
|
||||
#### 3.1.1 Build tools
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y meson ninja-build python3-pyelftools
|
||||
```
|
||||
|
||||
#### 3.1.2 Create a meson cross-file
|
||||
|
||||
```shell
|
||||
cat > ~/aarch64-dragonwing.ini <<'EOF'
|
||||
[binaries]
|
||||
c = '/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/bin/aarch64-none-linux-gnu-gcc'
|
||||
cpp = '/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/bin/aarch64-none-linux-gnu-g++'
|
||||
ar = '/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/bin/aarch64-none-linux-gnu-ar'
|
||||
strip = '/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/bin/aarch64-none-linux-gnu-strip'
|
||||
pkg-config = 'pkg-config'
|
||||
|
||||
[host_machine]
|
||||
system = 'linux'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'armv8.2-a'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
# DPDK reads this via meson.get_external_property() when cross-compiling;
|
||||
# -Dplatform=generic on the command line is ignored in that path.
|
||||
platform = 'generic'
|
||||
|
||||
[built-in options]
|
||||
c_args = ['-I/usr/include/aarch64-linux-gnu', '-I/usr/include']
|
||||
cpp_args = ['-I/usr/include/aarch64-linux-gnu', '-I/usr/include']
|
||||
c_link_args = ['-L/usr/lib/aarch64-linux-gnu']
|
||||
cpp_link_args = ['-L/usr/lib/aarch64-linux-gnu']
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 3.1.3 Download, build and install
|
||||
|
||||
```shell
|
||||
cd ~
|
||||
wget http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz # K release
|
||||
# wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz # F release
|
||||
tar xf dpdk-24.11.4.tar.xz
|
||||
cd dpdk-stable-24.11.4
|
||||
|
||||
# Always wipe the build directory — meson caches pkg-config results and will
|
||||
# silently reuse x86 libelf paths if the previous setup run was bad.
|
||||
rm -rf build-dragonwing
|
||||
|
||||
PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig \
|
||||
meson setup build-dragonwing \
|
||||
--cross-file ~/aarch64-dragonwing.ini \
|
||||
--prefix=$HOME/dw-sysroot \
|
||||
-Dplatform=generic \
|
||||
-Ddisable_libs=bpf \
|
||||
-Ddisable_drivers=net/ice,net/i40e,net/iavf,net/ixgbe
|
||||
ninja -C build-dragonwing
|
||||
ninja -C build-dragonwing install
|
||||
```
|
||||
|
||||
> **Note:** `PKG_CONFIG_LIBDIR` replaces (not appends) the pkg-config search
|
||||
> path, ensuring meson finds arm64 `.pc` files rather than x86 ones.
|
||||
> `-Ddisable_libs=bpf` drops the libelf dependency (only needed for eBPF
|
||||
> packet filtering, not for fronthaul use), avoiding the x86/arm64 libelf
|
||||
> mismatch. The `-Ddisable_drivers` list trims x86-only NIC drivers;
|
||||
> add DragonWing-specific PMDs with `-Denable_drivers=...` if needed.
|
||||
|
||||
Verify pkg-config can see it:
|
||||
|
||||
```shell
|
||||
PKG_CONFIG_PATH=$DW_SYSROOT/lib/pkgconfig \
|
||||
pkg-config --modversion libdpdk
|
||||
```
|
||||
|
||||
### 3.2 libxran
|
||||
|
||||
#### 3.2.1 Clone and patch
|
||||
|
||||
```shell
|
||||
git clone https://github.com/openairinterface/o-du-phy.git ~/phy
|
||||
cd ~/phy
|
||||
|
||||
# K release — tag must match K_VERSION in radio/fhi_72/CMakeLists.txt
|
||||
git checkout oran_k_release_v1.1
|
||||
# F release alternative:
|
||||
# git checkout oran_f_release_v1.9
|
||||
# git apply ~/openairinterface5g/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch
|
||||
```
|
||||
|
||||
#### 3.2.2 Cross-compile
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y libnuma-dev:arm64
|
||||
|
||||
cd ~/phy/fhi_lib/lib
|
||||
make clean
|
||||
|
||||
CPATH=/usr/include/aarch64-linux-gnu:/usr/include \
|
||||
PKG_CONFIG_PATH=$DW_SYSROOT/lib/pkgconfig \
|
||||
make -j$(nproc) XRAN_LIB_SO=1 \
|
||||
CC=${DW_TC}-gcc \
|
||||
CPP=${DW_TC}-g++ \
|
||||
AR=${DW_TC}-ar \
|
||||
AS=${DW_TC}-as \
|
||||
LD=${DW_TC}-gcc \
|
||||
WIRELESS_SDK_TOOLCHAIN=gcc \
|
||||
TARGET=armv8 \
|
||||
RTE_SDK=$DW_SYSROOT \
|
||||
XRAN_DIR=~/phy/fhi_lib
|
||||
```
|
||||
|
||||
The output is `~/phy/fhi_lib/lib/build/libxran.so`.
|
||||
|
||||
> **Note:** The xran Makefile for `TARGET=armv8` hardcodes `CC := gcc`,
|
||||
> `CPP := g++` etc., so the cross-compiler must be passed as **make
|
||||
> command-line arguments** (after `make`) — not as shell environment variables
|
||||
> — so they override the Makefile assignments. `PKG_CONFIG_PATH` and `CPATH`
|
||||
> must remain shell environment variables: the Makefile invokes pkg-config via
|
||||
> `$(shell ...)` which inherits the shell environment, and GCC reads `CPATH`
|
||||
> independently to prepend extra include directories (bridging the gap between
|
||||
> the SDK GCC's minimal built-in sysroot and the Ubuntu arm64 system headers).
|
||||
> `RTE_SDK` must point at the **installed** DPDK prefix so pkg-config finds
|
||||
> `libdpdk.pc` and resolves the correct arm64 include paths.
|
||||
|
||||
### 3.3 armral (Arm RAN Acceleration Library)
|
||||
|
||||
armral is required by OAI on aarch64 for BFP compression.
|
||||
|
||||
```shell
|
||||
git clone https://git.gitlab.arm.com/networking/ral.git ~/ral
|
||||
cd ~/ral
|
||||
git checkout armral-25.01
|
||||
mkdir build && cd build
|
||||
|
||||
cmake .. -GNinja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/openairinterface5g/cmake_targets/cross-arm-dragonwing.cmake \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=$DW_SYSROOT \
|
||||
-DCMAKE_C_FLAGS="-mcpu=cortex-a78+crypto" \
|
||||
-DCMAKE_CXX_FLAGS="-mcpu=cortex-a78+crypto"
|
||||
|
||||
ninja
|
||||
ninja install
|
||||
```
|
||||
|
||||
### 3.4 OAI gNB with FHI 7.2
|
||||
|
||||
```shell
|
||||
cd ~/openairinterface5g
|
||||
|
||||
# Step 1 — native host tools (skip if already done)
|
||||
mkdir -p build && cd build && cmake .. -DAVOID_SIGN=ON && make -j$(nproc) ldpc_generators generate_T && cd ..
|
||||
|
||||
# Step 2 — DragonWing cross-compile with FHI 7.2
|
||||
rm -rf dragonwing_build && mkdir dragonwing_build && cd dragonwing_build
|
||||
|
||||
PKG_CONFIG_PATH=$DW_SYSROOT/lib/pkgconfig \
|
||||
cmake .. \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../cmake_targets/cross-arm-dragonwing.cmake \
|
||||
-DNATIVE_DIR=../build \
|
||||
-DOAI_FHI72=ON \
|
||||
-Dxran_LOCATION=$HOME/phy/fhi_lib/lib \
|
||||
-Darmral_LOCATION=$DW_SYSROOT
|
||||
|
||||
make -j$(nproc) nr-softmodem nr-cuup params_libconfig coding oran_fhlib_5g
|
||||
```
|
||||
|
||||
> **Note:** `PKG_CONFIG_PATH` (not `PKG_CONFIG_LIBDIR`) is used here so that
|
||||
> the ARM DPDK `.pc` file is found *in addition to* the existing system arm64
|
||||
> packages, rather than replacing them.
|
||||
|
||||
### 3.5 linuxptp (ptp4l / phc2sys)
|
||||
|
||||
S-plane synchronization requires `ptp4l` and `phc2sys` from linuxptp 3.1.1.
|
||||
They are not present on the DragonWing by default and must be cross-compiled.
|
||||
|
||||
```shell
|
||||
git clone https://git.code.sf.net/p/linuxptp/code ~/linuxptp
|
||||
cd ~/linuxptp
|
||||
git checkout v3.1.1
|
||||
|
||||
KBUILD_OUTPUT=/opt/Hexagon_SDK/6.4.0.2/tools/gcc_tools_64/aarch64-none-linux-gnu/libc \
|
||||
CROSS_COMPILE=${DW_TC}- \
|
||||
make -j$(nproc) EXTRA_CFLAGS="-mcpu=cortex-a78"
|
||||
```
|
||||
|
||||
> **Note — two common pitfalls when cross-compiling linuxptp:**
|
||||
>
|
||||
> 1. Use `EXTRA_CFLAGS`, **not** `CFLAGS`, to add compiler flags.
|
||||
> The linuxptp `makefile` defines `CFLAGS = -Wall $(VER) $(incdefs) $(EXTRA_CFLAGS)`.
|
||||
> Passing `CFLAGS=...` on the make command line replaces that entire definition,
|
||||
> discarding `$(incdefs)` — which contains `-DHAVE_ONESTEP_SYNC` and `-D_GNU_SOURCE`.
|
||||
> Without `-DHAVE_ONESTEP_SYNC`, `missing.h` redeclares the enum that the system
|
||||
> `net_tstamp.h` already defines, causing a compile error.
|
||||
>
|
||||
> 2. Set `KBUILD_OUTPUT` to the cross-compiler sysroot so `incdefs.sh` picks up the
|
||||
> ARM `linux/net_tstamp.h` instead of the host x86 one. `CROSS_COMPILE` must be
|
||||
> an environment variable (not a `make CC=` argument) because `incdefs.sh` calls
|
||||
> `${CROSS_COMPILE}cpp` in a subshell to discover include paths.
|
||||
|
||||
Deploy:
|
||||
|
||||
```shell
|
||||
adb push ~/linuxptp/ptp4l /usr/sbin/ptp4l
|
||||
adb push ~/linuxptp/phc2sys /usr/sbin/phc2sys
|
||||
adb shell chmod +x /usr/sbin/ptp4l /usr/sbin/phc2sys
|
||||
```
|
||||
|
||||
Follow the ptp4l / phc2sys configuration in `doc/ORAN_FHI7.2_Tutorial.md`
|
||||
(§ "PTP configuration") once the RTL 10G NIC is available.
|
||||
|
||||
### 3.6 RTL8127 kernel module
|
||||
|
||||
The IQ-9075 EVK uses a Realtek RTL8127A (10 GbE) NIC for the fronthaul connection.
|
||||
The vendor kernel (`6.18.12-g06f8ab99cf04-dirty`) does not include any RTL8127 driver.
|
||||
Two options are available, depending on whether hardware timestamping is needed:
|
||||
|
||||
| Option | Module | Hardware timestamping | Notes |
|
||||
|--------|--------|-----------------------|-------|
|
||||
| A (§ 3.6.1–3.6.5) | mainline `r8169` | **No** — software only | Simpler build |
|
||||
| B (§ 3.6.6) | Realtek out-of-tree `r8127` | **Yes** — PHC registered | Required for ptp4l hardware mode |
|
||||
|
||||
For S-plane synchronisation with `ptp4l`, **use option B**. Option A is adequate if
|
||||
only basic connectivity is needed (e.g. rfsimulator, debug access).
|
||||
|
||||
Because the vendor kernel has `CONFIG_MODVERSIONS=n` and `CONFIG_MODULE_SIG=n`, the only
|
||||
requirement for any module to load is that its **version magic string matches exactly**:
|
||||
`6.18.12-g06f8ab99cf04-dirty SMP preempt mod_unload aarch64`
|
||||
|
||||
> **Note:** If QCOM provides a kernel SDK or `kernel-devsrc` package for the IQ-9075
|
||||
> build, use that instead — it will produce a better-matched module. The procedure
|
||||
> below is a best-effort workaround using the mainline source.
|
||||
|
||||
#### 3.6.1 Download and configure the kernel source
|
||||
|
||||
```shell
|
||||
cd ~
|
||||
# Download mainline stable 6.18.12
|
||||
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.12.tar.xz
|
||||
tar xf linux-6.18.12.tar.xz
|
||||
cd linux-6.18.12
|
||||
|
||||
# Seed config from the running device kernel
|
||||
adb pull /proc/config.gz /tmp/kernel-config.gz
|
||||
gunzip -f /tmp/kernel-config.gz
|
||||
cp /tmp/kernel-config .config
|
||||
|
||||
# Enable r8169 and its Realtek PHY driver as loadable modules
|
||||
scripts/config --module CONFIG_R8169
|
||||
scripts/config --module CONFIG_REALTEK_PHY
|
||||
|
||||
# Set LOCALVERSION so vermagic matches the device exactly
|
||||
scripts/config --set-str CONFIG_LOCALVERSION "-g06f8ab99cf04-dirty"
|
||||
scripts/config --disable CONFIG_LOCALVERSION_AUTO
|
||||
|
||||
# Resolve any new Kconfig options introduced since the device config was made.
|
||||
# CROSS_COMPILE must be set here so compiler/assembler capability checks (MTE,
|
||||
# shadow call stack, etc.) are evaluated against the ARM toolchain, not the host.
|
||||
# Omitting CROSS_COMPILE causes interactive questions during modules_prepare.
|
||||
make ARCH=arm64 CROSS_COMPILE=${DW_TC}- olddefconfig
|
||||
```
|
||||
|
||||
> **Note:** `olddefconfig` may set `CONFIG_HAVE_RUST=y` if the build host has `rustc`
|
||||
> installed. This does not affect the r8169 module build, but if `modules_prepare`
|
||||
> fails with a Rust-related error, disable it and re-run olddefconfig:
|
||||
> ```shell
|
||||
> scripts/config --disable CONFIG_HAVE_RUST
|
||||
> make ARCH=arm64 CROSS_COMPILE=${DW_TC}- olddefconfig
|
||||
> ```
|
||||
|
||||
#### 3.6.2 Prepare the kernel build tree
|
||||
|
||||
```shell
|
||||
# Builds host scripts (modpost etc.) and generates arch headers.
|
||||
# Does NOT compile the full kernel.
|
||||
make ARCH=arm64 CROSS_COMPILE=${DW_TC}- modules_prepare
|
||||
```
|
||||
|
||||
#### 3.6.3 Build r8169.ko and realtek.ko
|
||||
|
||||
Two modules are needed: the MAC driver (`r8169`) and the Realtek PHY driver (`realtek`).
|
||||
The r8169 probe fails with "no dedicated PHY driver found for PHY ID 0x001cc890" if
|
||||
`realtek.ko` is not loaded first.
|
||||
|
||||
```shell
|
||||
make ARCH=arm64 CROSS_COMPILE=${DW_TC}- M=drivers/net/ethernet/realtek KBUILD_MODPOST_WARN=1 modules
|
||||
make ARCH=arm64 CROSS_COMPILE=${DW_TC}- M=drivers/net/phy/realtek KBUILD_MODPOST_WARN=1 modules
|
||||
```
|
||||
|
||||
Outputs: `drivers/net/ethernet/realtek/r8169.ko` and `drivers/net/phy/realtek/realtek.ko`.
|
||||
|
||||
> **Note:** `KBUILD_MODPOST_WARN=1` is required because `Module.symvers` does not exist
|
||||
> (only generated by a full kernel build). Without it, `modpost` errors on unresolved
|
||||
> symbols such as `napi_alloc_skb`, `free_irq`, etc. These are all standard kernel
|
||||
> exports that are present in the running kernel; since `CONFIG_MODVERSIONS=n` on the
|
||||
> device there is no CRC check at load time, so the warnings are safe to ignore.
|
||||
|
||||
#### 3.6.4 Prepare the firmware
|
||||
|
||||
The RTL8127A requires firmware `rtl_nic/rtl8127a-1.fw`. The build host has it in
|
||||
compressed form (`linux-firmware` package); decompress before pushing because the vendor
|
||||
kernel does not support `CONFIG_FW_LOADER_COMPRESS`.
|
||||
|
||||
```shell
|
||||
zstd -d /lib/firmware/rtl_nic/rtl8127a-1.fw.zst -o ~/rtl8127a-1.fw
|
||||
```
|
||||
|
||||
#### 3.6.5 Deploy and load
|
||||
|
||||
The modules depend on `libphy.ko` and `mdio-bus.ko`, which are already present in the
|
||||
device's module tree. Load order matters: `libphy` → `realtek` → `r8169`.
|
||||
|
||||
```shell
|
||||
# Push modules and firmware
|
||||
adb push ~/linux-6.18.12/drivers/net/ethernet/realtek/r8169.ko /data/local/tmp/
|
||||
adb push ~/linux-6.18.12/drivers/net/phy/realtek/realtek.ko /data/local/tmp/
|
||||
adb shell mkdir -p /lib/firmware/rtl_nic
|
||||
adb push ~/rtl8127a-1.fw /lib/firmware/rtl_nic/
|
||||
|
||||
# Load in dependency order
|
||||
adb shell "modprobe libphy && insmod /data/local/tmp/realtek.ko && insmod /data/local/tmp/r8169.ko"
|
||||
|
||||
# Verify
|
||||
adb shell "dmesg | grep -i r8169"
|
||||
adb shell "ip link show"
|
||||
```
|
||||
|
||||
If the NIC is detected, assign an IP and verify link before proceeding to the DPDK /
|
||||
ptp4l configuration. To make the modules persistent across reboots, copy them into the
|
||||
device module tree:
|
||||
|
||||
```shell
|
||||
adb shell mkdir -p /lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/ethernet/realtek
|
||||
adb shell mkdir -p /lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/phy/realtek
|
||||
adb push ~/linux-6.18.12/drivers/net/ethernet/realtek/r8169.ko \
|
||||
/lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/ethernet/realtek/
|
||||
adb push ~/linux-6.18.12/drivers/net/phy/realtek/realtek.ko \
|
||||
/lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/phy/realtek/
|
||||
adb shell depmod -a
|
||||
```
|
||||
|
||||
#### 3.6.6 Option B — Realtek out-of-tree r8127 driver (hardware timestamping)
|
||||
|
||||
Realtek publishes a standalone driver with full PTP/PHC support
|
||||
(`ptp_clock_info`, hardware timestamp registers). It replaces both `r8169.ko` and
|
||||
`realtek.ko` from option A with a single `r8127.ko`.
|
||||
|
||||
The upstream Realtek source (`openwrt/rtl8127`) requires several fixes before the
|
||||
driver works correctly on the DragonWing. Use the patched fork below instead of
|
||||
the upstream repo; it already contains all fixes described in the repo's `README.md`:
|
||||
|
||||
```shell
|
||||
cd ~
|
||||
# TODO: update URL once the fork is published
|
||||
git clone https://github.com/rtknopp/rtl8127.git
|
||||
cd rtl8127
|
||||
|
||||
make KERNELDIR=~/linux-6.18.12 \
|
||||
ARCH=arm64 \
|
||||
CROSS_COMPILE=${DW_TC}- \
|
||||
KBUILD_MODPOST_WARN=1 \
|
||||
ENABLE_PTP_SUPPORT=y \
|
||||
modules
|
||||
```
|
||||
|
||||
The fork contains the following fixes relative to `openwrt/rtl8127` v11.015.00
|
||||
(see `README.md` in the fork for full technical details):
|
||||
|
||||
| Fix | Symptom without it |
|
||||
|-----|--------------------|
|
||||
| **Kernel 6.15+ API** — `hrtimer_init()` + manual `.function` assignment replaced by `hrtimer_setup()` | Build fails on kernels ≥ 6.15 with "implicit declaration" error |
|
||||
| **PHC clock initialisation outside spinlock** — `rtl8127_set_local_time()` moved to after `r8127_spin_unlock()` in `rtl8127_hwtstamp_enable()` | `_rtl8127_phc_settime()` re-acquires `phy_lock` while it is already held, causing an RCU stall / spinlock deadlock; PHC stays at 0 while the PTP master uses Unix time, producing ~1.8 s rms offset |
|
||||
| **Capture engine arm sequence** — `rtl8127_ptp_enable_config()` (PTP_CTL = 0x103F) called *before* writing `0xA640 BIT_15` at probe time; `0xA640` removed from the `hwtstamp_enable()` path entirely | The PHY timestamp pipeline activates only when `0xA640 BIT_15` is written while `PTP_CTL != 0`; writing it with `PTP_CTL = 0` (the upstream sequence) is silently accepted but `PTP_INSR` never signals `TX_TX_INTR` or `RX_TS_INTR` — all timestamps read as zero. Writing `0xA640` from the `hwtstamp_enable()` path (called on every `SIOCSHWTSTAMP`) causes an infinite link-drop loop: the autoneg restart drops the link, which ptp4l answers with another `SIOCSHWTSTAMP`, which drops the link again |
|
||||
| **Disable path writes `PTP_CTL = 0x0000`** instead of clearing only `BIT_0` | Clearing `BIT_0` alone leaves the PHY in a partial PTP mode that causes the link partner to time out and drop the link on disable |
|
||||
| **`rtl8127_ptp_link_up_init()`** — new function called from `rtl8127_link_on_patch` to re-write `PTP_CTL` and `PTP_INER` after any link recovery while hwtstamp is enabled | After a cable pull / reinsert, `PTP_CTL` was not restored; upstream only called `rtl8127_set_local_time()` on link-up, which does not restore PTP capture configuration |
|
||||
|
||||
If option A modules are already loaded, unload them first:
|
||||
|
||||
```shell
|
||||
adb shell "rmmod r8169; rmmod realtek"
|
||||
```
|
||||
|
||||
Deploy and load:
|
||||
|
||||
```shell
|
||||
adb push ~/rtl8127/r8127.ko /data/local/tmp/
|
||||
adb shell "modprobe libphy && insmod /data/local/tmp/r8127.ko"
|
||||
|
||||
# Verify hardware timestamping is now available
|
||||
adb shell "ethtool -T enP1p1s0"
|
||||
```
|
||||
|
||||
Expected output should include `PTP Hardware Clock: 0` and hardware transmit/receive
|
||||
timestamp modes. To make persistent across reboots:
|
||||
|
||||
```shell
|
||||
adb shell mkdir -p /lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/ethernet/realtek
|
||||
adb push ~/rtl8127/r8127.ko \
|
||||
/lib/modules/6.18.12-g06f8ab99cf04-dirty/kernel/drivers/net/ethernet/realtek/
|
||||
adb shell depmod -a
|
||||
```
|
||||
|
||||
#### 3.7 Deploy FHI 7.2 binaries via adb
|
||||
|
||||
```shell
|
||||
adb shell mkdir -p /data/oai/lib
|
||||
|
||||
# OAI executables
|
||||
adb push dragonwing_build/nr-softmodem /data/oai/
|
||||
adb push dragonwing_build/liboran_fhlib_5g.so /data/oai/lib/
|
||||
adb push dragonwing_build/libparams_libconfig.so /data/oai/lib/
|
||||
adb push dragonwing_build/libcoding.so /data/oai/lib/
|
||||
|
||||
# DPDK shared libraries
|
||||
find $DW_SYSROOT/lib -name "librte_*.so*" -exec adb push {} /data/oai/lib/ \;
|
||||
|
||||
# dpdk-devbind.py — needed to bind VFs to vfio-pci before starting the gNB.
|
||||
# Install to /usr/local/bin to match the path used in ORAN_FHI7.2_Tutorial.md.
|
||||
# Python 3 is already present on the device.
|
||||
adb push $DW_SYSROOT/bin/dpdk-devbind.py /data/oai/
|
||||
adb shell "cp /data/oai/dpdk-devbind.py /usr/local/bin/dpdk-devbind.py && chmod +x /usr/local/bin/dpdk-devbind.py"
|
||||
|
||||
# xran
|
||||
adb push ~/phy/fhi_lib/lib/build/libxran.so /data/oai/lib/
|
||||
|
||||
# armral
|
||||
adb push $DW_SYSROOT/lib/libarmral.so /data/oai/lib/
|
||||
|
||||
# Run with combined LD_LIBRARY_PATH
|
||||
adb shell "export LD_LIBRARY_PATH=/data/oai/lib && /data/oai/nr-softmodem --help"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4 Alternative: sysroot from device
|
||||
|
||||
If the Ubuntu multiarch packages are not available on the build host, you can
|
||||
extract the device's root filesystem and use it as a sysroot instead.
|
||||
|
||||
```shell
|
||||
# Pull relevant library directories from the device
|
||||
adb pull /usr/lib dragonwing-sysroot/usr/lib
|
||||
adb pull /usr/include dragonwing-sysroot/usr/include
|
||||
adb pull /lib dragonwing-sysroot/lib
|
||||
|
||||
# Then configure cmake with an explicit sysroot (from the repo root)
|
||||
rm -rf dragonwing_build && mkdir dragonwing_build && cd dragonwing_build
|
||||
cmake .. \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../cmake_targets/cross-arm-dragonwing.cmake \
|
||||
-DNATIVE_DIR=../build \
|
||||
-DCMAKE_SYSROOT=$(pwd)/../dragonwing-sysroot \
|
||||
-DCMAKE_FIND_ROOT_PATH=$(pwd)/../dragonwing-sysroot
|
||||
```
|
||||
|
||||
You will also need to regenerate the pkg-config search path:
|
||||
|
||||
```shell
|
||||
export PKG_CONFIG_LIBDIR=$(pwd)/dragonwing-sysroot/usr/lib/pkgconfig:$(pwd)/dragonwing-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5 Troubleshooting
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| `Could NOT find GnuTLS` | arm64 packages not installed or wrong `PKG_CONFIG_LIBDIR` | Check Section 1.3; verify `pkg-config --list-all` finds arm64 packages |
|
||||
| `cannot find -lgnutls` at link | Linker not searching `/usr/lib/aarch64-linux-gnu` | Verify that toolchain file `CMAKE_EXE_LINKER_FLAGS_INIT` is being applied |
|
||||
| `Unsupported architecture` from apt | Host apt sources not configured for arm64 | Redo Section 1.3.1 |
|
||||
| Binary crashes with `SIGILL` on device | Wrong `-mcpu` / `-march` flag | The toolchain defaults to `cortex-a78`; if running on a Silver (A55) core, add `-DCMAKE_C_FLAGS=-mcpu=cortex-a55` |
|
||||
| `adb: error: failed to copy` | `/data/oai` directory doesn't exist or no permission | `adb shell mkdir -p /data/oai` or use a writable path like `/data/local/tmp/oai` |
|
||||
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 registry.redhat.io/ubi9/ubi:latest AS ran-base
|
||||
LABEL MAINTAINER OpenAirInterface <contact@openairinterface.org>
|
||||
LABEL MAINTAINER OpenAirInterface <oaicicdteam@openairinterface.org>
|
||||
ARG NEEDED_GIT_PROXY
|
||||
ARG TARGETARCH
|
||||
ENV TZ=Europe/Paris
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN /bin/sh oaienv && \
|
||||
mkdir -p log ran_build/build ran_build/build-cross && \
|
||||
cd ran_build/build && \
|
||||
cmake ../../.. -GNinja && \
|
||||
ninja generate_T && \
|
||||
ninja ldpc_generators generate_T && \
|
||||
cd ../build-cross/ && \
|
||||
# install missing libyaml-cpp-dev for arm64
|
||||
apt install -y libyaml-cpp-dev:arm64 && \
|
||||
|
||||
@@ -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,23 +111,15 @@ struct timespec timespec_sub(struct timespec, struct timespec)
|
||||
struct timespec t = {0};
|
||||
return t;
|
||||
};
|
||||
|
||||
void perform_symbol_rotation(NR_DL_FRAME_PARMS *fp, 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(NR_DL_FRAME_PARMS *fp)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -132,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];
|
||||
@@ -168,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));
|
||||
@@ -175,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"
|
||||
|
||||
@@ -172,13 +172,17 @@ static void rx_func(processingData_L1_t *info)
|
||||
if (gNB->phase_comp) {
|
||||
//apply the rx signal rotation here
|
||||
int soffset = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size;
|
||||
for (int aa = 0; aa < gNB->frame_parms.nb_antennas_tx; aa++) {
|
||||
const uint max_symb = (gNB->frame_parms.Ncp == NR_EXTENDED) ? 12 : 14;
|
||||
const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
|
||||
for (int aa = 0; aa < fp->nb_antennas_rx; aa++) {
|
||||
const uint max_symb = fp->Ncp == NR_EXTENDED ? 12 : 14;
|
||||
for (int sym = 0; sym < max_symb; sym++)
|
||||
apply_nr_rotation_symbol_RX(&gNB->frame_parms,
|
||||
gNB->common_vars.rxdataF[aa] + soffset + sym * gNB->frame_parms.ofdm_symbol_size,
|
||||
gNB->frame_parms.symbol_rotation[1],
|
||||
gNB->frame_parms.N_RB_UL,
|
||||
apply_nr_rotation_symbol_RX(fp->symbols_per_slot,
|
||||
fp->slots_per_subframe,
|
||||
fp->timeshift_symbol_rotation,
|
||||
fp->first_carrier_offset,
|
||||
gNB->common_vars.rxdataF[aa] + soffset + sym * fp->ofdm_symbol_size,
|
||||
fp->symbol_rotation[1],
|
||||
fp->N_RB_UL,
|
||||
slot_rx,
|
||||
sym);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user