Compare commits

...

29 Commits

Author SHA1 Message Date
francescomani
1617c32e3c symbol based RU control function
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:20:03 +02:00
francescomani
ae780bae57 adapt FR2 RFsim CI test to use beam features
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:20:01 +02:00
francescomani
2834088ea0 do not send beam information to USRP if the beam is the same as before
this keeps the same logic as before, the understanding is that executing the GPIO command requires time or at least have an impact on the timing, so it is better not to execute it if not necessary

Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:59 +02:00
francescomani
0b320c8dce function to set GPIO beam for USRP using new API
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:57 +02:00
francescomani
9fc066394e RU control function for beam information
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:55 +02:00
francescomani
04ce714f37 modification in tx_rf and rx_rf to use read/write beams_func in case of concurrent beams
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:52 +02:00
Robert Schmidt
d420f8c123 Merge branch 'integration_2026_w18' into 'develop'
Integration `2026.w18`

* !4070 fix(nrLDPC_coding): Remove ldpc_xdma
* !4068 fix(radio): Make write_reorder context thread-safe
* !4031 Add AoA selection to vrtsim
* !4071 Add 200MHz DDDSU Liteon configuration file
* !4080 spsc_q: make compile on older compilers
* !4081 fix (F1 Handover): do not trigger when no DRB is configured
* !4072 UE PRACH slot fix
* !4082 fixing typo in AWGN calculation
* !4049 Validate signed MR commits and update contribution guidelines
* !4087 Remove unnecessary log

Closes #1072

See merge request oai/openairinterface5g!4075
2026-04-30 15:24:22 +00:00
Robert Schmidt
e92dfe2f93 Merge remote-tracking branch 'origin/log_fix' into integration_2026_w18 (!4087)
Remove unnecessary log

We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length of
the samples is no longer samples_per_slot.  # Please enter a commit
message to explain why this merge is necessary,
2026-04-30 11:50:37 +02:00
Rakesh Mundlamuri
35965a1418 Remove unnecessary log
We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length
of the samples is no longer samples_per_slot.

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-04-30 13:29:44 +05:30
Robert Schmidt
103c1d48e3 Merge remote-tracking branch 'origin/ci-signed-commit' into integration_2026_w18 (!4049)
Validate signed MR commits and update contribution guidelines

This MR adds validation for signed merge request commits and updates the
contributing guidelines including the requirement for signing commits.

- The CI will warn in the Verify Guidelines stage if any commit in the
  merge request is unsigned.
  - Checks all commits in the MR source branch that are not present in
    the target branch
  - It then verifies if all these commits are signed
- Update contributing guidelines to include commit rules and signing
  commits using git commit -s.
- The CI will also check if there are any merge commits and fail the
  pipeline in that case.
2026-04-29 18:51:04 +02:00
Robert Schmidt
0755086cc2 Merge remote-tracking branch 'origin/hotfix_AWGN' into integration_2026_w18 (!4082)
fixing typo in AWGN calculation
2026-04-29 18:50:31 +02:00
Robert Schmidt
c8ff0819f8 Merge remote-tracking branch 'origin/issue1272' into integration_2026_w18 (!4072)
UE PRACH slot fix

This fixes a bug in prach slot computation if more than 1 slot in
subframe at UE

Closes #1072
2026-04-29 18:01:28 +02:00
Robert Schmidt
ee4018b5d0 Merge remote-tracking branch 'origin/f1-ho-trigger-no-drb' into integration_2026_w18 (!4081)
fix (F1 Handover): do not trigger when no DRB is configured

Prevent triggering F1 handover for UEs without any DRB, to align with
3GPP TS 38.473 clause 8.3.1.2 and avoid initiating an invalid UE Context
Setup Request in the F1 handover flow.

Changes:

- Add a preliminary check in nr_rrc_trigger_f1_ho() to verify at least
  one DRB exists in ue->drbs
- Abort F1 HO trigger early when no DRB is present, with warning
2026-04-29 18:01:04 +02:00
Robert Schmidt
49e6575910 Merge remote-tracking branch 'origin/fix-spsc-q-older-compilers' into integration_2026_w18 (!4080)
spsc_q: make compile on older compilers

While compiling the spsc_q unit tests on older compilers, some complain
with

     /openairinterface5g/common/utils/ds/tests/test_spsc_q.cpp:11:44: error: use of deleted function ‘spsc_q::spsc_q(spsc_q&&)’
     spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
     /openairinterface5g/common/utils/ds/spsc_q.h:19:16: error: use of deleted function ‘std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)’

These compiles cannot copy the atomic. Change the API to avoid this
copy.

"Older" compiler here means g++-11/12.
2026-04-29 18:00:36 +02:00
Florian Kaltenberger
79ebf23a80 fixing typo in AWGN calculation
Signed-off-by: Florian Kaltenberger <florian.kaltenberger@eurecom.fr>
2026-04-29 16:54:14 +02:00
Shubhika Garg
3cbf0927be ci: validate for signed MR commits in the CI
- Add a new script to validate for signed commits
- Post a GitLab MR Warning comment listing the unsigned commit details

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-04-29 12:22:52 +02:00
Guido Casati
6c534c3b76 F1 Handover: do not trigger when no DRB is configured
Prevent triggering F1 handover for UEs without any DRB, to align
with 3GPP TS 38.473 clause 8.3.1.2 and avoid initiating an invalid
UE Context Setup Request in the F1 handover flow.

Changes:

- Add a preliminary check in `nr_rrc_trigger_f1_ho()` to verify at
  least one DRB exists in ue->drbs
- Abort F1 HO trigger early when no DRB is present, with warning
2026-04-29 10:30:26 +02:00
Jaroslava Fiedlerova
b5e0cf6a2b Merge remote-tracking branch 'origin/liteon_200MHz' into integration_2026_w18 (!4071)
Add 200MHz DDDSU Liteon configuration file

Adding 200MHz DDDSU 2x2 Liteon configuration file. I think we should keep
only 200 MHz configuration for both FR2 RUs: Liteon and Microamp.
Achieved throughputs are DL: 980 Mbps UL 90 Mbps
2026-04-28 23:06:01 +02:00
Robert Schmidt
4d67a6e513 spsc_q: make compile on older compilers
While compiling the spsc_q unit tests on some compilers, some
complain with

     /openairinterface5g/common/utils/ds/tests/test_spsc_q.cpp:11:44: error: use of deleted function ‘spsc_q::spsc_q(spsc_q&&)’
     spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
     /openairinterface5g/common/utils/ds/spsc_q.h:19:16: error: use of deleted function ‘std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)’

These compiles cannot copy the atomic. Change the API to avoid this
copy.

"Older" compiler here means g++-11/12.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-28 18:40:24 +02:00
Bartosz Podrygajlo
ced388a0f4 fix(ue): Add a log entry for PRACH occasion selection 2026-04-27 22:27:15 +02:00
francescomani
134d750a88 fix(mac): correct PRACH slot computation in configure_prach_occasions
Move the subframe scaling logic outside the PRACH slot loop to prevent the
subframe index from being incorrectly multiplied multiple times when there
are multiple PRACH slots per subframe.
2026-04-27 22:26:56 +02:00
Shubhika Garg
13963ba70d docs: update contributing guidelines to include commit signing rules
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
Co-authored-by: Sagar Arora <sagar.arora@openairinterface.org>
2026-04-27 13:59:00 +02:00
Karim Boutiba
28c1634fdd Add 200MHz DDDSU Liteon configuration file 2026-04-27 13:49:06 +02:00
Jaroslava Fiedlerova
ec171483e9 Merge remote-tracking branch 'origin/vrtsim-aoa-selection' into integration_2026_w18 (!4031)
Add AoA selection to vrtsim

Adds AoA as a selection parameter in vrtsim. Different UEs can be
configured at different arrival angles, emulating distinct spatial
locations, which can serve as an enabler for beamforming evaluation.
2026-04-27 10:00:10 +02:00
Jaroslava Fiedlerova
d75b8963ab Merge remote-tracking branch 'origin/fix-reorder-context' into integration_2026_w18 (!4068)
fix(radio): Make write_reorder context thread-safe
2026-04-27 09:58:01 +02:00
Jaroslava Fiedlerova
3329877914 Merge remote-tracking branch 'origin/remove_ldpc_xdma' into integration_2026_w18 (!4070)
fix(nrLDPC_coding): Remove ldpc_xdma

Arguments for removing:
- There are no resources to maintain it. Including no machine with the
  board.
- There is no clue that someone is actually using it.
- The library is not standard/cannot be generalized. It was designed for
  a specific solution and cannot be used for another.
- The standard AAL (BBDev or other) should be privileged for LDPC offload
  to a hardware accelerator.
2026-04-27 09:56:35 +02:00
Romain Beurdouche
0538ea4937 fix(nrLDPC_coding): Remove ldpc_xdma
Arguments for removing:
1. There are no resources to maintain it.
   Including no machine with the board.
2. There is no clue that someone is actually using it.
3. AAL is privileged for LDPC offload to a hardware accelerator.
2026-04-24 12:09:34 +00:00
Bartosz Podrygajlo
b5aa2d87fb fix(radio): Make write_reorder context thread-safe 2026-04-24 06:49:20 +02:00
Merkebu Girmay
58f00921c8 Add AoA selection to vrtsim in-process CIRDB path 2026-04-15 11:19:55 -07:00
36 changed files with 477 additions and 1764 deletions

View File

@@ -1,6 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# Contributing to OpenAirInterface #
# Contributing to OpenAirInterface
We want to make contributing to this project as easy and transparent as possible.
@@ -11,10 +11,10 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
if you do not have any.
- We recommend that you register with a professional or student email address.
- If your email domain (`@domain.com`) is not whitelisted, please contact us
(mailto:contact@openairinterface.org).
(mailto:oaicicdteam@openairinterface.org).
- Eurecom GitLab does NOT accept public email domains.
3. Provide the OAI team with the **username** of this account to
(mailto:contact@openairinterface.org) ; we will give you the developer
(mailto:oaicicdteam@openairinterface.org) ; we will give you the developer
rights on this repository.
4. The contributing policies are described in the [corresponding documentation
page](doc/code-style-contrib.md).
@@ -24,8 +24,43 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
request from a forked repository.
* This decision was made for the license reasons.
* The Continuous Integration will reject your merge request.
5. Mandatory signing of all the commits using the email address used for CLA.
# License #
## Commit Guidelines
### Signing Commits
To sign commits:
You can also get the verified label
on your commits via using [SSH KEYS or GPG KEYS](https://docs.gitlab.com/user/project/repository/signed_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 MR.
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
licensed under

View File

@@ -89,6 +89,19 @@ pipeline {
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
// GitLab-Jenkins plugin integration is lacking to perform the merge by itself
// Doing it manually --> it may have merge conflicts
// Validate MR commits: checks for missing Signed-off-by and merge commits
def mrValidationLog = "signedCommit_${env.BUILD_NUMBER}.log"
def mrValidationExitCode = sh(
script: "bash ./ci-scripts/pre-ci-check.sh -s origin/${env.gitlabSourceBranch} -t origin/${env.gitlabTargetBranch} > ${mrValidationLog} 2>&1",
returnStatus: true
)
def mrValidationMessage = readFile(mrValidationLog).trim()
sh "rm -f ${mrValidationLog}"
addGitLabMRComment comment: "${mrValidationMessage}"
if (mrValidationExitCode >= 2) {
error("${mrValidationMessage}")
}
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
} else {
echo "Git Branch is ${GIT_BRANCH}"

View File

@@ -70,7 +70,7 @@ gNBs =
initialULBWPsubcarrierSpacing = 3;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 52;
prach_ConfigurationIndex = 70;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
@@ -112,7 +112,7 @@ gNBs =
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
ssb_PositionsInBurst_Bitmap = 85;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
@@ -179,7 +179,7 @@ MACRLCs = ({
set_analog_beamforming = "lophy";
beam_duration = 1;
beams_per_period = 1;
beam_weights = [0]; // single SSB -> one analog beam
beam_weights = [0, 1, 2, 3]; // 4 SSBs -> 4 analog beams
});
L1s = (

94
ci-scripts/pre-ci-check.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/bin/bash
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
function usage {
echo "OAI GitLab MR validation script (Signed-off-by and merge commits)"
echo ""
echo "Usage:"
echo "------"
echo " $0 -s <source-branch> -t <target-branch>"
echo ""
echo "Options:"
echo "--------"
echo " -s"
echo " The source branch of the merge request. Default value is current Git Branch (HEAD)"
echo ""
echo " -t"
echo " The target branch of the merge request. Default value is develop"
echo ""
echo " -h"
echo " Print this help message."
echo ""
}
# Parse arguments properly
SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
TARGET_BRANCH="origin/develop"
git fetch --quiet
while getopts ":s:t:h" opt; do
case "$opt" in
s)
SOURCE_BRANCH="$OPTARG"
;;
t)
TARGET_BRANCH="$OPTARG"
;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires a value."
echo ""
usage
exit 2
;;
\?)
echo "Error: Invalid option -$OPTARG"
echo ""
usage
exit 2
;;
esac
done
# ----------------------------
# Merged commits
# ----------------------------
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 ',' -)\n"
echo -e "$message"
exit 3
fi
# ----------------------------
# Check unsigned commits
# ----------------------------
unsignedCommits=$(
for c in $(git rev-list "$TARGET_BRANCH".."$SOURCE_BRANCH"); do
if ! git log -1 --format=%B "$c" | grep -q "Signed-off-by:"; then
git log -1 --format='%h' "$c"
fi
done | paste -sd ","
)
# ----------------------------
# Report unsigned commits
# ----------------------------
message=""
if [ -n "$unsignedCommits" ]; then
message="> WARNING: The following commit(s) are missing a Signed-off-by:\n>\n> $unsignedCommits\n>\n"
message+="> Please use 'git commit -s' to sign your commits.\n>\n"
message+="> For detailed instructions, refer to the CONTRIBUTING file at the root of this repository."
echo -e "$message"
exit 1
else
message="> All commits are signed off using 'git commit -s'."
echo -e "$message"
exit 0
fi

View File

@@ -99,6 +99,7 @@ services:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time
--rfsimulator.[0].enable_beams --rfsimulator.[0].beam_gains 0,-20,-30,-40
ASAN_OPTIONS: detect_leaks=0
networks:
public_net:
@@ -125,6 +126,7 @@ services:
USE_ADDITIONAL_OPTIONS: --rfsim
--rfsimulator.[0].serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
-C 27975360000 -r 66 --numerology 3 --ssb 48 --band 257
--rfsimulator.[0].enable_beams --rfsimulator.[0].beam_gains 0,-20,-30,-40 --rfsimulator.[0].beam_ids 2
ASAN_OPTIONS: detect_leaks=0
devices:
- /dev/net/tun:/dev/net/tun

View File

@@ -18,7 +18,7 @@ HWs=""
BUILD_DIR=ran_build
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal websrv oai_iqplayer imscope imscope_record"
TARGET_LIST=""
BUILD_TOOL_OPT="-j$(nproc)"

View File

@@ -8,7 +8,7 @@
#include "spsc_q.h"
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz)
{
/* internally, use one element more: the ringbuffer is full if
* (write_idx + 1) % cnt == read_idx, so it's full if one element is still
@@ -17,7 +17,8 @@ spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
cnt += 1;
spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz};
rb.buf = calloc(cnt, elsiz);
return rb;
*rbn = rb;
return rb.buf != NULL;
}
void spsc_q_free(spsc_q_t *rb)

View File

@@ -25,7 +25,7 @@ typedef struct spsc_q {
atomic_size_t read_idx;
} spsc_q_t;
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz);
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz);
void spsc_q_free(spsc_q_t *rb);
bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz);

View File

@@ -8,7 +8,8 @@ extern "C" {
}
TEST(spsc_q, basic_test) {
spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 2, sizeof(int)));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
@@ -26,7 +27,8 @@ TEST(spsc_q, basic_test) {
}
TEST(spsc_q, cont_write) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int a = 1;
for (int i = 0; i < 1111; ++i) {
@@ -57,7 +59,8 @@ bool count(const void *data, void *user)
return true; /* count all */
}
TEST(spsc_q, iterator) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -85,7 +88,8 @@ bool drop(const void *data, void *user)
return true; /* drop all */
}
TEST(spsc_q, drop) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -104,7 +108,8 @@ TEST(spsc_q, drop) {
TEST(spsc_q, full) {
int n = 3;
spsc_q_t rb = spsc_q_alloc(n, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, n, sizeof(int)));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
a++;

View File

@@ -1,163 +0,0 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# LDPC offload with the XDMA driver
[TOC]
This documentation aims to provide a tutorial for using Xilinx PCIe-XDMA based FPGA LDPC decoding within OAI. LDPC decoding is offloaded to FPGA.
## XDMA Driver Build & Install
- Get XDMA driver source
```bash
git clone https://github.com/Xilinx/dma_ip_drivers.git
cd dma_ip_drivers/XDMA/linux-kernel
```
The *xdma_driver* directory contains the following:
```bash
dma_ip_drivers/XDMA/linux-kernel/
├── COPYING
├── include
├── LICENSE
├── readme.txt
├── RELEASE
├── tests
├── tools
└── xdma
```
Before building the driver, ensure that your system recognizes the Xilinx device. You can check this using the `lspci` command:
```bash
$ lspci | grep Xilinx
01:00.0 Serial controller: Xilinx Corporation Device 8038
```
Building and Installing the Driver
```bash
cd ~/dma_ip_drivers/XDMA/linux-kernel/xdma
make clean
make
# install to make the driver loading automatically at system startup
sudo make install
```
Load the Driver
```bash
cd ~/dma_ip_drivers/XDMA/linux-kernel/tests
sudo ./load_driver.sh
```
## OAI Build
```bash
# Get openairinterface5g source code
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g
# Install OAI dependencies
cd ~/openairinterface5g/cmake_targets
./build_oai -I
# Build OAI gNB & UE
cd ~/openairinterface5g
source oaienv
cd cmake_targets
./build_oai --ninja -w SIMU --gNB --nrUE -P --build-lib "ldpc_xdma" -C -c
```
Shared object file *libldpc_xdma.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using `--build-lib ldpc_xdma`.
## 5G PHY simulators
The simulated test uses the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface. Additionally, the option `--nrLDPC_coding_xdma.num_threads_prepare` is used to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
Another way to activate the feature is to add the `xdma.conf` file with the following content:
```
nrLDPC_coding_xdma : {
num_threads_prepare : 2;
};
loader : {
ldpc : {
shlibversion : "_xdma";
};
};
```
and use option `-O xdma.conf`.
### nr_ulsim test
Example command for running nr_ulsim with LDPC decoding offload to the FPGA:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P -O xdma.conf
```
## Run
Both gNB and nrUE use the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface and `--nrLDPC_coding_xdma.num_threads_prepare` to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
Another way to activate the feature is to add the following content to the `.conf` file you want to use:
```
nrLDPC_coding_xdma : {
num_threads_prepare : 2;
};
loader : {
ldpc : {
shlibversion : "_xdma";
};
};
```
and use option `-O *.conf`.
### gNB
Example command using rfsim:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```bash
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf
```
if you have added the configuration to the `.conf` file.
### UE
Example command using rfsim:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100 --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```bash
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100
```
if you have added the configuration to the `.conf` file.

View File

@@ -617,9 +617,9 @@ jumboframe 1 # enable jumbo frame
###### FR2
The OAI configuration file [`gnb.sa.band257.66prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.66prb.fhi72.2x2-liteon.conf) corresponds to:
- TDD pattern `DDDDDDDSUU`, 1.25ms
- Bandwidth 100MHz
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-liteon.conf) corresponds to:
- TDD pattern `DDDSU`, 0.625ms
- Bandwidth 200MHz
- FW v02.00.07
- DL uses jumbo frame, UL uses standard MTU of 1500 bytes

View File

@@ -92,18 +92,17 @@ static void tx_func(processingData_L1tx_t *info)
pushNotifiedFIFO(&gNB->resp_L1, res);
int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx);
// TODO check for analog_bf_vendor_ext set to 1 is a workaround while no beam API for beam selection is implemented
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx
|| IS_SOFTMODEM_RFSIM || cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) {
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) {
start_meas(&info->gNB->phy_proc_tx);
phy_procedures_gNB_TX(info->gNB, &sched_response.DL_req, &sched_response.TX_req, &sched_response.UL_dci_req, frame_tx,slot_tx);
PHY_VARS_gNB *gNB = info->gNB;
processingData_RU_t syncMsgRU;
syncMsgRU.frame_tx = frame_tx;
syncMsgRU.slot_tx = slot_tx;
syncMsgRU.ru = gNB->RU_list[0];
syncMsgRU.timestamp_tx = info->timestamp_tx;
}
processingData_RU_t syncMsgRU;
syncMsgRU.frame_tx = frame_tx;
syncMsgRU.slot_tx = slot_tx;
syncMsgRU.ru = gNB->RU_list[0];
syncMsgRU.timestamp_tx = info->timestamp_tx;
ru_ctrl_func((void *)&syncMsgRU);
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) {
LOG_D(PHY, "gNB: %d.%d : calling RU TX function\n", syncMsgRU.frame_tx, syncMsgRU.slot_tx);
ru_tx_func((void *)&syncMsgRU);
stop_meas(&info->gNB->phy_proc_tx);

View File

@@ -347,17 +347,28 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
AssertFatal(*slot < fp->slots_per_frame && *slot >= 0, "slot %d is illegal (%d)\n", *slot, fp->slots_per_frame);
start_meas(&ru->rx_fhaul);
int nb = ru->nb_rx * ru->num_beams_period;
void *rxp[nb];
for (int i = 0; i < nb; i++)
rxp[i] = (void *)&ru->common.rxdata[i][get_samples_slot_timestamp(fp, *slot)];
openair0_timestamp_t old_ts = proc->timestamp_rx;
LOG_D(PHY,"Reading %d samples for slot %d (%p)\n", samples_per_slot, *slot, rxp[0]);
openair0_timestamp_t ts;
unsigned int rxs;
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb);
metadata mt = {.slot = *slot, .frame = *frame};
if (ru->num_beams_period > 1) {
void *rxp_flat[ru->num_beams_period * ru->nb_rx];
void **rxp[ru->num_beams_period];
for (int j = 0; j < ru->num_beams_period; j++) {
for (int i = 0; i < ru->nb_rx; i++)
rxp_flat[i + j * ru->nb_rx] = (void *)&ru->common.rxdata[i + j * ru->nb_rx][get_samples_slot_timestamp(fp, *slot)];
rxp[j] = &rxp_flat[j * ru->nb_rx];
}
rxs = ru->rfdevice.trx_read_beams_func(&ru->rfdevice, &ts, (void ***)rxp, samples_per_slot, ru->nb_rx, ru->num_beams_period);
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0][0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
} else {
void *rxp[ru->nb_rx];
for (int i = 0; i < ru->nb_rx; i++)
rxp[i] = (void *)&ru->common.rxdata[i][get_samples_slot_timestamp(fp, *slot)];
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, ru->nb_rx);
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
}
proc->timestamp_rx = ts-ru->ts_offset;
if (rxs != samples_per_slot)
@@ -422,17 +433,28 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
proc->first_rx = 0;
*frame = proc->frame_rx;
*slot = proc->tti_rx;
samples_per_slot = get_samples_per_slot(*slot, fp); // slot number might change depending on the timestamp
// Align to slot boundary
uint64_t samples_to_slot_boundary = 0;
uint64_t sample_offset_within_frame = proc->timestamp_rx % fp->samples_per_frame;
uint64_t sample_offset_within_slot = sample_offset_within_frame - get_samples_slot_timestamp(fp, *slot);
if (sample_offset_within_slot > 0) {
samples_to_slot_boundary = get_samples_per_slot(*slot, fp) - sample_offset_within_slot;
samples_to_slot_boundary = samples_per_slot - sample_offset_within_slot;
LOG_A(NR_PHY, "Aligning to the slot boundary %lu\n", samples_to_slot_boundary);
// Read and discard the samples in the first_rx to align to the slot boundary
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_to_slot_boundary, nb);
if (ru->num_beams_period > 1) {
void *rxp_skip_b[ru->num_beams_period][ru->nb_rx];
rxs = ru->rfdevice.trx_read_beams_func(&ru->rfdevice,
&ts,
(void ***)rxp_skip_b,
samples_to_slot_boundary,
ru->nb_rx,
ru->num_beams_period);
} else {
void *rxp_skip[ru->nb_rx];
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp_skip, samples_to_slot_boundary, ru->nb_rx);
}
if (rxs != samples_to_slot_boundary)
LOG_E(PHY, "rx_rf: Asked for %ld samples, got %d from USRP\n", samples_to_slot_boundary, rxs);
@@ -442,64 +464,92 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
*slot = (*slot + 1) % fp->slots_per_frame;
}
}
metadata mt = {.slot = *slot, .frame = *frame};
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
if (rxs != samples_per_slot) {
//exit_fun( "problem receiving samples" );
LOG_E(PHY, "problem receiving samples\n");
}
stop_meas(&ru->rx_fhaul);
}
static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
static void ctrl_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
radio_tx_gpio_flag_t flags_gpio = 0;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
openair0_config_t *cfg0 = &ru->openair0_cfg;
int num_events = 0;
int event_symbol[fp->symbols_per_slot];
int event_counts[fp->symbols_per_slot];
int beam_events[fp->symbols_per_slot][ru->num_beams_period];
switch (cfg0->gpio_controller) {
case RU_GPIO_CONTROL_GENERIC:
// currently we switch beams at the beginning of a slot and we take the beam index of the first symbol of this slot
// we only send the beam to the gpio if the beam is different from the previous slot
int last_applied_beams[ru->num_beams_period];
for (int i = 0; i < ru->num_beams_period; i++)
last_applied_beams[i] = -1;
if (ru->common.beam_id) {
int prev_slot = (slot - 1 + fp->slots_per_frame) % fp->slots_per_frame;
const int *beam_ids = ru->common.beam_id[0];
int prev_beam = beam_ids[prev_slot * fp->symbols_per_slot];
int beam = beam_ids[slot * fp->symbols_per_slot];
if (prev_beam != beam) {
flags_gpio = beam | TX_GPIO_CHANGE; // enable change of gpio
LOG_I(HW, "slot %d, beam %d\n", slot, ru->common.beam_id[0][slot * fp->symbols_per_slot]);
}
}
break;
int first_available_sym = 0;
bool idle_beam = true;
for (int j = 0; j < fp->symbols_per_slot; j++) {
int current_beams[ru->num_beams_period];
int active_count = 0;
bool differs = false;
case RU_GPIO_CONTROL_INTERDIGITAL: {
// the beam index is written in bits 8-10 of the flags
// bit 11 enables the gpio programming
int beam = 0;
if ((slot % 10 == 0) && ru->common.beam_id && (ru->common.beam_id[0][slot * fp->symbols_per_slot] < 64)) {
// beam = ru->common.beam_id[0][slot*fp->symbols_per_slot] | 64;
beam = 1024; // hardcoded now for beam32 boresight
// beam = 127; //for the sake of trying beam63
LOG_D(HW, "slot %d, beam %d\n", slot, beam);
}
flags_gpio = beam | TX_GPIO_CHANGE;
// flags_gpio |= beam << 8; // MSB 8 bits are used for beam
LOG_I(HW, "slot %d, beam %d, flags_gpio %d\n", slot, beam, flags_gpio);
break;
for (int i = 0; i < ru->num_beams_period; i++) {
current_beams[i] = ru->common.beam_id[i][slot * fp->symbols_per_slot + j];
if (current_beams[i] != last_applied_beams[i])
differs = true;
if (current_beams[i] != -1)
active_count++;
}
if (active_count > 0) {
if (differs) {
event_symbol[num_events] = first_available_sym;
event_counts[num_events] = 0;
for (int i = 0; i < ru->num_beams_period; i++) {
if (current_beams[i] != -1) {
beam_events[num_events][event_counts[num_events]] = current_beams[i];
event_counts[num_events]++;
}
last_applied_beams[i] = current_beams[i];
}
num_events++;
}
idle_beam = false;
first_available_sym = j;
} else {
if (!idle_beam) {
first_available_sym = j;
idle_beam = true;
for (int i = 0; i < ru->num_beams_period; i++)
last_applied_beams[i] = -1;
}
}
default:
AssertFatal(false, "illegal GPIO controller %d\n", cfg0->gpio_controller);
}
return flags_gpio;
nfapi_nr_config_request_scf_t *cfg = &ru->config;
uint64_t slot_base_ts = timestamp + ru->ts_offset;
if (cfg->cell_config.frame_duplex_type.value == TDD
&& nr_slot_select(cfg, frame, slot % fp->slots_per_frame) == NR_DOWNLINK_SLOT
&& nr_slot_select(cfg, frame, (slot + fp->slots_per_frame - 1) % fp->slots_per_frame) == NR_UPLINK_SLOT
&& !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM) {
slot_base_ts -= ru->sf_extension;
}
for (int e = 0; e < num_events; e++) {
if (e > 0 && event_symbol[e] == event_symbol[e-1])
continue;
uint64_t symbol_offset = 0;
if (event_symbol[e] > 0)
symbol_offset = get_samples_symbol_duration(fp, slot, 0, event_symbol[e]);
uint64_t event_ts = slot_base_ts + symbol_offset;
for (int n = 0; n < event_counts[e]; n++)
LOG_D(NR_PHY,
"RU Control [%d.%d]: Trigger Sym %d, Beam %d at TS %lu\n",
frame,
slot,
event_symbol[e],
beam_events[e][n],
event_ts);
ru->rfdevice.trx_set_beams2(&ru->rfdevice, beam_events[e], event_counts[e], event_ts);
}
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
static void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
@@ -514,7 +564,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
int sf_extension = 0;
int siglen = get_samples_per_slot(slot, fp);
radio_tx_burst_flag_t flags_burst = TX_BURST_INVALID;
radio_tx_gpio_flag_t flags_gpio = 0;
if (cfg->cell_config.frame_duplex_type.value == TDD && !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM) {
int slot_type = nr_slot_select(cfg,frame,slot%fp->slots_per_frame);
@@ -558,24 +607,41 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
}
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
flags_gpio = get_gpio_flags(ru, slot);
const int flags = flags_burst | (flags_gpio << 4);
proc->first_tx = 0;
int nt = ru->nb_tx * ru->num_beams_period;
void *txp[nt];
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);
double sig_en = 0;
uint32_t txs;
if (ru->num_beams_period > 1) {
void *txp_flat[ru->num_beams_period * ru->nb_tx];
void **txp[ru->num_beams_period];
for (int b = 0; b < ru->num_beams_period; b++) {
for (int i = 0; i < ru->nb_tx; i++)
txp_flat[b * ru->nb_tx + i] = (void *)&ru->common.txdata[i + b * ru->nb_tx][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
txp[b] = &txp_flat[b * ru->nb_tx];
}
sig_en = (double)signal_energy(txp[0][0], siglen + sf_extension);
txs = ru->rfdevice.trx_write_beams_func(&ru->rfdevice,
timestamp + ru->ts_offset - sf_extension,
(void ***)txp,
siglen + sf_extension,
ru->nb_tx,
ru->num_beams_period,
flags_burst);
} else {
void *txp[ru->nb_tx];
for (int i = 0; i < ru->nb_tx; i++)
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
sig_en = (double)signal_energy(txp[0], siglen + sf_extension);
// prepare tx buffer pointers
txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
timestamp + ru->ts_offset - sf_extension,
txp,
siglen + sf_extension,
ru->nb_tx,
flags_burst);
}
// prepare tx buffer pointers
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
timestamp + ru->ts_offset - sf_extension,
txp,
siglen + sf_extension,
nt,
flags);
LOG_D(PHY,
"[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, "
"returned %d, E %f\n",
@@ -585,10 +651,10 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
slot,
proc->frame_tx_unwrap,
slot,
flags,
flags_burst,
siglen + sf_extension,
txs,
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
10 * log10(sig_en));
}
static void fill_rf_config(RU_t *ru, char *rf_config_file)
@@ -742,6 +808,25 @@ int setup_RU_buffers(RU_t *ru)
return(0);
}
void ru_ctrl_func(void *param)
{
processingData_RU_t *info = (processingData_RU_t *) param;
int frame = info->frame_tx;
int slot = info->slot_tx;
LOG_D(PHY,"ru_ctrl_func: frame = %d, slot = %d\n", frame, slot);
RU_t *ru = info->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
if (ru->gNB_list[0]->common_vars.analog_bf) {
for (int i = 0; i < ru->num_beams_period; i++) {
memcpy((void*) &ru->common.beam_id[i][slot * fp->symbols_per_slot],
(void*) &ru->gNB_list[0]->common_vars.beam_id[i][slot * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
}
if (ru->fh_south_ctrl)
ru->fh_south_ctrl(ru, frame, slot, info->timestamp_tx);
}
void ru_tx_func(void *param)
{
processingData_RU_t *info = (processingData_RU_t *) param;
@@ -1134,6 +1219,7 @@ void set_function_spec_param(RU_t *ru)
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->fh_south_ctrl = ctrl_rf; // local sunchronous RF control
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

View File

@@ -310,7 +310,7 @@ extern int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
extern int usrp_tx_thread;
extern int sf_ahead;
extern int oai_exit;
void ru_ctrl_func(void *param);
void ru_tx_func(void *param);
void configure_ru(void *, void *arg);
void configure_rru(void *, void *arg);

View File

@@ -48,30 +48,6 @@ Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://..
`libldpc_aal.so` has its decoder and its encoder implemented in [nrLDPC_coding_aal.c](file://../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c).
loading `libldpc_xdma.so` instead of `libldpc.so`:
`make ldpc_xdma` or `ninja ldpc_xdma`
This command creates the `libldpc_xdma.so` shared library.
```
ninja ldpc_xdma
[2/2] Linking C shared module libldpc_xdma.so
```
At runtime, to successfully use the xdma, you need to install vendor specific drivers and tools.\
Please refer to the dedicated documentation at [LDPC_XDMA_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_XDMA_OFFLOAD_SETUP.md).
```
./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.[0].serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
`libldpc_xdma.so` has its decoder implemented in [nrLDPC_coding_xdma.c](file://../nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c).\
Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c).
*Note: `libldpc_xdma.so` relies on a segment coding library for encoding.*
*The segment coding library is `libldpc.so` by default but it can be chosen with option `--nrLDPC_coding_xdma.encoder_shlibversion` followed by the library version - like with `--loder.ldpc.shlibversion` in the segment coding case above -*
#### Examples of ldpc shared lib selection when running ldpctest:
Slot coding libraries cannot be used yet within ldpctest.
@@ -86,8 +62,6 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version>
`libldpc_aal.so` is completed.
`libldpc_xdma.so` is completed.
## LDPC segment coding
The interface of the library is defined in [nrLDPC_defs.h](file://../nrLDPC_defs.h) as typedefs of the functions of the interface.
The name of the functions implementing these typedefs can be found in [nrLDPC_extern.h](file://../nrLDPC_extern.h).

View File

@@ -1,5 +1,4 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
add_subdirectory(nrLDPC_coding_segment)
add_subdirectory(nrLDPC_coding_xdma)
add_subdirectory(nrLDPC_coding_aal)

View File

@@ -1,31 +0,0 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
##########################################################
# LDPC offload library - XDMA
##########################################################
add_boolean_option(ENABLE_LDPC_XDMA OFF "Build support for LDPC Offload to XDMA library" OFF)
if (ENABLE_LDPC_XDMA)
add_library(ldpc_xdma MODULE
nrLDPC_coding_xdma_offload.c
nrLDPC_coding_xdma.c
../nrLDPC_coding_segment/nr_rate_matching.c
../nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c
../../nrLDPC_encoder/ldpc_encoder_optim8segmulti.c
)
target_include_directories(ldpc_xdma PRIVATE ../nrLDPC_coding_segment)
target_link_libraries(ldpc_xdma PRIVATE ldpc_gen_HEADERS log_headers)
set_target_properties(ldpc_xdma PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_dependencies(nr-softmodem ldpc_xdma)
add_dependencies(nr-uesoftmodem ldpc_xdma)
add_dependencies(nr_ulsim ldpc_xdma)
add_dependencies(nr_ulschsim ldpc_xdma)
add_dependencies(nr_dlsim ldpc_xdma)
add_dependencies(nr_dlschsim ldpc_xdma)
endif()

View File

@@ -1,414 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c
* \brief Top-level routines for decoding LDPC (ULSCH) transport channels
* decoding implemented using a FEC IP core on FPGA through XDMA driver
*/
// [from gNB coding]
#include <syscall.h>
#include <nr_rate_matching.h>
#include "PHY/CODING/coding_defs.h"
#include "PHY/CODING/coding_extern.h"
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma_offload.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "common/utils/LOG/log.h"
#include "defs.h"
// #define DEBUG_ULSCH_DECODING
// #define gNB_DEBUG_TRACE
#define OAI_UL_LDPC_MAX_NUM_LLR 27000 // 26112 // NR_LDPC_NCOL_BG1*NR_LDPC_ZMAX = 68*384
// #define DEBUG_CRC
#ifdef DEBUG_CRC
#define PRINT_CRC_CHECK(a) a
#else
#define PRINT_CRC_CHECK(a)
#endif
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h"
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h"
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
// Global var to limit the rework of the dirty legacy code
int num_threads_prepare_max = 0;
char *user_device = NULL;
char *enc_read_device = NULL;
char *enc_write_device = NULL;
char *dec_read_device = NULL;
char *dec_write_device = NULL;
/*!
* \typedef args_fpga_decode_prepare_t
* \struct args_fpga_decode_prepare_s
* \brief arguments structure for passing arguments to the nr_ulsch_FPGA_decoding_prepare_blocks function
*/
typedef struct args_fpga_decode_prepare_s {
nrLDPC_TB_decoding_parameters_t *TB_params; /*!< transport blocks parameters */
uint8_t *multi_indata; /*!< pointer to the head of the block destination array that is then passed to the FPGA decoding */
int no_iteration_ldpc; /*!< pointer to the number of iteration set by this function */
uint32_t r_first; /*!< index of the first block to be prepared within this function */
uint32_t r_span; /*!< number of blocks to be prepared within this function */
int r_offset; /*!< r index expressed in bits */
int input_CBoffset; /*!< */
int Kc; /*!< ratio between the number of columns in the parity check graph and the lifting size */
int Kprime; /*!< size of payload and CRC bits in a code block */
task_ans_t *ans; /*!< pointer to the answer that is used by thread pool to detect job completion */
} args_fpga_decode_prepare_t;
int32_t nrLDPC_coding_init(void);
int32_t nrLDPC_coding_shutdown(void);
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *slot_params, int frame_rx, int slot_rx);
// int32_t nrLDPC_coding_encoder(void);
int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int slot_rx, tpool_t *ldpc_threadPool);
void nr_ulsch_FPGA_decoding_prepare_blocks(void *args);
int32_t nrLDPC_coding_init(void)
{
paramdef_t LoaderParams[] = {
{"num_threads_prepare", NULL, 0, .iptr = &num_threads_prepare_max, .defintval = 0, TYPE_INT, 0, NULL},
{"user_device", NULL, 0, .strptr = &user_device, .defstrval = DEVICE_NAME_DEFAULT_USER, TYPE_STRING, 0, NULL},
{"enc_read_device", NULL, 0, .strptr = &enc_read_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_READ, TYPE_STRING, 0, NULL},
{"enc_write_device", NULL, 0, .strptr = &enc_write_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_WRITE, TYPE_STRING, 0, NULL},
{"dec_read_device", NULL, 0, .strptr = &dec_read_device, .defstrval = DEVICE_NAME_DEFAULT_DEC_READ, TYPE_STRING, 0, NULL},
{"dec_write_device", NULL, 0, .strptr = &dec_write_device, .defstrval = DEVICE_NAME_DEFAULT_DEC_WRITE, TYPE_STRING, 0, NULL}};
config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_xdma");
AssertFatal(num_threads_prepare_max != 0, "nrLDPC_coding_xdma.num_threads_prepare was not provided");
return 0;
}
int32_t nrLDPC_coding_shutdown(void)
{
return 0;
}
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *slot_params, int frame_rx, int slot_rx)
{
int nbDecode = 0;
for (int ULSCH_id = 0; ULSCH_id < slot_params->nb_TBs; ULSCH_id++)
nbDecode += decoder_xdma(&slot_params->TBs[ULSCH_id], frame_rx, slot_rx, slot_params->threadPool);
return nbDecode;
}
/*
int32_t nrLDPC_coding_encoder(void)
{
return 0;
}
*/
int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int slot_rx, tpool_t *ldpc_threadPool)
{
const uint32_t K = TB_params->K;
const int Kc = TB_params->BG == 2 ? 52 : 68;
int r_offset = 0, offset = 0;
int Kprime = K - TB_params->F;
// FPGA parameter preprocessing
static uint8_t multi_indata[27000 * 25]; // FPGA input data
static uint8_t multi_outdata[1100 * 25]; // FPGA output data
int bg_len = TB_params->BG == 1 ? 22 : 10;
// Calc input CB offset
int input_CBoffset = TB_params->Z * Kc * 8;
if ((input_CBoffset & 0x7F) == 0)
input_CBoffset = input_CBoffset / 8;
else
input_CBoffset = 16 * ((input_CBoffset / 128) + 1);
DecIFConf dec_conf = {0};
dec_conf.Zc = TB_params->Z;
dec_conf.BG = TB_params->BG;
dec_conf.max_iter = TB_params->max_ldpc_iterations;
dec_conf.numCB = TB_params->C;
// input soft bits length, Zc x 66 - length of filler bits
dec_conf.numChannelLls = (Kprime - 2 * TB_params->Z) + (Kc * TB_params->Z - K);
// filler bits length
dec_conf.numFillerBits = TB_params->F;
dec_conf.max_schedule = 0;
dec_conf.SetIdx = 12;
dec_conf.nRows = (dec_conf.BG == 1) ? 46 : 42;
dec_conf.user_device = user_device;
dec_conf.enc_read_device = enc_read_device;
dec_conf.enc_write_device = enc_write_device;
dec_conf.dec_read_device = dec_read_device;
dec_conf.dec_write_device = dec_write_device;
int out_CBoffset = dec_conf.Zc * bg_len;
if ((out_CBoffset & 0x7F) == 0)
out_CBoffset = out_CBoffset / 8;
else
out_CBoffset = 16 * ((out_CBoffset / 128) + 1);
#ifdef LDPC_DATA
printf("\n------------------------\n");
printf("BG:\t\t%d\n", dec_conf.BG);
printf("TB_params->C: %d\n", TB_params->C);
printf("TB_params->K: %d\n", TB_params->K);
printf("TB_params->Z: %d\n", TB_params->Z);
printf("TB_params->F: %d\n", TB_params->F);
printf("numChannelLls:\t %d = (%d - 2 * %d) + (%d * %d - %d)\n",
dec_conf.numChannelLls,
Kprime,
TB_params->Z,
Kc,
TB_params->Z,
K);
printf("numFillerBits:\t %d\n", TB_params->F);
printf("------------------------\n");
// ===================================
// debug mode
// ===================================
FILE *fptr_llr, *fptr_ldpc;
fptr_llr = fopen("../../../cmake_targets/log/ulsim_ldpc_llr.txt", "w");
fptr_ldpc = fopen("../../../cmake_targets/log/ulsim_ldpc_output.txt", "w");
// ===================================
#endif
int length_dec = lenWithCrc(TB_params->C, TB_params->A);
uint8_t crc_type = crcType(TB_params->C, TB_params->A);
int no_iteration_ldpc = 2;
uint32_t num_threads_prepare = 0;
// calculate required number of jobs
uint32_t r_while = 0;
while (r_while < TB_params->C) {
// calculate number of segments processed in the new job
uint32_t modulus = (TB_params->C - r_while) % (num_threads_prepare_max - num_threads_prepare);
uint32_t quotient = (TB_params->C - r_while) / (num_threads_prepare_max - num_threads_prepare);
uint32_t r_span_max = modulus == 0 ? quotient : quotient + 1;
// saturate to be sure to not go above C
uint32_t r_span = TB_params->C - r_while < r_span_max ? TB_params->C - r_while : r_span_max;
// increment
num_threads_prepare++;
r_while += r_span;
}
args_fpga_decode_prepare_t arr[num_threads_prepare];
task_ans_t ans[num_threads_prepare];
memset(ans, 0, num_threads_prepare * sizeof(task_ans_t));
thread_info_tm_t t_info = {.buf = (uint8_t *)arr, .len = 0, .cap = num_threads_prepare, .ans = ans};
// start the prepare jobs
uint32_t r_remaining = 0;
for (uint32_t r = 0; r < TB_params->C; r++) {
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
if (r_remaining == 0) {
// TODO: int nr_tti_rx = 0;
args_fpga_decode_prepare_t *args = &((args_fpga_decode_prepare_t *)t_info.buf)[t_info.len];
DevAssert(t_info.len < t_info.cap);
args->ans = &t_info.ans[t_info.len];
t_info.len += 1;
args->TB_params = TB_params;
args->multi_indata = multi_indata;
args->no_iteration_ldpc = no_iteration_ldpc;
args->r_first = r;
uint32_t modulus = (TB_params->C - r) % (num_threads_prepare_max - num_threads_prepare);
uint32_t quotient = (TB_params->C - r) / (num_threads_prepare_max - num_threads_prepare);
uint32_t r_span_max = modulus == 0 ? quotient : quotient + 1;
uint32_t r_span = TB_params->C - r < r_span_max ? TB_params->C - r : r_span_max;
args->r_span = r_span;
args->r_offset = r_offset;
args->input_CBoffset = input_CBoffset;
args->Kc = Kc;
args->Kprime = Kprime;
r_remaining = r_span;
task_t t = {.func = &nr_ulsch_FPGA_decoding_prepare_blocks, .args = args};
pushTpool(ldpc_threadPool, t);
LOG_D(PHY, "Added %d block(s) to prepare for decoding, in pipe: %d to %d\n", r_span, r, r + r_span - 1);
}
r_offset += segment_params->E;
offset += ((K >> 3) - (TB_params->F >> 3) - ((TB_params->C > 1) ? 3 : 0));
r_remaining -= 1;
}
// reset offset in order to properly fill the output array later
offset = 0;
DevAssert(num_threads_prepare == t_info.len);
// wait for the prepare jobs to complete
join_task_ans(t_info.ans);
for (uint32_t job = 0; job < num_threads_prepare; job++) {
args_fpga_decode_prepare_t *args = &arr[job];
if (args->no_iteration_ldpc >= TB_params->max_ldpc_iterations)
no_iteration_ldpc = TB_params->max_ldpc_iterations;
}
// launch decode with FPGA
LOG_I(PHY, "Run the LDPC ------[FPGA version]------\n");
//==================================================================
// Xilinx FPGA LDPC decoding function -> nrLDPC_decoder_FPGA_PYM()
//==================================================================
start_meas(&TB_params->segments[0].ts_ldpc_decode);
nrLDPC_decoder_FPGA_PYM(&multi_indata[0], &multi_outdata[0], dec_conf);
// printf("Xilinx FPGA -> CB = %d\n", harq_process->C);
stop_meas(&TB_params->segments[0].ts_ldpc_decode);
*TB_params->processedSegments = 0;
for (uint32_t r = 0; r < TB_params->C; r++) {
// ------------------------------------------------------------
// --------------------- copy FPGA output ---------------------
// ------------------------------------------------------------
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
if (check_crc(multi_outdata, length_dec, crc_type)) {
#ifdef DEBUG_CRC
LOG_I(PHY, "Segment %d CRC OK\n", r);
#endif
no_iteration_ldpc = 2;
} else {
#ifdef DEBUG_CRC
LOG_I(PHY, "segment %d CRC NOK\n", r);
#endif
no_iteration_ldpc = TB_params->max_ldpc_iterations;
}
for (int i = 0; i < out_CBoffset; i++) {
segment_params->c[i] = multi_outdata[i + r * out_CBoffset];
}
segment_params->decodeSuccess = (no_iteration_ldpc < TB_params->max_ldpc_iterations);
if (segment_params->decodeSuccess) {
*TB_params->processedSegments = *TB_params->processedSegments + 1;
}
}
return 0;
}
/*!
* \fn nr_ulsch_FPGA_decoding_prepare_blocks(void *args)
* \brief prepare blocks for LDPC decoding on FPGA
*
* \param args pointer to the arguments of the function in a structure of type args_fpga_decode_prepare_t
*/
void nr_ulsch_FPGA_decoding_prepare_blocks(void *args)
{
// extract the arguments
args_fpga_decode_prepare_t *arguments = (args_fpga_decode_prepare_t *)args;
nrLDPC_TB_decoding_parameters_t *TB_params = arguments->TB_params;
uint8_t Qm = TB_params->Qm;
uint8_t BG = TB_params->BG;
uint8_t rv_index = TB_params->rv_index;
uint8_t max_ldpc_iterations = TB_params->max_ldpc_iterations;
uint32_t tbslbrm = TB_params->tbslbrm;
uint32_t K = TB_params->K;
uint32_t Z = TB_params->Z;
uint32_t F = TB_params->F;
uint32_t C = TB_params->C;
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[0];
short *ulsch_llr = segment_params->llr;
uint8_t *multi_indata = arguments->multi_indata;
int no_iteration_ldpc = arguments->no_iteration_ldpc;
uint32_t r_first = arguments->r_first;
uint32_t r_span = arguments->r_span;
int r_offset = arguments->r_offset;
int input_CBoffset = arguments->input_CBoffset;
int Kc = arguments->Kc;
int Kprime = arguments->Kprime;
int16_t z[68 * 384 + 16] __attribute__((aligned(16)));
simde__m128i *pv = (simde__m128i *)&z;
// the function processes r_span blocks starting from block at index r_first in ulsch_llr
for (uint32_t r = r_first; r < (r_first + r_span); r++) {
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
// ----------------------- FPGA pre process ------------------------
simde__m128i ones = simde_mm_set1_epi8(255); // Generate a vector with all elements set to 255
simde__m128i *temp_multi_indata = (simde__m128i *)&multi_indata[r * input_CBoffset];
// -----------------------------------------------------------------
// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1)
int16_t harq_e[segment_params->E];
// -------------------------------------------------------------------------------------------
// deinterleaving
// -------------------------------------------------------------------------------------------
start_meas(&segment_params->ts_deinterleave);
nr_deinterleaving_ldpc(segment_params->E, Qm, harq_e, ulsch_llr + r_offset);
stop_meas(&segment_params->ts_deinterleave);
// -------------------------------------------------------------------------------------------
// dematching
// -------------------------------------------------------------------------------------------
start_meas(&segment_params->ts_rate_unmatch);
if (nr_rate_matching_ldpc_rx(tbslbrm,
BG,
Z,
segment_params->d,
harq_e,
C,
rv_index,
*segment_params->d_to_be_cleared,
segment_params->E,
F,
K - F - 2 * Z)
== -1) {
stop_meas(&segment_params->ts_rate_unmatch);
LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n");
no_iteration_ldpc = max_ldpc_iterations;
arguments->no_iteration_ldpc = no_iteration_ldpc;
return;
} else {
stop_meas(&segment_params->ts_rate_unmatch);
}
*segment_params->d_to_be_cleared = false;
memset(segment_params->c, 0, K >> 3);
// set first 2*Z_c bits to zeros
memset(&z[0], 0, 2 * Z * sizeof(int16_t));
// set Filler bits
memset((&z[0] + Kprime), 127, F * sizeof(int16_t));
// Move coded bits before filler bits
memcpy((&z[0] + 2 * Z), segment_params->d, (Kprime - 2 * Z) * sizeof(int16_t));
// skip filler bits
memcpy((&z[0] + K), segment_params->d + (K - 2 * Z), (Kc * Z - K) * sizeof(int16_t));
// Saturate coded bits before decoding into 8 bits values
for (int i = 0, j = 0; j < ((Kc * Z) >> 4); i += 2, j++) {
temp_multi_indata[j] =
simde_mm_xor_si128(simde_mm_packs_epi16(pv[i], pv[i + 1]),
simde_mm_cmpeq_epi32(ones,
ones)); // Perform NOT operation and write the result to temp_multi_indata[j]
}
// the last bytes before reaching "Kc * harq_process->Z" should not be written 128 bits at a time to avoid overwritting the
// following block in multi_indata
simde__m128i tmp =
simde_mm_xor_si128(simde_mm_packs_epi16(pv[2 * ((Kc * Z) >> 4)], pv[2 * ((Kc * Z) >> 4) + 1]),
simde_mm_cmpeq_epi32(ones,
ones)); // Perform NOT operation and write the result to temp_multi_indata[j]
uint8_t *tmp_p = (uint8_t *)&tmp;
for (int i = 0, j = ((Kc * Z) & 0xfffffff0); j < Kc * Z; i++, j++) {
multi_indata[r * input_CBoffset + j] = tmp_p[i];
}
r_offset += segment_params->E;
}
arguments->no_iteration_ldpc = no_iteration_ldpc;
}

View File

@@ -1,783 +0,0 @@
/*
* Copyright (c) 2016-present, Xilinx, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license
* the terms of the BSD Licence are reported below:
*
* BSD License
*
* For Xilinx DMA IP software
*
* Copyright (c) 2016-present, Xilinx, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name Xilinx nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _DEFAULT_SOURCE
#define _XOPEN_SOURCE 500
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <byteswap.h>
#include <signal.h>
#include <ctype.h>
#include <termios.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include "xdma_diag.h"
#include "nrLDPC_coding_xdma_offload.h"
#include "common/utils/assertions.h"
typedef unsigned long long U64;
void* map_base;
int fd;
int fd_enc_write, fd_enc_read;
char *dev_enc_write, *dev_enc_read;
int fd_dec_write, fd_dec_read;
char *dev_dec_write, *dev_dec_read;
char allocated_write[24 * 1024] __attribute__((aligned(4096)));
char allocated_read[24 * 1024 * 3] __attribute__((aligned(4096)));
// dma_from_device.c
// [Start] #include "dma_utils.c" ===================================
/*
* man 2 write:
* On Linux, write() (and similar system calls) will transfer at most
* 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes
* actually transferred. (This is true on both 32-bit and 64-bit
* systems.)
*/
#define RW_MAX_SIZE 0x7ffff000
int verbose = 0;
uint64_t getopt_integer(char* optarg)
{
int rc;
uint64_t value;
rc = sscanf(optarg, "0x%lx", &value);
if (rc <= 0)
rc = sscanf(optarg, "%lu", &value);
return value;
}
ssize_t read_to_buffer(char* fname, int fd, char* buffer, uint64_t size, uint64_t base)
{
ssize_t rc;
uint64_t count = 0;
char* buf = buffer;
off_t offset = base;
while (count < size) {
uint64_t bytes = size - count;
if (bytes > RW_MAX_SIZE)
bytes = RW_MAX_SIZE;
if (offset) {
rc = lseek(fd, offset, SEEK_SET);
if (rc != offset) {
fprintf(stderr, "%s, seek off 0x%lx != 0x%lx.\n", fname, rc, offset);
perror("seek file");
return -EIO;
}
}
/* read data from file into memory buffer */
rc = read(fd, buf, bytes);
if (rc != bytes) {
fprintf(stderr, "%s, R off 0x%lx, 0x%lx != 0x%lx.\n", fname, count, rc, bytes);
perror("read file");
return -EIO;
}
count += bytes;
buf += bytes;
offset += bytes;
}
if (count != size) {
fprintf(stderr, "%s, R failed 0x%lx != 0x%lx.\n", fname, count, size);
return -EIO;
}
return count;
}
ssize_t write_from_buffer(char* fname, int fd, char* buffer, uint64_t size, uint64_t base)
{
ssize_t rc;
uint64_t count = 0;
char* buf = buffer;
off_t offset = base;
while (count < size) {
uint64_t bytes = size - count;
if (bytes > RW_MAX_SIZE)
bytes = RW_MAX_SIZE;
if (offset) {
rc = lseek(fd, offset, SEEK_SET);
if (rc != offset) {
fprintf(stderr, "%s, seek off 0x%lx != 0x%lx.\n", fname, rc, offset);
perror("seek file");
return -EIO;
}
}
/* write data to file from memory buffer */
rc = write(fd, buf, bytes);
if (rc != bytes) {
fprintf(stderr, "%s, W off 0x%lx, 0x%lx != 0x%lx.\n", fname, offset, rc, bytes);
perror("write file");
return -EIO;
}
count += bytes;
buf += bytes;
offset += bytes;
}
if (count != size) {
fprintf(stderr, "%s, R failed 0x%lx != 0x%lx.\n", fname, count, size);
return -EIO;
}
return count;
}
// [End] #include "dma_utils.c" ===================================
int test_dma_enc_read(char* EncOut, EncIPConf Confparam)
{
ssize_t rc;
void* virt_addr;
uint64_t size;
uint32_t writeval;
uint32_t Z_val;
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a;
uint16_t z_set;
uint32_t ctrl_data;
uint32_t CB_num = CB_PROCESS_NUMBER;
// this values should be given by Shane
max_schedule = 0;
mb = Confparam.mb;
id = CB_num;
bg = Confparam.BGSel - 1;
z_set = Confparam.z_set - 1;
z_j = Confparam.z_j;
if (z_set == 0)
z_a = 2;
else if (z_set == 1)
z_a = 3;
else if (z_set == 2)
z_a = 5;
else if (z_set == 3)
z_a = 7;
else if (z_set == 4)
z_a = 9;
else if (z_set == 5)
z_a = 11;
else if (z_set == 6)
z_a = 13;
else
z_a = 15;
if (bg == 0)
kb = 22;
else if (bg == 1)
kb = 10;
else if (bg == 2)
kb = 9;
else if (bg == 3)
kb = 8;
else
kb = 6;
mb = Confparam.kb_1 + kb;
Z_val = (unsigned int)(z_a << z_j);
ctrl_data = (max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (bg << 6) | (z_set << 3) | z_j;
uint32_t OutDataNUM = Z_val * mb;
uint32_t Out_dwNumItems_p128;
uint32_t Out_dwNumItems;
if ((OutDataNUM & 0x7F) == 0)
Out_dwNumItems_p128 = OutDataNUM >> 5;
else
Out_dwNumItems_p128 = ((OutDataNUM >> 7) + 1) << 2;
Out_dwNumItems = ((Out_dwNumItems_p128 << 2) * CB_num);
size = Out_dwNumItems;
writeval = ctrl_data;
/* calculate the virtual address to be accessed */
virt_addr = map_base + OFFSET_ENC_OUT;
/* swap 32-bit endianess if host is not little-endian */
writeval = htoll(writeval);
*((uint32_t*)virt_addr) = writeval;
if (fd_enc_read < 0) {
fprintf(stderr, "unable to open device %s, %d.\n", dev_enc_read, fd_enc_read);
perror("open device");
return -EINVAL;
}
/* lseek & read data from AXI MM into buffer using SGDMA */
rc = read_to_buffer(dev_enc_read, fd_enc_read, EncOut, size, 0);
if (rc < 0)
goto out;
rc = 0;
out:
return rc;
}
int test_dma_enc_write(char* data, EncIPConf Confparam)
{
ssize_t rc;
void* virt_addr;
uint64_t size;
uint32_t writeval;
uint32_t Z_val;
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a;
uint16_t z_set;
uint32_t ctrl_data;
uint32_t CB_num = CB_PROCESS_NUMBER;
// this values should be given by Shane
max_schedule = 0;
mb = Confparam.mb;
id = CB_num;
bg = Confparam.BGSel - 1;
z_set = Confparam.z_set - 1;
z_j = Confparam.z_j;
if (z_set == 0)
z_a = 2;
else if (z_set == 1)
z_a = 3;
else if (z_set == 2)
z_a = 5;
else if (z_set == 3)
z_a = 7;
else if (z_set == 4)
z_a = 9;
else if (z_set == 5)
z_a = 11;
else if (z_set == 6)
z_a = 13;
else
z_a = 15;
if (bg == 0)
kb = 22;
else if (bg == 1)
kb = 10;
else if (bg == 2)
kb = 9;
else if (bg == 3)
kb = 8;
else
kb = 6;
mb = Confparam.kb_1 + kb;
Z_val = (unsigned int)(z_a << z_j);
ctrl_data = (max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (bg << 6) | (z_set << 3) | z_j;
uint32_t InDataNUM = Z_val * kb;
uint32_t In_dwNumItems_p128;
uint32_t In_dwNumItems;
if ((InDataNUM & 0x7F) == 0)
In_dwNumItems_p128 = InDataNUM >> 5;
else
In_dwNumItems_p128 = ((InDataNUM >> 7) + 1) << 2;
In_dwNumItems = ((In_dwNumItems_p128 << 2) * CB_num);
size = In_dwNumItems;
writeval = ctrl_data;
/* calculate the virtual address to be accessed */
virt_addr = map_base + OFFSET_ENC_IN;
/* swap 32-bit endianess if host is not little-endian */
writeval = htoll(writeval);
*((uint32_t*)virt_addr) = writeval;
if (fd_enc_write < 0) {
fprintf(stderr, "unable to open device %s, %d.\n", dev_enc_write, fd_enc_write);
perror("open device");
return -EINVAL;
}
rc = write_from_buffer(dev_enc_write, fd_enc_write, data, size, 0);
if (rc < 0)
goto out;
rc = 0;
out:
return rc;
}
// int test_dma_dec_read(unsigned int *DecOut, DecIPConf Confparam)
int test_dma_dec_read(char* DecOut, DecIPConf Confparam)
{
ssize_t rc;
void* virt_addr;
uint64_t size;
uint32_t writeval;
uint32_t Z_val;
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a, max_iter, sc_idx;
uint16_t z_set;
uint32_t ctrl_data;
uint32_t CB_num = Confparam.CB_num;
// this values should be given by Shane
max_schedule = 0;
mb = Confparam.mb;
id = CB_num;
bg = Confparam.BGSel - 1;
z_set = Confparam.z_set - 1;
z_j = Confparam.z_j;
max_iter = 8;
sc_idx = 12;
if (z_set == 0)
z_a = 2;
else if (z_set == 1)
z_a = 3;
else if (z_set == 2)
z_a = 5;
else if (z_set == 3)
z_a = 7;
else if (z_set == 4)
z_a = 9;
else if (z_set == 5)
z_a = 11;
else if (z_set == 6)
z_a = 13;
else
z_a = 15;
if (bg == 0)
kb = 22;
else if (bg == 1)
kb = 10;
else if (bg == 2)
kb = 9;
else if (bg == 3)
kb = 8;
else
kb = 6;
Z_val = (unsigned int)(z_a << z_j);
ctrl_data =
(max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (max_iter << 13) | (sc_idx << 9) | (bg << 6) | (z_set) << 3 | z_j;
uint32_t OutDataNUM = Z_val * kb;
uint32_t Out_dwNumItems_p128;
uint32_t Out_dwNumItems;
if (CB_num & 0x01) // odd cb number
{
if ((OutDataNUM & 0xFF) == 0)
Out_dwNumItems_p128 = OutDataNUM;
else
Out_dwNumItems_p128 = 256 * ((OutDataNUM / 256) + 1);
Out_dwNumItems = (Out_dwNumItems_p128 * CB_num) >> 3;
} else {
if ((OutDataNUM & 0x7F) == 0)
Out_dwNumItems_p128 = OutDataNUM;
else
Out_dwNumItems_p128 = 128 * ((OutDataNUM / 128) + 1);
Out_dwNumItems = (Out_dwNumItems_p128 * CB_num) >> 3;
if ((Out_dwNumItems & 0x1f) != 0)
Out_dwNumItems = ((Out_dwNumItems + 31) >> 5) << 5;
}
size = Out_dwNumItems;
writeval = ctrl_data;
/* calculate the virtual address to be accessed */
virt_addr = map_base + OFFSET_DEC_OUT;
/* swap 32-bit endianess if host is not little-endian */
writeval = htoll(writeval);
*((uint32_t*)virt_addr) = writeval;
if (fd_dec_read < 0) {
fprintf(stderr, "unable to open device %s, %d.\n", dev_dec_read, fd_dec_read);
perror("open device");
return -EINVAL;
}
/* lseek & read data from AXI MM into buffer using SGDMA */
rc = read_to_buffer(dev_dec_read, fd_dec_read, DecOut, size, 0);
if (rc < 0)
goto out;
rc = 0;
out:
return rc;
}
// int test_dma_dec_write(unsigned int *data, DecIPConf Confparam)
int test_dma_dec_write(char* data, DecIPConf Confparam)
{
ssize_t rc;
void* virt_addr;
uint64_t size;
uint32_t writeval;
uint32_t Z_val;
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a, max_iter, sc_idx;
uint16_t z_set;
uint32_t ctrl_data;
uint32_t CB_num = Confparam.CB_num; // CB_PROCESS_NUMBER_Dec;//
// this values should be given by Shane
max_schedule = 0;
mb = Confparam.mb;
id = CB_num;
bg = Confparam.BGSel - 1;
z_set = Confparam.z_set - 1;
z_j = Confparam.z_j;
max_iter = 8;
sc_idx = 12;
if (z_set == 0)
z_a = 2;
else if (z_set == 1)
z_a = 3;
else if (z_set == 2)
z_a = 5;
else if (z_set == 3)
z_a = 7;
else if (z_set == 4)
z_a = 9;
else if (z_set == 5)
z_a = 11;
else if (z_set == 6)
z_a = 13;
else
z_a = 15;
if (bg == 0)
kb = 22;
else if (bg == 1)
kb = 10;
else if (bg == 2)
kb = 9;
else if (bg == 3)
kb = 8;
else
kb = 6;
Z_val = (unsigned int)(z_a << z_j);
ctrl_data =
(max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (max_iter << 13) | (sc_idx << 9) | (bg << 6) | (z_set) << 3 | z_j;
uint32_t InDataNUM = Z_val * mb;
uint32_t In_dwNumItems_p128;
uint32_t In_dwNumItems;
InDataNUM = Z_val * mb * 8;
if ((InDataNUM & 0x7F) == 0)
In_dwNumItems_p128 = InDataNUM;
else
In_dwNumItems_p128 = 128 * ((InDataNUM / 128) + 1);
In_dwNumItems = (In_dwNumItems_p128 * CB_num) >> 3;
if ((In_dwNumItems & 0x1f) != 0)
In_dwNumItems = ((In_dwNumItems + 31) >> 5) << 5;
size = In_dwNumItems;
writeval = ctrl_data;
/* calculate the virtual address to be accessed */
virt_addr = map_base + OFFSET_DEC_IN;
/* swap 32-bit endianess if host is not little-endian */
writeval = htoll(writeval);
*((uint32_t*)virt_addr) = writeval;
if (fd_dec_write < 0) {
fprintf(stderr, "unable to open device %s, %d.\n", dev_dec_write, fd_dec_write);
perror("open device");
return -EINVAL;
}
rc = write_from_buffer(dev_dec_write, fd_dec_write, data, size, 0);
if (rc < 0)
goto out;
rc = 0;
out:
return rc;
}
void test_dma_init(devices_t devices)
{
/* access width */
char* device2 = devices.user_device;
AssertFatal((fd = open(device2, O_RDWR | O_SYNC)) != -1, "CHARACTER DEVICE %s OPEN FAILURE\n", device2);
fflush(stdout);
/* map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
AssertFatal(map_base != (void*)-1, "MEMORY MAP AT ADDRESS %p FAILED\n", map_base);
void* virt_addr;
virt_addr = map_base + OFFSET_RESET;
*((uint32_t*)virt_addr) = 1;
dev_enc_write = devices.enc_write_device;
dev_enc_read = devices.enc_read_device;
dev_dec_write = devices.dec_write_device;
dev_dec_read = devices.dec_read_device;
fd_enc_write = open(dev_enc_write, O_RDWR);
fd_enc_read = open(dev_enc_read, O_RDWR);
fd_dec_write = open(dev_dec_write, O_RDWR);
fd_dec_read = open(dev_dec_read, O_RDWR);
fflush(stdout);
}
void dma_reset(devices_t devices)
{
char* device2 = devices.user_device;
void* virt_addr;
virt_addr = map_base + PCIE_OFF;
*((uint32_t*)virt_addr) = 1;
AssertFatal(munmap(map_base, MAP_SIZE) != -1, "munmap failure");
close(fd_enc_write);
close(fd_enc_read);
close(fd_dec_write);
close(fd_dec_read);
close(fd);
AssertFatal((fd = open(device2, O_RDWR | O_SYNC)) != -1, "CHARACTER DEVICE %s OPEN FAILURE\n", device2);
fflush(stdout);
/* map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
AssertFatal(map_base != (void*)-1, "MEMORY MAP AT ADDRESS %p FAILED\n", map_base);
virt_addr = map_base + PCIE_OFF;
*((uint32_t*)virt_addr) = 1;
virt_addr = map_base + OFFSET_RESET;
*((uint32_t*)virt_addr) = 1;
dev_enc_write = devices.enc_write_device;
dev_enc_read = devices.enc_read_device;
dev_dec_write = devices.dec_write_device;
dev_dec_read = devices.dec_read_device;
fd_enc_write = open(dev_enc_write, O_RDWR);
fd_enc_read = open(dev_enc_read, O_RDWR);
fd_dec_write = open(dev_dec_write, O_RDWR);
fd_dec_read = open(dev_dec_read, O_RDWR);
fflush(stdout);
}
void test_dma_shutdown()
{
void* virt_addr;
virt_addr = map_base + PCIE_OFF;
*((uint32_t*)virt_addr) = 1;
AssertFatal(munmap(map_base, MAP_SIZE) != -1, "munmap failure");
close(fd_enc_write);
close(fd_enc_read);
close(fd_dec_write);
close(fd_dec_read);
close(fd);
}
// reg_rx.c
int nrLDPC_decoder_FPGA_PYM(uint8_t* buf_in, uint8_t* buf_out, DecIFConf dec_conf)
{
struct timespec ts_start0; // evaluate time from input setting to output setting including xdma
int Zc;
int nRows;
int baseGraph;
int CB_num;
DecIPConf Confparam;
int z_a, z_tmp;
int z_j = 0;
int input_CBoffset, output_CBoffset;
uint8_t i_LS;
devices_t devices = {
.user_device = dec_conf.user_device,
.enc_write_device = dec_conf.enc_write_device,
.enc_read_device = dec_conf.enc_read_device,
.dec_write_device = dec_conf.dec_write_device,
.dec_read_device = dec_conf.dec_read_device
};
static int init_flag = 0;
if (init_flag == 0) {
/*Init*/
test_dma_init(devices);
init_flag = 1;
} else {
dma_reset(devices);
}
clock_gettime(CLOCK_MONOTONIC, &ts_start0); // time start0
// LDPC input parameter
Zc = dec_conf.Zc; // shifting size
nRows = dec_conf.nRows; // number of Rows
baseGraph = dec_conf.BG; // base graph
CB_num = dec_conf.numCB; // 31 number of code block
// calc xdma LDPC parameter
// calc i_LS
if ((Zc % 15) == 0)
i_LS = 7;
else if ((Zc % 13) == 0)
i_LS = 6;
else if ((Zc % 11) == 0)
i_LS = 5;
else if ((Zc % 9) == 0)
i_LS = 4;
else if ((Zc % 7) == 0)
i_LS = 3;
else if ((Zc % 5) == 0)
i_LS = 2;
else if ((Zc % 3) == 0)
i_LS = 1;
else
i_LS = 0;
// calc z_a
if (i_LS == 0)
z_a = 2;
else
z_a = i_LS * 2 + 1;
// calc z_j
z_tmp = Zc / z_a;
while (z_tmp % 2 == 0) {
z_j = z_j + 1;
z_tmp = z_tmp / 2;
}
// calc CB_num and mb
Confparam.CB_num = CB_num;
if (baseGraph == 1)
Confparam.mb = 22 + nRows;
else
Confparam.mb = 10 + nRows;
// set BGSel, z_set, z_j
Confparam.BGSel = baseGraph;
Confparam.z_set = i_LS + 1;
Confparam.z_j = z_j;
// Calc input CB offset
input_CBoffset = Zc * Confparam.mb * 8;
if ((input_CBoffset & 0x7F) == 0)
input_CBoffset = input_CBoffset / 8;
else
input_CBoffset = 16 * ((input_CBoffset / 128) + 1);
// Calc output CB offset
output_CBoffset = Zc * (Confparam.mb - nRows);
if ((output_CBoffset & 0x7F) == 0)
output_CBoffset = output_CBoffset / 8;
else
output_CBoffset = 16 * ((output_CBoffset / 128) + 1);
// LDPC accelerator start
// write into accelerator
if (test_dma_dec_write((char *)buf_in, Confparam) != 0) {
exit(1);
printf("write exit!!\n");
}
// read output of accelerator
if (test_dma_dec_read((char *)buf_out, Confparam) != 0) {
exit(1);
printf("read exit!!\n");
}
return 0;
}

View File

@@ -1,46 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*!
* \briefFPGA accelerator integrated into OAI (for one and multi code block)
*/
#ifndef __NRLDPC_CODING_XDMA_OFFLOAD__H_
#define __NRLDPC_CODING_XDMA_OFFLOAD__H_
#include <stdint.h>
#define DEVICE_NAME_DEFAULT_USER "/dev/xdma0_user"
#define DEVICE_NAME_DEFAULT_ENC_READ "/dev/xdma0_c2h_1"
#define DEVICE_NAME_DEFAULT_ENC_WRITE "/dev/xdma0_h2c_1"
#define DEVICE_NAME_DEFAULT_DEC_READ "/dev/xdma0_c2h_0"
#define DEVICE_NAME_DEFAULT_DEC_WRITE "/dev/xdma0_h2c_0"
/**
\brief LDPC input parameter
\param Zc shifting size
\param Rows
\param baseGraph base graph
\param CB_num number of code block
\param numChannelLlrs input soft bits length, Zc x 66 - length of filler bits
\param numFillerBits filler bits length
*/
typedef struct {
char *user_device, *enc_write_device, *enc_read_device, *dec_write_device, *dec_read_device;
unsigned char max_schedule;
unsigned char SetIdx;
int Zc;
unsigned char numCB;
unsigned char BG;
unsigned char max_iter;
int nRows;
int numChannelLls;
int numFillerBits;
} DecIFConf;
int nrLDPC_decoder_FPGA_PYM(uint8_t *buf_in, uint8_t *buf_out, DecIFConf dec_conf);
#endif // __NRLDPC_CODING_XDMA_OFFLOAD__H_

View File

@@ -1,119 +0,0 @@
/*
* Copyright (c) 2016-present, Xilinx, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license
* the terms of the BSD Licence are reported below:
*
* BSD License
*
* For Xilinx DMA IP software
*
* Copyright (c) 2016-present, Xilinx, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name Xilinx nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MODULES_TXCTRL_INC_XDMA_DIAG_H_
#define MODULES_TXCTRL_INC_XDMA_DIAG_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
unsigned char max_schedule; // max_schedule = 0;
unsigned char mb; // mb = 32;
unsigned char CB_num; // id = CB_num;
unsigned char BGSel; // bg = 1;
unsigned char z_set; // z_set = 0;
unsigned char z_j; // z_j = 6;
unsigned char max_iter; // max_iter = 8;
unsigned char SetIdx; // sc_idx = 12;
} DecIPConf;
typedef struct {
int SetIdx;
int NumCBSegm;
int PayloadLen;
int Z;
int z_set;
int z_j;
int Kbmax;
int BGSel;
unsigned mb;
unsigned char CB_num;
unsigned char kb_1;
} EncIPConf;
typedef struct {
char *user_device, *enc_write_device, *enc_read_device, *dec_write_device, *dec_read_device;
} devices_t;
/* ltoh: little to host */
/* htol: little to host */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ltohl(x) (x)
#define ltohs(x) (x)
#define htoll(x) (x)
#define htols(x) (x)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define ltohl(x) __bswap_32(x)
#define ltohs(x) __bswap_16(x)
#define htoll(x) __bswap_32(x)
#define htols(x) __bswap_16(x)
#endif
#define MAP_SIZE (32 * 1024UL)
#define MAP_MASK (MAP_SIZE - 1)
#define SIZE_DEFAULT (32)
#define COUNT_DEFAULT (1)
#define OFFSET_DEC_IN 0x0000
#define OFFSET_DEC_OUT 0x0004
#define OFFSET_ENC_IN 0x0008
#define OFFSET_ENC_OUT 0x000c
#define OFFSET_RESET 0x0020
#define PCIE_OFF 0x0030
#define CB_PROCESS_NUMBER 24 // add by JW
#define CB_PROCESS_NUMBER_Dec 24
// dma_from_device.c
int test_dma_enc_read(char *EncOut, EncIPConf Confparam);
int test_dma_enc_write(char *data, EncIPConf Confparam);
int test_dma_dec_read(char *DecOut, DecIPConf Confparam);
int test_dma_dec_write(char *data, DecIPConf Confparam);
void test_dma_init(devices_t devices);
void test_dma_shutdown();
void dma_reset(devices_t devices);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -424,11 +424,15 @@ void init_nr_transport(PHY_VARS_gNB *gNB)
gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1;
int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1;
gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t));
gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t));
bool ret;
ret = spsc_q_alloc(&gNB->pucch_queue, max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t));
DevAssert(ret);
ret = spsc_q_alloc(&gNB->pusch_queue, gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t));
DevAssert(ret);
int max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot
gNB->srs_queue = spsc_q_alloc(max_nb_srs, sizeof(NR_gNB_SRS_job_t));
ret = spsc_q_alloc(&gNB->srs_queue, max_nb_srs, sizeof(NR_gNB_SRS_job_t));
DevAssert(ret);
gNB->ulsch = (NR_gNB_ULSCH_t *)malloc16(gNB->max_nb_pusch * sizeof(NR_gNB_ULSCH_t));
for (int i = 0; i < gNB->max_nb_pusch; i++) {

View File

@@ -15,8 +15,11 @@
void init_nr_prach(PHY_VARS_gNB *gNB)
{
int num_prach = 16;
gNB->prach_ru_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t));
gNB->prach_l1rx_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t));
bool ret;
ret = spsc_q_alloc(&gNB->prach_ru_queue, num_prach, sizeof(prach_item_t));
DevAssert(ret);
ret = spsc_q_alloc(&gNB->prach_l1rx_queue, num_prach, sizeof(prach_item_t));
DevAssert(ret);
}
void reset_nr_prach(PHY_VARS_gNB *gNB)

View File

@@ -490,6 +490,8 @@ typedef struct RU_t_s {
void (*fh_south_in)(struct RU_t_s *ru, int *frame, int *subframe);
/// function pointer to synchronous TX fronthaul function
void (*fh_south_out)(struct RU_t_s *ru, int frame_tx, int tti_tx, uint64_t timestamp_tx);
/// function pointer to synchronous control fronthaul function
void (*fh_south_ctrl)(struct RU_t_s *ru, int frame, int slot, uint64_t timestamp);
/// function pointer to synchronous RX fronthaul function (RRU)
void (*fh_north_in)(struct RU_t_s *ru, int *frame, int *subframe);
/// function pointer to synchronous RX fronthaul function (RRU)

View File

@@ -151,14 +151,6 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
start_meas(&ru->precoding_stats);
if (gNB->common_vars.analog_bf) {
for (int i = 0; i < ru->num_beams_period; i++) {
memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot],
(void*) &gNB->common_vars.beam_id[i][slot_tx * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
}
if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT)
return;
@@ -197,16 +189,9 @@ void nr_feptx(void *arg)
if (tx_idx == 0)
start_meas(&ru->precoding_stats);
if (ru->gNB_list[0]->common_vars.analog_bf) {
memcpy(&ru->common.beam_id[bb][slot * fp->symbols_per_slot],
&ru->gNB_list[0]->common_vars.beam_id[bb][slot * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
// If there is no digital beamforming we just need to copy the data to RU
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
// FFT shift
const NR_DL_FRAME_PARMS *fp = &ru->gNB_list[0]->frame_parms;
fft_shift(ru->gNB_list[0]->common_vars.txdataF[bb][aa],
fp->ofdm_symbol_size,
fp->N_RB_DL,

View File

@@ -462,11 +462,10 @@ static void configure_prach_occasions(NR_UE_MAC_INST_t *mac, int scs)
for (int s = 0; s < prach_info.N_RA_sfn; s++) { // subframe/60kHz slot occasions in period
while (((temp_s_map >> sf) & 0x01) == 0)
sf++;
int sl = sf;
int sl = scs == 1 || scs == 3 ? sf * 2 : sf;
for (int i = 0; i < prach_slots_in_sf; i++) { // slot per subframe/60kHz slot
int add_slot = i;
if (scs == 1 || scs == 3) {
sl *= 2;
// if only 1 slot per subframe (or 60kHz slot) in case of 30 or 120kHz it's the odd one
// as defined in 5.3.2 of 211
if (((prach_info.format & 0xff) > 3) && prach_slots_in_sf == 1)
@@ -528,6 +527,14 @@ static void configure_prach_occasions(NR_UE_MAC_INST_t *mac, int scs)
LOG_D(NR_MAC, "PRACH configuration period %d association period %d\n", config_period, ra->association_periods);
select_prach_occasion(ra, mac->ssb_list.nb_tx_ssb, max_num_occasions, ra_occasions_period, num_ra_occasions_period);
const prach_occasion_info_t *pi = &ra->sched_ro_info;
LOG_I(NR_MAC,
"[UE %d] selected PRACH occasion: start_symbol %d fdm %d slot %d format %d\n",
mac->ue_id,
pi->start_symbol,
pi->fdm,
pi->slot,
pi->format);
}
/* TS 38.321 subclause 7.3 - return DELTA_PREAMBLE values in dB */

View File

@@ -272,6 +272,12 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc,
DevAssert(rrc != NULL);
DevAssert(ue != NULL);
/* 3GPP TS 38.473, clause 8.3.1.2: The gNB-CU shall only initiate UeContextSetupRequest
* for handover when at least one DRB is setup for the UE. */
if (!seq_arr_size(&ue->drbs)) {
LOG_W(NR_RRC, "UE %u: no DRB configured, cannot trigger handover\n", ue->rrc_ue_id);
return;
}
uint8_t buf[NR_RRC_BUF_SIZE];
int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);

View File

@@ -5,6 +5,7 @@
/*!
* \brief common APIs for different RF frontend device
*/
#include <pthread.h>
#include <stdio.h>
#include <strings.h>
#include <dlfcn.h>
@@ -126,6 +127,8 @@ int openair0_device_load(openair0_device_t *device, openair0_config_t *openair0_
} else
AssertFatal(false, "can't open the radio device: %s\n", get_devname(device->type));
pthread_mutex_init(&device->reOrder.mutex_store, NULL);
pthread_mutex_init(&device->reOrder.mutex_write, NULL);
return rc;
}
@@ -223,15 +226,15 @@ int openair0_write_reorder_common(nrue_ru_write_t nrue_ru_write,
int wroteSamples = 0;
re_order_t *ctx = &device->reOrder;
LOG_D(HW, "received write order ts: %lu, nb samples %d, next ts %luflags %d\n", timestamp, nsamps, timestamp + nsamps, flags);
pthread_mutex_lock(&ctx->mutex_store);
if (!ctx->initDone) {
ctx->nextTS = timestamp;
pthread_mutex_init(&ctx->mutex_write, NULL);
pthread_mutex_init(&ctx->mutex_store, NULL);
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
ctx->queue[i].txp = malloc(sizeof(void *) * NB_ANTENNAS_TX);
}
ctx->initDone = true;
}
pthread_mutex_unlock(&ctx->mutex_store);
if (pthread_mutex_trylock(&ctx->mutex_write) == 0) {
// We have the write exclusivity
if (llabs(timestamp - ctx->nextTS) < MAX_GAP) { // We are writing in sequence of the previous write
@@ -274,13 +277,13 @@ void openair0_write_reorder_clear_context(openair0_device_t *device)
return;
if (pthread_mutex_trylock(&ctx->mutex_write) != 0)
LOG_E(HW, "write_reorder_clear_context call while still writing on the device\n");
pthread_mutex_destroy(&ctx->mutex_write);
else
pthread_mutex_unlock(&ctx->mutex_write);
pthread_mutex_lock(&ctx->mutex_store);
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
ctx->queue[i].active = false;
free(ctx->queue[i].txp);
}
pthread_mutex_unlock(&ctx->mutex_store);
pthread_mutex_destroy(&ctx->mutex_store);
ctx->initDone = false;
pthread_mutex_unlock(&ctx->mutex_store);
}

View File

@@ -80,6 +80,7 @@ typedef struct {
int64_t rx_count;
int wait_for_first_pps;
int use_gps;
int prev_beam;
//int first_tx;
//int first_rx;
//! timestamp of RX packet
@@ -313,6 +314,7 @@ static int trx_usrp_start(openair0_device_t *device)
//s->first_tx = 1;
//s->first_rx = 1;
s->rx_timestamp = 0;
s->prev_beam = -1;
//wait for next pps
uhd::time_spec_t last_pps = s->usrp->get_time_last_pps();
@@ -359,6 +361,43 @@ static void trx_usrp_finish_rx(usrp_state_t *s)
} while (samples > 0);
}
static int trx_set_beam(openair0_device_t *device, int *beams, int num_beams, openair0_timestamp_t timestamp)
{
AssertFatal(beams, "Invalid input for beams %p, vector not present\n", beams);
AssertFatal(num_beams == 1, "Cannot handle more than 1 concurrent beam in USRP\n");
usrp_state_t *s = (usrp_state_t *)device->priv;
// to keep consistent with previous integration where we sent beam information to GPIO
// only if it changed to avoid unnecessary timing issues that the procedure may cause
if (beams[0] == s->prev_beam)
return 0;
else
s->prev_beam = beams[0];
int gpio = 0;
switch (device->openair0_cfg->gpio_controller) {
case RU_GPIO_CONTROL_GENERIC:
AssertFatal(beams[0] < 8, "Only 3 bits available for setting beams\n");
gpio = s->prev_beam | TX_GPIO_CHANGE;
break;
case RU_GPIO_CONTROL_INTERDIGITAL:
// TODO
break;
default:
AssertFatal(false, "illegal GPIO controller for beam handling %d\n", device->openair0_cfg->gpio_controller);
}
radio_tx_gpio_flag_t flags_gpio = (radio_tx_gpio_flag_t) gpio;
// bit 13 enables gpio
timestamp -= device->openair0_cfg->command_line_sample_advance + device->openair0_cfg->tx_sample_advance;
s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 1);
// push GPIO bits
s->usrp->set_command_time(s->tx_md.time_spec);
s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK);
s->usrp->clear_command_time();
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 0);
return 0;
}
static void trx_usrp_write_reset(openair0_thread_t *wt);
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
@@ -418,7 +457,6 @@ static int trx_usrp_write(openair0_device_t *device,
int nsamps2; // aligned to upper 32 or 16 byte boundary
radio_tx_burst_flag_t flags_burst = (radio_tx_burst_flag_t) (flags & 0xf);
radio_tx_gpio_flag_t flags_gpio = (radio_tx_gpio_flag_t) ((flags >> 4) & 0x1fff);
int end;
openair0_thread_t *write_thread = &device->write_thread;
@@ -480,16 +518,6 @@ static int trx_usrp_write(openair0_device_t *device,
s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
s->tx_count++;
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 1);
// bit 13 enables gpio
if ((flags_gpio & TX_GPIO_CHANGE) != 0) {
// push GPIO bits
s->usrp->set_command_time(s->tx_md.time_spec);
s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK);
s->usrp->clear_command_time();
}
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 0);
if (cc > 1) {
std::vector<void *> buff_ptrs;
@@ -524,7 +552,7 @@ static int trx_usrp_write(openair0_device_t *device,
write_package[end].cc = cc;
write_package[end].first_packet = first_packet_state;
write_package[end].last_packet = last_packet_state;
write_package[end].flags_gpio = flags_gpio;
//write_package[end].flags_gpio = flags_gpio;
for (int i = 0; i < cc; i++)
write_package[end].buff[i] = buff[i];
write_thread->count_write++;
@@ -1519,6 +1547,7 @@ extern "C" {
LOG_I(HW,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());
device->trx_write_func = trx_usrp_write;
device->trx_read_func = trx_usrp_read;
device->trx_set_beams2 = trx_set_beam;
s->sample_rate = openair0_cfg[0].sample_rate;
// TODO:

View File

@@ -1363,7 +1363,7 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
int16_t noise_power = (int16_t)(32767.0 / powf(10.0, .05 * -get_noise_power_dBFS()));
for (int a = 0; a < nbAnt; a++) {
for (int i = 0; i < nsamps; i++) {
temp_array[a][i].r += noise_power + gaussZiggurat(0.0, 1.0);
temp_array[a][i].r += noise_power * gaussZiggurat(0.0, 1.0);
temp_array[a][i].i += noise_power * gaussZiggurat(0.0, 1.0);
}
}

View File

@@ -179,12 +179,13 @@ void cirdb_connect(int id,
float want_speed = (sel && sel->want_speed_mps > 0) ? sel->want_speed_mps : -1.0f;
LOG_I(HW,
"CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n",
"CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s, AoA=%.1fdeg\n",
want_model_id,
G.num_tx,
G.num_rx,
want_ds,
want_speed);
want_speed,
sel->want_aoa_deg);
cirdb_entry_meta_t m = (cirdb_entry_meta_t){0};
cirdb_select_req_t req = {.want_model_id = want_model_id,
@@ -192,6 +193,7 @@ void cirdb_connect(int id,
.want_rx = G.num_rx,
.want_ds_ns = want_ds,
.want_speed_mps = want_speed,
.want_aoa_deg = sel->want_aoa_deg,
.allow_shape_swap = 0,
.w_ds = 1.0f,
.w_speed = 0.2f,
@@ -279,7 +281,7 @@ void cirdb_connect(int id,
}
LOG_I(HW,
"CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s\n",
"CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s aoa=%.1fdeg\n",
G.model_id,
G.ds_ns,
G.n_tx,
@@ -289,7 +291,8 @@ void cirdb_connect(int id,
G.S,
G.fs_hz,
G.snapshot_dt_s,
G.speed_mps);
G.speed_mps,
sel->want_aoa_deg);
}
void cirdb_update(uint64_t ns_since_start)

View File

@@ -25,6 +25,7 @@ typedef struct {
int want_model_id; /* 0..4, or <0 for no preference */
float want_ds_ns; /* nonnegative, or <0 for no preference */
float want_speed_mps; /* nonnegative, or <0 for no preference */
float want_aoa_deg; /* degrees; TDL-D/E only; 0.0 = broadside default */
} cirdb_select_opts_t;
/* Initialize provider and publish snapshot 0 through channel_desc_out. */

View File

@@ -96,6 +96,11 @@ int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out)
float ds_ns = e["ds_ns"].as<float>(0.0f);
float sp = e["speed_mps"].as<float>(0.0f);
float aoa = e["los_aoa_deg"].as<float>(0.0f);
// STRICT: AoA exact match when entry has los_aoa_deg field
if (e["los_aoa_deg"].IsDefined() && dabs(aoa - req->want_aoa_deg) > 0.01)
continue;
// FLEXIBLE: delay spread and speed - find closest match using cost function
double cds = 0.0;

View File

@@ -15,6 +15,7 @@ typedef struct {
int want_rx; // exact match
float want_ds_ns; // nearest
float want_speed_mps; // nearest
float want_aoa_deg; // exact match; 0.0 = broadside default
int allow_shape_swap; // allow TX/RX swap when matching
float w_ds; // DS distance weight
float w_speed; // speed distance weight

View File

@@ -76,6 +76,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
{"cirdb_model_id", "Preferred TDL model id 0..4", 0, .iptr = &vrtsim_state->cirdb_model_id, .defintval = 0, TYPE_INT, 0}, \
{"cirdb_ds_ns", "Desired RMS delay spread in ns", 0, .dblptr = &vrtsim_state->cirdb_ds_ns, .defdblval = 10.0, TYPE_DOUBLE, 0}, \
{"cirdb_speed_mps", "Desired speed in m/s", 0, .dblptr = &vrtsim_state->cirdb_speed_mps, .defdblval = 1.5, TYPE_DOUBLE, 0}, \
{"cirdb_aoa_deg", "Desired AoA in degrees (TDL-D/E only)", 0, .dblptr = &vrtsim_state->cirdb_aoa_deg, .defdblval = 0.0, TYPE_DOUBLE, 0}, \
{"num_ues", "Number of UE slots (server only)\n", 0, .iptr = &vrtsim_state->num_ues, .defintval = 1, TYPE_INT, 0}, \
{"ue_id", "UE slot index 0..num_ues-1 (client only)\n", 0, .iptr = &vrtsim_state->ue_id, .defintval = 0, TYPE_INT, 0}, \
{"thread-pool", TPOOL_HLP, .strptr = &vrtsim_state->thread_pool_cores, .defstrval = "-1,-1,-1,-1", TYPE_STRING, 0} \
@@ -106,6 +107,7 @@ typedef struct {
int model_id;
double ds_ns;
double speed_mps;
double aoa_deg;
} cirdb_conf_t;
typedef struct {
@@ -148,6 +150,7 @@ typedef struct {
int cirdb_model_id;
double cirdb_ds_ns;
double cirdb_speed_mps;
double cirdb_aoa_deg;
// Multi-UE support
int num_ues;
int ue_id;
@@ -323,13 +326,16 @@ static void parse_ue_config(vrtsim_state_t *vrtsim_state)
int model_id = vrtsim_state->cirdb_model_id;
double ds_ns = vrtsim_state->cirdb_ds_ns;
double speed_mps = vrtsim_state->cirdb_speed_mps;
double aoa_deg = vrtsim_state->cirdb_aoa_deg;
paramdef_t ue_params[] = {
{"antennas", "Antenna config e.g. \"1x2\"", 0, .strptr = &antennas, .defstrval = NULL, TYPE_STRING, 0},
{"model_id", "TDL model id 0..4", 0, .iptr = &model_id, .defintval = vrtsim_state->cirdb_model_id, TYPE_INT, 0},
{"ds_ns", "Delay spread in ns", 0, .dblptr = &ds_ns, .defdblval = vrtsim_state->cirdb_ds_ns, TYPE_DOUBLE, 0},
{"speed_mps", "Speed in m/s", 0, .dblptr = &speed_mps, .defdblval = vrtsim_state->cirdb_speed_mps, TYPE_DOUBLE, 0},
{"aoa_deg", "LOS AoA in degrees (TDL-D/E only)", 0, .dblptr = &aoa_deg, .defdblval = vrtsim_state->cirdb_aoa_deg, TYPE_DOUBLE, 0},
};
config_get(config_get_if(), ue_params, sizeofArray(ue_params), prefix);
if (antennas != NULL) {
@@ -357,12 +363,13 @@ static void parse_ue_config(vrtsim_state_t *vrtsim_state)
vrtsim_state->ue_conf[i].cir_conf.model_id = model_id;
vrtsim_state->ue_conf[i].cir_conf.ds_ns = ds_ns;
vrtsim_state->ue_conf[i].cir_conf.speed_mps = speed_mps;
vrtsim_state->ue_conf[i].cir_conf.aoa_deg = aoa_deg;
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s, AoA %.1fdeg\n",
i,
vrtsim_state->ue_conf[i].tx_ant,
vrtsim_state->ue_conf[i].rx_ant,
model_id, 'A' + model_id, ds_ns, speed_mps);
model_id, 'A' + model_id, ds_ns, speed_mps, aoa_deg);
}
}
@@ -450,14 +457,17 @@ static int vrtsim_connect(openair0_device_t *device)
vrtsim_state->cirdb_model_id = vrtsim_state->ue.cir_conf.model_id;
vrtsim_state->cirdb_ds_ns = vrtsim_state->ue.cir_conf.ds_ns;
vrtsim_state->cirdb_speed_mps = vrtsim_state->ue.cir_conf.speed_mps;
vrtsim_state->cirdb_aoa_deg = vrtsim_state->ue.cir_conf.aoa_deg;
LOG_I(HW,
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s, AoA %.1fdeg\n",
vrtsim_state->ue_id,
vrtsim_state->cirdb_model_id,
'A' + vrtsim_state->cirdb_model_id,
vrtsim_state->cirdb_ds_ns,
vrtsim_state->cirdb_speed_mps);
vrtsim_state->cirdb_speed_mps,
vrtsim_state->cirdb_aoa_deg);
}
vrtsim_state->channel = shm_td_iq_channel_connect(DEFAULT_CHANNEL_NAME, 10);
vrtsim_state->peer_info.num_rx_antennas = client_info.gnb_num_rx_ant;
@@ -507,6 +517,7 @@ static int vrtsim_connect(openair0_device_t *device)
sel.want_model_id = vrtsim_state->cirdb_model_id;
sel.want_ds_ns = (float)(vrtsim_state->cirdb_ds_ns > 0.0 ? vrtsim_state->cirdb_ds_ns : -1.0);
sel.want_speed_mps = (float)(vrtsim_state->cirdb_speed_mps > 0.0 ? vrtsim_state->cirdb_speed_mps : -1.0);
sel.want_aoa_deg = (float)vrtsim_state->cirdb_aoa_deg;
LOG_A(HW,
"VRTSIM: CIR DB select yaml='%s' bin='%s'\n",
@@ -520,6 +531,7 @@ static int vrtsim_connect(openair0_device_t *device)
ue_sel.want_model_id = vrtsim_state->ue_conf[u].cir_conf.model_id;
ue_sel.want_ds_ns = vrtsim_state->ue_conf[u].cir_conf.ds_ns;
ue_sel.want_speed_mps = vrtsim_state->ue_conf[u].cir_conf.speed_mps;
ue_sel.want_aoa_deg = (float)vrtsim_state->ue_conf[u].cir_conf.aoa_deg;
AssertFatal(ue_sel.want_model_id >= 0 && ue_sel.want_model_id <= 4,
"Invalid CIRDB model_id=%d for UE %d\n",

View File

@@ -26,7 +26,7 @@ gNBs =
maxMIMO_layers = 2;
do_CSIRS = 1;
do_SRS = 0;
min_rxtxtime = 4;
min_rxtxtime = 3;
servingCellConfigCommon = (
{
@@ -37,20 +37,20 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 28701.12 MHz
# selected SSB frequency = 28691.04 MHz
absoluteFrequencySSB = 2090683;
# selected SSB frequency = 28656.48 MHz
absoluteFrequencySSB = 2090107;
dl_frequencyBand = 257;
# frequency point A = 28653.6 MHz
dl_absoluteFrequencyPointA = 2090059;
# frequency point A = 28606.08 MHz
dl_absoluteFrequencyPointA = 2089267;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 3;
dl_carrierBandwidth = 66;
dl_carrierBandwidth = 132;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 17875;
initialDLBWPlocationAndBandwidth = 36025;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 3;
@@ -66,11 +66,11 @@ gNBs =
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 3;
ul_carrierBandwidth = 66;
ul_carrierBandwidth = 132;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 17875;
initialULBWPlocationAndBandwidth = 36025;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 3;
@@ -90,7 +90,7 @@ gNBs =
powerRampingStep = 2;
#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 = 3;
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
@@ -119,11 +119,11 @@ gNBs =
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
ssb_PositionsInBurst_Bitmap = 4;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 1;
ssb_periodicityServingCell = 4;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
@@ -141,11 +141,11 @@ gNBs =
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 3;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
dl_UL_TransmissionPeriodicity = 1;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 10;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 2;
ssPBCH_BlockPower = 0;
}
@@ -166,8 +166,8 @@ gNBs =
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.51";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.18.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.18.51";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
@@ -178,7 +178,7 @@ MACRLCs = (
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 250;
pusch_FailureThres = 1000;
#ul_bler_target_upper =.02;
#ul_bler_target_lower =.01;
@@ -195,12 +195,12 @@ L1s = (
{
tr_n_preference = "local_mac";
prach_dtx_threshold = 250;
pucch0_dtx_threshold = 100;
pucch0_dtx_threshold = 150;
pusch_dtx_threshold = -200;
max_ldpc_iterations = 10;
tx_amp_backoff_dB = 18;
L1_rx_thread_core = 10;
L1_tx_thread_core = 11;
L1_rx_thread_core = 37;
L1_tx_thread_core = 36;
phase_compensation = 1;
}
@@ -219,8 +219,8 @@ RUs = (
max_rxgain = 60;
#sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 9;
sl_ahead = 10;
ru_thread_core = 35;
sl_ahead = 5;
tr_preference = "raw_if4p5";
do_precoding = 0;
}
@@ -257,10 +257,10 @@ log_config :
};
fhi_72 = {
dpdk_devices = ("0000:c1:11.0", "0000:c1:11.1"); # one VF can be used as well
system_core = 6;
io_core = 7;
worker_cores = (8);
dpdk_devices = ("01:01.0","01:01.1");
system_core = 32;
io_core = 33;
worker_cores = (34);
ru_addr = ("E8:C7:4F:1E:C7:5B", "E8:C7:4F:1E:C7:5B");
mtu = 9216;
fh_config = ({