Compare commits

..

59 Commits

Author SHA1 Message Date
Filippo Olimpieri
7d3ef0140b Modified nr_radio_config.c to always include SLIV(0,12) UL TDA when E3_AGENT is enabled 2026-05-01 20:13:18 +00:00
Filippo Olimpieri
ecc6c9ec3e modified CMakeLists of E3 to compile asn files the same way as in the other parts of the repository 2026-04-30 18:22:56 -04:00
Filippo Olimpieri
ce211dc2ef removed duplicated definition and modified skipping of TDS symbol to match the new code structure 2026-04-30 17:19:13 -04:00
Filippo Olimpieri
c3971d524f E2SM DAPP revision
* updated ran functiond for dapp to remove action def and renamed formats

* updated e3_agent code and ran_func_dapp to expose dapp ids and e3 ran func to which each dapp is subscribed

* implemented triggering of RIC service update at every status change of dApp

* modified ran function dapp to handle action definition to specify the kind of report to send in return

* removed report style 1 that was used to geth both types of indications

* updated ran func dapp to populate the new ran func struct following the new structure definition

* fixed missing branchets in if, restored some comments and exception messages, and added macros for report styles

* clang-formatted files in ran func def
2026-04-14 13:31:44 -04:00
Andrea Lacava
5f3ea814e5 Implementation of LIBE3 inside OAI
* implemented spectrum SM

* fix t tracer connection

* Enhance E3 Agent integration and logging in Spectrum Service Model

* Refactor E3 service model: update data types and improve memory handling

* Update documentation
2026-03-05 15:02:21 -05:00
Andrea Lacava
c2fdea2f01 Add documentation
* Add documentation for Spectrum Service Model architecture and functionalities

* Add xApp-dApp bridge

* updated documentation

* Update documentation for xApp-dApp interaction and files

---------

Co-authored-by: Andrea Lacava <a.lacava@northeastern.edu>

---------

Co-authored-by: Filippo Olimpieri <126257005+Ninjabippo1205@users.noreply.github.com>
2026-02-19 12:44:18 -05:00
Filippo Olimpieri
df5ddd2ea4 solve ASN E3AP PDU duplication 2026-01-26 16:11:01 -05:00
Filippo Olimpieri
e5c922feba E2SM-DAPP for xApp-dApp synchronization
* updated flexric submodule

* added support in E3AP for report messages and xAppControlMessages

* improved consistecy

* added conditional compilation guard to ensure generate_e2_indication_from_e3_dapp_report is called only when E2_AGENT is defined

* updated ran func dapp to handle the new version of the e3 agent, and modified also e3 agent to support xAppControlAction

* addressed comments of PR by removing code in CMakeLists and renamed shared queue, marked as untested the code not tested

* removed unused code and  implemented read_dapp_setup_sm

* updated flexric

* removed unused code

* removed unused parameters and updated code to work with new implementation of flexric dapp sm

* updated dapp_e3_obj with e3sm_obj

* updated flexric

* build ran functions of e2sm_dapp ony if E3_AGENT is defined

* fixed decoding of dapp control to enable read of all 273 prbs, currently it supports max 137

* update asn and encoding/decoding to consider prbblacklist and whitelist as sequence of integers instead of octet string

* fix issue that cap the number of xapps to which the indication message could be sent

* added include guard for void* e3_agent_dapp_task(void* args_p);

* modified PRB_Index in e3sm_spectrum.asn

* updated flexric

* resolved confict with develop-dapp branch

* resolved confict with develop-dapp branch

* updated flexric to new branch

* restored initial .gitmodule and flexric submodule

* updated .gitmodules to add missing newline

* added compilation guards to avoid to use of  e2sm-dapp when e3 agent is not defined during copilation

* update DAPP service model description for clarity in synchronization context

---------

Co-authored-by: Andrea Lacava <a.lacava@northeastern.edu>
2026-01-23 16:49:04 -05:00
Andrea Lacava
65df4e2afd enable restart of the main thread for SM 2025-12-17 15:18:44 -05:00
Andrea Lacava
af8d1daf36 Increase indication for the foxconn
* Enhance spectrum encoding with improved error logging and validation checks

* Increase encode buffer size to 32KB for improved IQ data handling
2025-12-09 10:00:07 -05:00
Andrea Lacava
e4ea2170b0 increase buffer size for foxconn 2025-12-05 18:26:29 -05:00
Andrea Lacava
4bb6c48a2f Enhance subscription management by tracking affected RAN functions during dApp unregistration and managing SM lifecycle accordingly 2025-12-05 14:38:59 -05:00
Andrea Lacava
4e6caca5a7 Include dApp report and xApp control action to ensure compatibility and fix ordering 2025-12-04 14:56:38 -05:00
Andrea Lacava
aa01359088 Management of subscribe request to allow clean disconnection
* Refactor subscription management: streamline add/remove logic and enhance error handling

* Enhance dApp unregistration: collect affected RAN function IDs and stop SMs if necessary

* Allow connection from different hosts

* Add management of already existing subscription in E3 Agent

* Change log level for existing subscription warning

* Refactor subscription management: now the RAN functions in the SMs are initialized and run from the beginning. the dApp subscriptions are not going to affect their behaviors, only the E3AP delivery

---------

Co-authored-by: absentio <20925479+absentio@users.noreply.github.com>
2025-12-01 15:48:59 -05:00
Andrea Lacava
d92a51d468 Introducing structured E3SM for different use cases support (#39)
Ex cathedra: squash-merge of E3AP integration and Spectrum SM refactor

This PR consolidates multiple commits that implement, refactor, and stabilize the E3AP integration into the OAI Layer 2 stack with support for Spectrum Service Models and full subscription management.
Thanks to this, now the codebase features a fully working E3AP/E3SM-based control loop in OAI, with subscription and indication message flow, Spectrum SM integration, dynamic sensing thresholds, and compile-time encoding format selection — establishing a solid foundation for scalable multi-RU support.
	•	E3AP Integration & Infrastructure
• Moved E3AP handling into the OAI Layer 2 to support multiple RUs.
• Defined generic data types and introduced compile-time encoding format selection for E3AP messages.
• Refactored socket handling with permission settings for IPC endpoints; added e3_response_queue and new thread support for SMs.
• Reorganized configuration handling, removing deprecated structs, obsolete control logic, and T-tracer functions.
	•	Subscription Management
• Added E3 Subscription Manager (e3_subscription_manager.h/.c) to manage dApp registrations and RAN function subscriptions.
• Implemented add/remove/check subscription functions, integrated with e3_agent.c for handling subscription requests and indications.
• Enhanced agent lifecycle (init/destroy) to incorporate subscription manager cleanup.
• Added subscription request/response handling, with logging improvements for traceability.
	•	Control Loop & Spectrum SM Enhancements
• Implemented setup response with RAN function ID list.
• Subscription and indication messages working end-to-end; completed full control loop.
• Enabled dynamic sampling threshold changes; renamed e3_agent_control to e3_sm_spectrum_control.
• Refined spectrum encoding functions to use uint32_t timestamps and updated ASN.1 definitions accordingly.
• Added ranFunctionIdentifier to control actions for consistency.
	•	Cleanup & Utilities
• Reduced verbosity and removed unused code, buffers, and logs.
• Commented unused timestamp functions to remove warnings.
• Updated CMakeLists and generator scripts.
2025-10-09 15:35:03 -04:00
Noemi Giustini
876696494f Add support for 100MHz over the E3AP and add additional configurations for RUs (#30)
* increase protocolData size to support 100MHz

---------

Co-authored-by: Andrea Lacava <lacava.a@northeastern.edu>
2025-07-02 16:53:25 -04:00
Andrea Lacava
7c6dd3b563 remove all helpers 2025-04-04 18:56:58 +00:00
Andrea Lacava
3ef37bfb37 formatting and cleanup of unnecessary code 2025-04-04 14:05:52 -04:00
Andrea Lacava
a0a0f1dbcd add RIC capabilities and E2AP on the dApp (#18)
* Optimize PHY actions, remove redundant code.
Ensure correct access to the gNB MAC parameters using locking.

* remove magic numbers

* improve memory management of the control action list

* fix direct memory leaks

* change scripts

* add size check

* Successful near-rt, xApp and dApps coexistence

* improve feedback logic of the PRB blocked

* fix index in NR PRB update policy
2025-03-14 17:30:51 -04:00
Andrea Lacava
7bdf1edf8a add include guards and reduce changes only to the necessary ones 2025-02-21 00:03:12 +00:00
Andrea Lacava
3c45d35972 fix data extraction thanks @RajeevGa 2025-02-11 23:12:53 +00:00
Andrea Lacava
4b14d6609b code formatting 2025-02-03 20:39:34 +00:00
Andrea Lacava
b3c9ef630a add back a space 2025-02-03 20:31:28 +00:00
Andrea Lacava
29d2586b8d remove references to GNB_PHY_INPUT_SIGNAL 2025-02-03 20:29:17 +00:00
Andrea Lacava
51ca8ed10d add UTIL back 2025-01-31 23:40:15 +00:00
Andrea Lacava
c48e3e21e4 Update mirror.yml 2025-01-31 16:50:07 -05:00
Andrea Lacava
a9d8f6edb4 Update mirror.yml 2025-01-31 16:43:40 -05:00
Andrea Lacava
ea95f93427 Update mirror.yml to remove conditional push 2025-01-31 16:40:39 -05:00
Andrea Lacava
7ccc29aed3 fix branch 2025-01-27 15:28:26 -05:00
Andrea Lacava
ac90a535f7 Implementation of ZeroMQ, IPC, SCTP, TCP to serve ASN1 PDU for the E3AP (#12)
* work with x310

* E3AP ASN inside

* fix the logs and remove dead code

* add unix domain socket

* add zmq handlers

* Add parameters for E3

* add e3connector to include all the logic related to the message exchange

* add mutex to the contol actions

* list of integers 84% of overhead

* loop apparently works

* indication working

* Action control working

* zmq tcp working

* start work ipc

* simplyfy uds as ipc and remove sctp zmq from valid configurations

* zmq and ipc almost working

* control working better

* LOG_I on the endpoints

* sampling done before the use of the t-tracer

* apply @gabri94 patch to extract modulation order

* remove topic for the inbound connection

* improve the ASN to reduce the PDU size

* improve sampling logic to support no sampling

* code to support data taking commented

* comment on the save of MAC stats

* create stats files

* fix taskset on dapp

* remove OP reference : (

* the best open source projects have references in their code

* add mirror workflow
2025-01-27 15:23:56 -05:00
Andrea Lacava
c5f3bae132 add local core and fix cmakelists for L2_NR 2024-10-19 21:41:03 +00:00
Andrea Lacava
b6d928d7a1 fix cmakelists.txt files for enabling and disabling the e3ap over builds and give the possibility to select whether to use raw tcp sockets or unix domain sockets 2024-10-18 20:35:47 +00:00
Andrea Lacava
21de22e0a3 sampling 5 symbols 2024-10-17 20:02:35 +00:00
Andrea Lacava
ec5f6e7e57 enabling TCP sockets along with UDS 2024-10-17 19:21:01 +00:00
Andrea Lacava
7999a88ea0 revert path of unix domain socket 2024-10-17 18:20:03 +00:00
Andrea Lacava
3ea1ad3a54 test speed colosseum 2024-10-17 17:52:49 +00:00
Andrea Lacava
2213ea1b31 Change of the configurations after the call (#11)
* add sampling counter and sampling_threshold for IQs, reduced RX gain for 410 conf

* removed sampling add configurations

* enable x410
2024-10-17 13:50:01 -04:00
Andrea Lacava
54e0287330 add x410 conf 2024-10-16 20:26:26 +00:00
Andrea Lacava
aa4481ac08 change TCP sockets to UDS (#10)
* change TCP sockets to UDS

* Save IQ samples to file and work OTA (#9)

* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

* change address to .62

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>

* add unix domain sockets

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-10-09 17:46:08 -04:00
Andrea Lacava
74374975ca Save IQ samples to file and work OTA (#9)
* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

* change address to .62

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-10-07 15:20:16 -04:00
Andrea Lacava
69efebb153 Add PRB masking and save data through dApp (#7)
* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-09-27 16:57:15 -04:00
Andrea Lacava
0f1bd004d3 change configuration for OTA 2024-09-27 18:34:05 +00:00
Andrea Lacava
efdf65d763 indentation 2024-09-27 16:00:47 +00:00
Andrea Lacava
707b031007 fix relative paths 2024-06-13 12:49:10 +00:00
Andrea Lacava
8b59a83ab6 move python code in the dApp repo 2024-06-13 12:21:28 +00:00
RajeevGa
b33261e10c Adding readme file 2024-06-09 15:47:41 -04:00
RajeevGa
2048ba532f adding gNB config file 2024-05-28 15:28:11 -04:00
RajeevGa
ac5212053f qtr working demo 2024-05-28 15:26:26 -04:00
Andrea Lacava
82a60c6d7b t tracer working internally 2024-04-25 20:14:53 +00:00
Andrea Lacava
1043bf4a6c moved initialization of E3 agent before the T tracer 2024-04-22 15:13:47 -04:00
Andrea Lacava
ad3158887c Add --nrUE option to build script 2024-03-25 19:39:40 -04:00
Andrea Lacava
70a7d63888 add stub e3 agent 2024-03-22 15:38:25 -04:00
Andrea Lacava
a455ae1436 Update build_oai.sh and e3_agent.c***
***Add run_oai.sh and gnb.band78.sa.fr1.106PRB.usrpx300.conf***

***Fix T_MESSAGES_PATH in e3_agent.h
2024-03-18 14:31:48 -04:00
Andrea Lacava
f71a0fd372 fix typo 2024-03-14 18:34:18 -04:00
RajeevGa
8aeb08d5e2 Added tracer for getting time domain UL iq samples 2024-03-14 15:38:17 -04:00
Andrea Lacava
3ae1b6fc7c Update E3AP CMakeLists.txt and e3_agent.c 2024-03-07 23:36:48 -05:00
Andrea Lacava
7d95e58ad8 Add E3AP capabilities and enable E3_AGENT in build 2024-03-07 22:33:10 -05:00
Andrea Lacava
79645dd554 fix error on including directories 2024-03-07 16:20:05 -05:00
Andrea Lacava
02a95300cd reproduced error on the develop version 2024-03-05 16:32:39 -05:00
84 changed files with 6492 additions and 949 deletions

2
.gitignore vendored
View File

@@ -34,3 +34,5 @@ nfapi_nr_interface_scf
*.log
*.out
CMakeUserPresets.json
build/*

View File

@@ -377,6 +377,12 @@ endif()
add_boolean_option(ENABLE_IMSCOPE OFF "Enable phy scope based on imgui" OFF)
add_boolean_option(ENABLE_IMSCOPE_RECORD OFF "Enable recording IQ data for imscope" OFF)
#########################
##### E3 AGENT
#########################
set(E3_AGENT "OFF" CACHE STRING "O-RAN nGRG E3 Agent for dApps")
set_property(CACHE E3_AGENT PROPERTY STRINGS "ON" "OFF")
##################################################
# ASN.1 grammar C code generation & dependencies #
##################################################
@@ -729,6 +735,9 @@ set(SCHED_NR_SRC
add_library(SCHED_NR_LIB ${SCHED_NR_SRC})
target_link_libraries(SCHED_NR_LIB PRIVATE asn1_nr_rrc_hdrs UTIL)
target_link_libraries(SCHED_NR_LIB PRIVATE NFAPI_LIB) # some FAPI messages pack "subPDUs"
if(E3_AGENT)
target_compile_definitions(SCHED_NR_LIB PRIVATE E3_AGENT)
endif()
set(SCHED_SRC_RU
${OPENAIR1_DIR}/SCHED/ru_procedures.c
@@ -1339,6 +1348,7 @@ if(E2_AGENT)
target_compile_definitions(L2 PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
endif()
add_library(MAC_UE_NR ${MAC_NR_SRC_UE})
target_link_libraries(MAC_UE_NR PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs PUBLIC nr_ue_power_procedures nr_ue_ra_procedures)
@@ -1369,6 +1379,10 @@ if(OAI_AERIAL)
target_compile_definitions(L2_NR PRIVATE ENABLE_AERIAL)
endif()
if(E3_AGENT)
target_compile_definitions(L2_NR PRIVATE E3_AGENT)
endif()
add_library(L2_LTE_NR
# temporary solution until 4G/5G code completely untangled (as evidenced by deletion of the following file)
${MAC_DIR}/dummy_functions.c
@@ -1889,6 +1903,11 @@ if(E2_AGENT)
target_compile_definitions(nr-softmodem PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
endif()
if (E3_AGENT)
target_link_libraries(nr-softmodem PRIVATE e3ap)
target_compile_definitions(nr-softmodem PRIVATE E3_AGENT)
endif()
# force the generation of ASN.1 so that we don't need to wait during the build
target_link_libraries(nr-softmodem PRIVATE
@@ -1911,6 +1930,10 @@ target_link_libraries(nr-cuup PRIVATE
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
if(E2_AGENT)
target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup)
if(E3_AGENT)
target_link_libraries(nr-cuup PRIVATE e3ap)
target_compile_definitions(nr-cuup PRIVATE E3_AGENT)
endif()
target_compile_definitions(nr-cuup PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
endif()

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:oaicicdteam@openairinterface.org).
(mailto:contact@openairinterface.org).
- Eurecom GitLab does NOT accept public email domains.
3. Provide the OAI team with the **username** of this account to
(mailto:oaicicdteam@openairinterface.org) ; we will give you the developer
(mailto:contact@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,43 +24,8 @@ 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.
## 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
# License #
By contributing to OpenAirInterface, you agree that your contributions will be
licensed under

View File

@@ -89,19 +89,6 @@ 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

@@ -1,94 +0,0 @@
#!/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

@@ -18,9 +18,9 @@ HWs=""
BUILD_DIR=ran_build
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal websrv oai_iqplayer imscope imscope_record"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
TARGET_LIST=""
BUILD_TOOL_OPT="-j8"
BUILD_TOOL_OPT="-j$(nproc)"
function print_help() {
echo_info "
@@ -43,6 +43,8 @@ Options:
Sets build directory (will be <oai-root>/cmake_targets/<build-dir>/build)
--build-e2
Enable the the E2 Agent
--build-e3
Enable the the E3 Agent
-I | --install-external-packages
Installs required packages such as LibXML, asn1.1 compiler, ...
This option will require root password
@@ -143,6 +145,10 @@ function main() {
CMAKE_CMD="$CMAKE_CMD -DE2_AGENT=ON"
shift
;;
--build-e3 )
CMAKE_CMD="$CMAKE_CMD -DE3_AGENT=ON"
shift
;;
-I | --install-external-packages)
INSTALL_EXTERNAL=1
echo_info "Will install external packages"

View File

@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
LOG_T(<component>,<format>,<argument>,...)
)
```
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functionnal module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functional module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
| macro | level letter | level value | level name |
|:---------|:---------------|:---------------|----------------:|

View File

@@ -155,6 +155,7 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""};
COMP_DEF(NFAPI_PNF, log) \
COMP_DEF(ITTI, log) \
COMP_DEF(UTIL, log) \
COMP_DEF(E3AP, log) \
COMP_DEF(MAX_LOG_PREDEF_COMPONENTS, )
#define COMP_ENUM(comp, file_extension) comp,

View File

@@ -159,6 +159,10 @@ ID = GNB_PHY_UL_SRS_TOA_NS
DESC = gNB ToA estimate from SRS (in ns)
GROUP = ALL:PHY:GNB
FORMAT = int,gNB_ID : int,rnti : int,frame : int,subframe : buffer,toa_ns
ID = GNB_PHY_UL_FREQ_SENSING_SYMBOL
DESC = gNodeB output data in the freq domain for sensing symbols
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,gNB_ID : int,frame : int,slot : int,antenna : buffer,rxdata
#MAC logs
ID = ENB_MAC_UE_DL_SDU
@@ -426,6 +430,27 @@ ID = LEGACY_MAC_TRACE
GROUP = ALL:LEGACY_MAC:LEGACY_GROUP_TRACE:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_INFO
DESC = E3AP legacy logs - info level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_INFO:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_ERROR
DESC = E3AP legacy logs - error level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_ERROR:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_WARNING
DESC = E3AP legacy logs - warning level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_WARNING:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_DEBUG
DESC = E3AP legacy logs - debug level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_DEBUG:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_TRACE
DESC = E3AP legacy logs - trace level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_TRACE:LEGACY
FORMAT = string,log
ID = LEGACY_NR_MAC_INFO
DESC = NR_MAC legacy logs - info level
GROUP = ALL:LEGACY_NR_MAC:LEGACY_GROUP_INFO:LEGACY

View File

@@ -12,6 +12,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <math.h>
#include <sys/un.h>
#include <sys/stat.h>
void new_thread(void *(*f)(void *), void *data) {
pthread_t t;
@@ -233,6 +235,117 @@ again:
return s;
}
/****************************************************************************/
/* unix domain socket */
/****************************************************************************/
int try_connect_to_uds(char *path)
{
int s;
struct sockaddr_un a;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
perror("socket");
exit(1);
}
memset(&a, 0, sizeof(struct sockaddr_un));
a.sun_family = AF_UNIX;
strncpy(a.sun_path, path, sizeof(a.sun_path) - 1);
if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1) {
perror("connect");
close(s);
return -1;
}
return s;
}
int connect_to_uds(char *path)
{
int s;
printf("connecting to %s\n", path);
again:
s = try_connect_to_uds(path);
if (s == -1) {
perror("trying again in 1s\n");
sleep(1);
goto again;
}
return s;
}
int create_listen_uds_socket(char *path)
{
struct sockaddr_un a;
int s;
int v;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
perror("socket");
exit(1);
}
v = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int))) {
perror("setsockopt");
exit(1);
}
// Bind the socket to a file path
memset(&a, 0, sizeof(struct sockaddr_un));
a.sun_family = AF_UNIX;
strncpy(a.sun_path, path, sizeof(a.sun_path) - 1);
// Remove any existing socket file
unlink(path);
if (bind(s, (struct sockaddr *)&a, sizeof(a)) == -1) {
perror("Bind failed");
close(s);
exit(1);
}
// Set permissions to 770 (rwxrwx---) to make sure groups can access
if (chmod(path, 0770) == -1) {
perror("Failed to change permissions");
close(s);
exit(1);
}
// Listen for incoming connections
if (listen(s, 5) == -1) {
perror("Listen failed");
close(s);
exit(1);
}
return s;
}
int get_uds_connection(char *path)
{
int s, t;
printf("waiting for connection on %s\n", path);
s = create_listen_uds_socket(path);
t = socket_accept(s);
if (t == -1) {
perror("accept");
exit(1);
}
close(s);
printf("connected\n");
return t;
}
/****************************************************************************/
/* buffer */
/****************************************************************************/

View File

@@ -8,7 +8,7 @@
#include "spsc_q.h"
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz)
spsc_q_t spsc_q_alloc(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,8 +17,7 @@ bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz)
cnt += 1;
spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz};
rb.buf = calloc(cnt, elsiz);
*rbn = rb;
return rb.buf != NULL;
return rb;
}
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;
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz);
spsc_q_t spsc_q_alloc(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,8 +8,7 @@ extern "C" {
}
TEST(spsc_q, basic_test) {
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 2, sizeof(int)));
spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
@@ -27,8 +26,7 @@ TEST(spsc_q, basic_test) {
}
TEST(spsc_q, cont_write) {
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
int a = 1;
for (int i = 0; i < 1111; ++i) {
@@ -59,8 +57,7 @@ bool count(const void *data, void *user)
return true; /* count all */
}
TEST(spsc_q, iterator) {
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -88,8 +85,7 @@ bool drop(const void *data, void *user)
return true; /* drop all */
}
TEST(spsc_q, drop) {
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -108,8 +104,7 @@ TEST(spsc_q, drop) {
TEST(spsc_q, full) {
int n = 3;
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, n, sizeof(int)));
spsc_q_t rb = spsc_q_alloc(n, sizeof(int));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
a++;

View File

@@ -0,0 +1,163 @@
<!-- 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.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
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
- FW v02.00.07
- DL uses jumbo frame, UL uses standard MTU of 1500 bytes

View File

@@ -19,6 +19,10 @@ COPY . .
# build all targets so final targets can be created afterwards
RUN \
# Mount CCache cache direrctory
--mount=type=cache,target=/root/.cache/ccache/ \
# Mount CPM package cache
--mount=type=cache,target=/root/.cache/cpm/ \
/bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
@@ -41,6 +45,8 @@ RUN \
## Build FlexRIC for SM Models
RUN \
# Mount CCache cache direrctory
--mount=type=cache,target=/root/.cache/ccache/ \
mkdir -p openair2/E2AP/flexric/build && \
cd openair2/E2AP/flexric/build && \
cmake -GNinja -DCMAKE_BUILD_TYPE=Release \

View File

@@ -446,6 +446,11 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
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);
}

View File

@@ -19,6 +19,11 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "openair2/E2AP/flexric/src/agent/e2_agent_api.h"
#include "openair2/E2AP/RAN_FUNCTION/init_ran_func.h"
#endif
#ifdef E3_AGENT
#include "openair2/E3AP/e3_agent.h"
#endif
#include "nr-softmodem.h"
#include <common/utils/assertions.h>
#include <openair2/GNB_APP/gnb_app.h>
@@ -527,6 +532,13 @@ int main( int argc, char **argv ) {
softmodem_verify_mode(get_softmodem_params());
//////////////////////////////////
//// Init the E3 Agent
#ifdef E3_AGENT
printf("Init E3 Agent\n");
e3_init();
#endif // E3_AGENT
#if T_TRACER
T_Config_Init();
#endif
@@ -733,6 +745,11 @@ int main( int argc, char **argv ) {
time_manager_finish();
#ifdef E3_AGENT
printf("Destroy E3 Agent\n");
e3_destroy();
#endif // E3_AGENT
free(pckg);
logClean();
printf("Bye.\n");

View File

@@ -48,6 +48,30 @@ 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.
@@ -62,6 +86,8 @@ 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,4 +1,5 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
add_subdirectory(nrLDPC_coding_segment)
add_subdirectory(nrLDPC_coding_xdma)
add_subdirectory(nrLDPC_coding_aal)

View File

@@ -0,0 +1,31 @@
# 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

@@ -0,0 +1,414 @@
/*
* 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

@@ -0,0 +1,783 @@
/*
* 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

@@ -0,0 +1,46 @@
/*
* 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

@@ -0,0 +1,119 @@
/*
* 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,15 +424,11 @@ 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;
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);
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));
int max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot
ret = spsc_q_alloc(&gNB->srs_queue, max_nb_srs, sizeof(NR_gNB_SRS_job_t));
DevAssert(ret);
gNB->srs_queue = spsc_q_alloc(max_nb_srs, sizeof(NR_gNB_SRS_job_t));
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

@@ -215,174 +215,6 @@ void nr_modulation(const uint32_t *in, uint32_t length, uint16_t mod_order, int1
AssertFatal(false, "Invalid or unsupported modulation order %d\n", mod_order);
}
static inline uint8_t get_packed_symbol(const uint8_t *in_bytes, uint32_t length, uint16_t mod_order, uint32_t symbol_idx)
{
const uint32_t bit_offset = symbol_idx * mod_order;
const uint32_t byte_offset = bit_offset >> 3;
const uint32_t bit_shift = bit_offset & 0x7;
const uint32_t num_bytes = (length + 7) >> 3;
uint16_t packed = in_bytes[byte_offset];
if (bit_shift > 8 - mod_order && byte_offset + 1 < num_bytes)
packed |= (uint16_t)in_bytes[byte_offset + 1] << 8;
return (packed >> bit_shift) & ((1U << mod_order) - 1);
}
static inline uint64_t get_packed_bits(const uint8_t *in_bytes, uint32_t length, uint32_t bit_offset, uint8_t width)
{
const uint32_t byte_offset = bit_offset >> 3;
const uint32_t bit_shift = bit_offset & 0x7;
const uint32_t num_bytes = (length + 7) >> 3;
const uint32_t bytes_needed = (bit_shift + width + 7) >> 3;
uint64_t packed = 0;
for (uint32_t i = 0; i < bytes_needed && byte_offset + i < num_bytes; i++)
packed |= (uint64_t)in_bytes[byte_offset + i] << (i << 3);
return (packed >> bit_shift) & ((UINT64_C(1) << width) - 1);
}
bool nr_modulation_layer_mapping(const uint32_t *in,
uint32_t length,
uint16_t mod_order,
uint8_t n_layers,
int layerSz,
c16_t tx_layers[][layerSz])
{
if (n_layers < 1 || n_layers > 4)
return false;
const uint32_t n_symbs = length / mod_order;
if ((n_symbs % n_layers) != 0)
return false;
const uint8_t *in_bytes = (const uint8_t *)in;
switch (mod_order) {
case 2: {
const c16_t *nr_mod_table = nr_qpsk_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
tx_layers[layer][layer_sym] = nr_mod_table[idx];
}
}
return true;
}
case 4: {
const int32_t *nr_mod_table = nr_16qam_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
((int32_t *)tx_layers[layer])[layer_sym] = nr_mod_table[idx];
}
}
return true;
}
case 6: {
const c16_t *nr_mod_table = (const c16_t *)nr_64qam_mod_table;
if (n_layers == 1) {
c16_t *tx0 = tx_layers[0];
uint32_t sym = 0;
for (; sym + 2 <= n_symbs; sym += 2) {
const uint16_t idx = get_packed_bits(in_bytes, length, sym * mod_order, 12);
tx0[sym] = nr_mod_table[idx * 2];
tx0[sym + 1] = nr_mod_table[idx * 2 + 1];
}
if (sym < n_symbs) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym);
tx0[sym] = nr_mod_table[idx * 2];
}
return true;
}
if (n_layers == 2) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += 2, layer_sym++) {
const uint16_t idx = get_packed_bits(in_bytes, length, sym * mod_order, 12);
tx0[layer_sym] = nr_mod_table[idx * 2];
tx1[layer_sym] = nr_mod_table[idx * 2 + 1];
}
return true;
}
if (n_layers == 3) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
c16_t *tx2 = tx_layers[2];
uint32_t sym = 0;
uint32_t layer_sym = 0;
for (; sym + 6 <= n_symbs; sym += 6, layer_sym += 2) {
const uint64_t bits = get_packed_bits(in_bytes, length, sym * mod_order, 36);
const uint16_t idx0 = bits & 0xfff;
const uint16_t idx1 = (bits >> 12) & 0xfff;
const uint16_t idx2 = (bits >> 24) & 0xfff;
tx0[layer_sym] = nr_mod_table[idx0 * 2];
tx1[layer_sym] = nr_mod_table[idx0 * 2 + 1];
tx2[layer_sym] = nr_mod_table[idx1 * 2];
tx0[layer_sym + 1] = nr_mod_table[idx1 * 2 + 1];
tx1[layer_sym + 1] = nr_mod_table[idx2 * 2];
tx2[layer_sym + 1] = nr_mod_table[idx2 * 2 + 1];
}
if (sym < n_symbs) {
for (uint8_t layer = 0; layer < 3; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
tx_layers[layer][layer_sym] = nr_mod_table[idx * 2];
}
}
return true;
}
if (n_layers == 4) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
c16_t *tx2 = tx_layers[2];
c16_t *tx3 = tx_layers[3];
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += 4, layer_sym++) {
const uint32_t bits = get_packed_bits(in_bytes, length, sym * mod_order, 24);
const uint16_t idx0 = bits & 0xfff;
const uint16_t idx1 = (bits >> 12) & 0xfff;
tx0[layer_sym] = nr_mod_table[idx0 * 2];
tx1[layer_sym] = nr_mod_table[idx0 * 2 + 1];
tx2[layer_sym] = nr_mod_table[idx1 * 2];
tx3[layer_sym] = nr_mod_table[idx1 * 2 + 1];
}
return true;
}
return false;
}
case 8: {
const int32_t *nr_mod_table = nr_256qam_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
((int32_t *)tx_layers[layer])[layer_sym] = nr_mod_table[idx];
}
}
return true;
}
default:
return false;
}
}
void nr_layer_mapping(int nbCodes,
int encoded_len,
c16_t mod_symbs[nbCodes][encoded_len],
@@ -425,12 +257,16 @@ void nr_layer_mapping(int nbCodes,
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
for (; i < (n_symbs & ~7); i += 8) {
uint32x4x2_t d = vld2q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
tx0 += 4;
tx1 += 4;
// SIMDe doesn't handle this properly, gcc up to 14.2 neither
uint8_t const perm0[16] = {0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7, 12, 13, 14, 15};
uint8x16_t perm = vld1q_u8(perm0);
uint8x16_t d;
for (; i < (n_symbs & (~3)); i += 4) {
d = vqtbl1q_u8(*(uint8x16_t *)(mod + i), perm);
*(int64_t *)tx0 = vgetq_lane_u64((uint64x2_t)d, 0);
*(int64_t *)tx1 = vgetq_lane_u64((uint64x2_t)d, 1);
tx0 += 2;
tx1 += 2;
}
#endif
for (; i < n_symbs; i += 2) {
@@ -539,17 +375,6 @@ void nr_layer_mapping(int nbCodes,
tx2 += 8;
}
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
for (; i < (n_symbs & ~11); i += 12) {
uint32x4x3_t d = vld3q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
vst1q_u32((uint32_t *)tx2, d.val[2]);
tx0 += 4;
tx1 += 4;
tx2 += 4;
}
#endif
for (; i < n_symbs; i += 3) {
*tx0++ = mod[i];
@@ -623,16 +448,17 @@ void nr_layer_mapping(int nbCodes,
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
for (; i < (n_symbs & ~15); i += 16) {
uint32x4x4_t d = vld4q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
vst1q_u32((uint32_t *)tx2, d.val[2]);
vst1q_u32((uint32_t *)tx3, d.val[3]);
tx0 += 4;
tx1 += 4;
tx2 += 4;
tx3 += 4;
// SIMDe doesn't handle this properly, gcc up to 14.2 neither
for (; i < (n_symbs & ~3); i += 4) {
uint32x4_t d4 = *(uint32x4_t *)(mod + i);
*(uint32_t *)tx0 = vgetq_lane_u32(d4, 0);
tx0++;
*(uint32_t *)tx1 = vgetq_lane_u32(d4, 1);
tx1++;
*(uint32_t *)tx2 = vgetq_lane_u32(d4, 2);
tx2++;
*(uint32_t *)tx3 = vgetq_lane_u32(d4, 3);
tx3++;
}
#endif
for (; i < n_symbs; i += 4) {
@@ -914,47 +740,21 @@ static inline __attribute__((always_inline)) __m256i cmac_prec256(__m256i y, __m
}
#endif
#ifdef __aarch64__
#ifdef NEWOPTIM
static inline __attribute__((always_inline)) int16x4x2_t cmac0_prec4(int16x8_t x, int16x4_t wr, int16x4_t wi) {
const int16x4_t x_lo = vget_low_s16(x);
const int16x4_t x_hi = vget_high_s16(x);
const int16x4_t xr = vuzp1_s16(x_lo, x_hi);
const int16x4_t xi = vuzp2_s16(x_lo, x_hi);
#else
static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x, int16x8_t wr, int16x8_t wi) {
//
int16x8_t xr = vuzp1q_s16(x, x); // even lanes
int16x8_t xi = vuzp2q_s16(x, x); // odd lanes
#endif
#ifdef __ARM_FEATURE_QRDMX
// ARMv8.1-A: Use RDM instructions
#ifdef NEWOPTIM
// real = ar*br - ai*bi (Q15 scaling via high-half doubling muls)
int16x4_t real = vqdmulh_s16(xr, wr); // ≈ round((2*xr*wr)/2^16)
real = vqrdmlsh_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16)
// imag = ar*bi + ai*br
int16x4_t imag = vqdmulh_s16(xr, wi);
imag = vqrdmlah_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16)
#else
// real = ar*br - ai*bi (Q15 scaling via high-half doubling muls)
int16x8_t real = vqdmulhq_s16(xr, wr); // ≈ round((2*xr*wr)/2^16)
real = vqrdmlshq_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16)
//
// imag = ar*bi + ai*br
int16x8_t imag = vqdmulhq_s16(xr, wi);
imag = vqrdmlahq_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16)
#endif
#else
// ARMv8.0-A fallback: Use standard 32-bit multiply
#ifdef NEWOPTIM
int32x4_t real_prod = vmull_s16(xr, wr);
real_prod = vmlsl_s16(real_prod, xi, wi);
int32x4_t imag_prod = vmull_s16(xr, wi);
imag_prod = vmlal_s16(imag_prod, xi, wr);
int16x4_t real = vqrshrn_n_s32(real_prod, 15);
int16x4_t imag = vqrshrn_n_s32(imag_prod, 15);
#else
int32x4_t real_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wr));
int32x4_t real_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wr));
real_lo = vmlsl_s16(real_lo, vget_low_s16(xi), vget_low_s16(wi));
@@ -968,33 +768,15 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x
int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15));
int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15));
#endif
#endif
#ifdef NEWOPTIM
int16x4x2_t produ;
produ.val[0] = real;
produ.val[1] = imag;
return produ;
#else
// Re-interleave [real, imag]
int16x8x2_t produ = vzipq_s16(real, imag);
return produ.val[0];
#endif
return produ.val[0];
}
#ifdef NEWOPTIM
static inline __attribute__((always_inline)) int16x4x2_t cmac_prec4(int16x4x2_t y, int16x8_t x, int16x4_t wr, int16x4_t wi) {
const int16x4x2_t produ = cmac0_prec4(x, wr, wi);
y.val[0] = vadd_s16(y.val[0], produ.val[0]);
y.val[1] = vadd_s16(y.val[1], produ.val[1]);
return y;
}
#else
static inline __attribute__((always_inline)) int16x8_t cmac_prec128(int16x8_t y, int16x8_t x, int16x8_t wr, int16x8_t wi) {
int16x8_t produ = cmac0_prec128(x, wr, wi);
return vaddq_s16(y, produ);
}
#endif
#else // __x86 128-bit
#else
static inline __attribute__((always_inline)) simde__m128i cmac0_prec128(simde__m128i x, simde__m128i w_c, simde__m128i w_s)
{
// Multiplication and shift
@@ -1025,12 +807,6 @@ static inline __attribute__((always_inline)) __m128i cmac_prec128(__m128i y, __m
const Type w_c##Rank = Instruct(c16toI32(c16conj(weights[Rank][ant]))); \
const Type w_s##Rank = Instruct(c16toI32(c16swap(weights[Rank][ant]))); \
const Type *in##Rank = (Type *)(txdataF_res_mapped[Rank] + sc_offset + (out-beginning));
#if defined(NEWOPTIM) && defined(__aarch64__)
#define load_consts_arm(Rank) \
const int16x4_t wr##Rank = vdup_n_s16(weights[Rank][ant].r); \
const int16x4_t wi##Rank = vdup_n_s16(weights[Rank][ant].i); \
const int16x8_t *in##Rank = (const int16x8_t *)(txdataF_res_mapped[Rank] + sc_offset + (out - beginning));
#endif
void nr_layer_precoder_simd(const int n_layers,
const int symSz,
@@ -1167,37 +943,19 @@ void nr_layer_precoder_simd(const int n_layers,
}
#endif
#ifdef __aarch64__
#ifdef NEWOPTIM
load_consts_arm(0);
#else
load_consts(int16x8_t, vdupq_n_s16, 0);
#endif
load_consts(int16x8_t, vdupq_n_s16, 0);
if (n_layers == 1) {
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
const int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
vst2_s16((int16_t *)out, y);
#else
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
// Store the result to txdataF
*(int16x8_t *)out = y;
#endif
}
}
if (n_layers == 2) {
#ifdef NEWOPTIM
load_consts_arm(1);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
#endif
load_consts(int16x8_t, vdupq_n_s16, 1);
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
vst2_s16((int16_t *)out, y);
#else
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
// Accumulate the product
@@ -1205,66 +963,41 @@ void nr_layer_precoder_simd(const int n_layers,
y = cmac_prec128(y, x1, w_c1, w_s1);
// Store the result to txdataF
*(int16x8_t *)out = y;
#endif
}
}
if (n_layers == 3) {
#ifdef NEWOPTIM
load_consts_arm(1);
load_consts_arm(2);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
#endif
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in2++), wr2, wi2);
vst2_s16((int16_t *)out, y);
#else
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
;
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
// Store the result to txdataF
*(int16x8_t *)out = y;
#endif
}
}
if (n_layers == 4) {
#ifdef NEWOPTIM
load_consts_arm(1);
load_consts_arm(2);
load_consts_arm(3);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
load_consts(int16x8_t, vdupq_n_s16, 3);
#endif
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in2++), wr2, wi2);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in3++), wr3, wi3);
vst2_s16((int16_t *)out, y);
#else
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
const int16x8_t x3 = vld1q_s16((const int16_t *)in3++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
;
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
y = cmac_prec128(y, x3, w_c3, w_s3);
// Store the result to txdataF
*(int16x8_t *)out = y;
#endif
*(int16x8_t *)out = y;
}
}
#else

View File

@@ -28,13 +28,6 @@ void nr_modulation(const uint32_t *in,
uint16_t mod_order,
int16_t *out);
bool nr_modulation_layer_mapping(const uint32_t *in,
uint32_t length,
uint16_t mod_order,
uint8_t n_layers,
int layerSz,
c16_t tx_layers[][layerSz]);
/*! \brief Perform NR layer mapping. TS 38.211 V15.4.0 subclause 7.3.1.3
@param[in] mod_symbs, double Pointer to modulated symbols for each codeword
@param[in] n_layers, number of layers

View File

@@ -27,26 +27,11 @@ static void nr_pdcch_scrambling(uint32_t *in, uint32_t size, uint32_t Nid, uint3
out[i] = in[i] ^ seq[i];
}
static inline void mark_prb(uint64_t *prb_mask, int prb_mask_words, int symbol, int prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static inline bool prb_marked(const uint64_t *prb_mask, int prb)
{
return (prb_mask[prb >> 6] >> (prb & 63)) & 0x1;
}
void nr_generate_dci(PHY_VARS_gNB *gNB,
const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
NR_DL_FRAME_PARMS *frame_parms,
int slot,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
int slot)
{
uint64_t local_phase_comp_prb_mask[gNB->common_vars.num_beams_period][frame_parms->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
// fill reg list per symbol
int reg_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE];
nr_fill_reg_list(reg_list, pdcch_pdu_rel15);
@@ -187,7 +172,6 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
dmrs_idx = (reg_list[d][reg_count] + pdcch_pdu_rel15->BWPStart + rb_offset) * 3;
else
dmrs_idx = (reg_list[d][reg_count] + rb_offset) * 3;
const int reg_prb = pdcch_pdu_rel15->BWPStart + reg_list[d][reg_count] + rb_offset;
int k_prime = 0;
@@ -215,8 +199,6 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
k++;
} // m
if (gNB->phase_comp)
mark_prb(&local_phase_comp_prb_mask[beam_nb][0][0], prb_mask_words, l, reg_prb);
} // reg_count
} // symbol_idx
@@ -225,33 +207,4 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
dci_pdu->PayloadSizeBits,
*(unsigned long long *)dci_pdu->Payload);
} // for (int d=0;d<pdcch_pdu_rel15->numDlDci;d++)
if (!gNB->phase_comp)
return;
const int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot;
for (int beam = 0; beam < gNB->common_vars.num_beams_period; beam++) {
c16_t *txdataF = gNB->common_vars.txdataF[beam][0];
for (int symbol = pdcch_pdu_rel15->StartSymbolIndex;
symbol < pdcch_pdu_rel15->StartSymbolIndex + pdcch_pdu_rel15->DurationSymbols;
symbol++) {
uint64_t *local_symbol_mask = &local_phase_comp_prb_mask[beam][symbol][0];
int prb = 0;
while (prb < frame_parms->N_RB_DL) {
while (prb < frame_parms->N_RB_DL && !prb_marked(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < frame_parms->N_RB_DL && prb_marked(local_symbol_mask, prb))
prb++;
if (prb > start_prb) {
c16_t *this_symbol = txdataF + symbol * frame_parms->ofdm_symbol_size + start_prb * NR_NB_SC_PER_RB;
const c16_t *rot = &frame_parms->symbol_rotation[0][symb_offset + symbol];
rotate_cpx_vector(this_symbol, rot, this_symbol, (prb - start_prb) * NR_NB_SC_PER_RB, 15);
uint64_t *global_symbol_mask = phase_comp_prb_mask + (beam * frame_parms->symbols_per_slot + symbol) * prb_mask_words;
for (int rr = start_prb; rr < prb; rr++)
global_symbol_mask[rr >> 6] |= UINT64_C(1) << (rr & 63);
}
}
}
}
}

View File

@@ -12,9 +12,7 @@
void nr_generate_dci(PHY_VARS_gNB *gNB,
const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
NR_DL_FRAME_PARMS *frame_parms,
int slot,
uint64_t *phase_comp_prb_mask,
int prb_mask_words);
int slot);
void nr_fill_dci(PHY_VARS_gNB *gNB,
int frame,

View File

@@ -500,21 +500,12 @@ typedef struct pdschSymbolProc_s {
unsigned int dlPtrsSymPos;
unsigned int n_ptrs;
unsigned int beam_nb;
uint64_t *pdsch_phase_comp_prb_mask;
int prb_mask_words;
unsigned int re_beginning_of_symbol[14];
c16_t *tx_layers[4];
time_stats_t dlsch_resource_mapping_stats;
time_stats_t dlsch_precoding_stats;
} pdschSymbolProc_t;
static inline void mark_prb_range(uint64_t *prb_mask, int prb_mask_words, int symbol, int start_prb, int nb_prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
for (int prb = start_prb; prb < start_prb + nb_prb; prb++)
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static void nr_pdsch_symbol_processing(void *arg)
{
pdschSymbolProc_t *rdata = (pdschSymbolProc_t *)arg;
@@ -534,7 +525,6 @@ static void nr_pdsch_symbol_processing(void *arg)
const int symbol_sz = frame_parms->ofdm_symbol_size;
c16_t **txdataF = gNB->common_vars.txdataF[rdata->beam_nb];
const int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot;
for (int l_symbol = rdata->startSymbol; l_symbol < rdata->startSymbol + rdata->numSymbols; l_symbol++) {
start_meas(&rdata->dlsch_resource_mapping_stats);
@@ -603,12 +593,6 @@ static void nr_pdsch_symbol_processing(void *arg)
block_end - block_start + 1,
txdataF_offset_per_symbol);
if (gNB->phase_comp) {
c16_t *pdsch_sc = &txdataF[ant][txdataF_offset_per_symbol + block_start];
const c16_t *rot = &frame_parms->symbol_rotation[0][symb_offset + l_symbol];
rotate_cpx_vector(pdsch_sc, rot, pdsch_sc,(block_end - block_start + 1)*NR_NB_SC_PER_RB, 15);
mark_prb_range(rdata->pdsch_phase_comp_prb_mask, rdata->prb_mask_words, l_symbol, block_start, block_end - block_start + 1);
}
}
}
stop_meas(&rdata->dlsch_precoding_stats);
@@ -617,12 +601,7 @@ static void nr_pdsch_symbol_processing(void *arg)
completed_task_ans(rdata->ans);
}
static int do_one_dlsch(unsigned char *input_ptr,
PHY_VARS_gNB *gNB,
NR_gNB_DLSCH_t *dlsch,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words)
static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSCH_t *dlsch, int slot)
{
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
@@ -671,67 +650,51 @@ static int do_one_dlsch(unsigned char *input_ptr,
if (IS_SOFTMODEM_DLSIM)
memcpy(dlsch->f, input_ptr, (encoded_length + 7) >> 3);
c16_t mod_symbs[rel15->NrOfCodewords][encoded_length] __attribute__((aligned(64)));
for (int codeWord = 0; codeWord < rel15->NrOfCodewords; codeWord++) {
/// scrambling
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator acces by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, codeWord, rel15->dataScramblingId, rel15->rnti, scrambled_output);
#ifdef DEBUG_DLSCH
printf("PDSCH scrambling:\n");
for (int i = 0; i < encoded_length >> 8; i++) {
for (int j = 0; j < 8; j++)
printf("0x%08x\t", scrambled_output[(i << 3) + j]);
printf("\n");
}
#endif
stop_meas(dlsch_scrambling_stats);
/// Modulation
start_meas(dlsch_modulation_stats);
nr_modulation(scrambled_output, encoded_length, Qm, (int16_t *)mod_symbs[codeWord]);
stop_meas(dlsch_modulation_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_re);
for (int i = 0; i < nb_re; i += 8) {
for (int j = 0; j < 8; j++) {
printf("%d %d\t", mod_symbs[codeWord][i + j].r, mod_symbs[codeWord][i + j].i);
}
printf("\n");
}
#endif
}
start_meas(&gNB->dlsch_pdsch_generation_stats);
/// Resource mapping
// Non interleaved VRB to PRB mapping
AssertFatal(n_dmrs, "n_dmrs can't be 0\n");
// make a large enough tail to process all re with SIMD regardless a garbadge filler
start_meas(&gNB->dlsch_layer_mapping_stats);
int layerSz2 = (layerSz + 63) & ~63;
c16_t tx_layers[rel15->nrOfLayers][layerSz2] __attribute__((aligned(64)));
memset(tx_layers, 0, sizeof(tx_layers));
const bool use_fused_mod_layer =
rel15->NrOfCodewords == 1 && (rel15->nrOfLayers == 3 || rel15->nrOfLayers == 4) && (Qm == 2 || Qm == 4 || Qm == 6 || Qm == 8);
if (use_fused_mod_layer) {
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator access by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
start_meas(dlsch_modulation_stats);
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, 0, rel15->dataScramblingId, rel15->rnti, scrambled_output);
stop_meas(dlsch_scrambling_stats);
const bool fused_ok =
nr_modulation_layer_mapping(scrambled_output, encoded_length, Qm, rel15->nrOfLayers, layerSz2, tx_layers);
AssertFatal(fused_ok,
"Unsupported fused modulation/layer mapping for Qm %d, %d layers, %d codewords\n",
Qm,
rel15->nrOfLayers,
rel15->NrOfCodewords);
stop_meas(dlsch_modulation_stats);
} else {
c16_t mod_symbs[rel15->NrOfCodewords][encoded_length] __attribute__((aligned(64)));
for (int codeWord = 0; codeWord < rel15->NrOfCodewords; codeWord++) {
/// scrambling
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator acces by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, codeWord, rel15->dataScramblingId, rel15->rnti, scrambled_output);
#ifdef DEBUG_DLSCH
printf("PDSCH scrambling:\n");
for (int i = 0; i < encoded_length >> 8; i++) {
for (int j = 0; j < 8; j++)
printf("0x%08x\t", scrambled_output[(i << 3) + j]);
printf("\n");
}
#endif
stop_meas(dlsch_scrambling_stats);
/// Modulation
start_meas(dlsch_modulation_stats);
nr_modulation(scrambled_output, encoded_length, Qm, (int16_t *)mod_symbs[codeWord]);
stop_meas(dlsch_modulation_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_re);
for (int i = 0; i < nb_re; i += 8) {
for (int j = 0; j < 8; j++) {
printf("%d %d\t", mod_symbs[codeWord][i + j].r, mod_symbs[codeWord][i + j].i);
}
printf("\n");
}
#endif
}
start_meas(&gNB->dlsch_layer_mapping_stats);
nr_layer_mapping(rel15->NrOfCodewords, encoded_length, mod_symbs, rel15->nrOfLayers, layerSz2, nb_re, tx_layers);
stop_meas(&gNB->dlsch_layer_mapping_stats);
}
nr_layer_mapping(rel15->NrOfCodewords, encoded_length, mod_symbs, rel15->nrOfLayers, layerSz2, nb_re, tx_layers);
/// Layer Precoding and Antenna port mapping
// tx_layers 1-8 are mapped on antenna ports 1000-1007
@@ -749,11 +712,7 @@ static int do_one_dlsch(unsigned char *input_ptr,
slot,
frame_parms->symbols_per_slot,
bitmap);
/// Resource mapping
// Non interleaved VRB to PRB mapping
AssertFatal(n_dmrs, "n_dmrs can't be 0\n");
// make a large enough tail to process all re with SIMD regardless a garbadge filler
stop_meas(&gNB->dlsch_layer_mapping_stats);
// spawn symbol threads
@@ -793,8 +752,6 @@ static int do_one_dlsch(unsigned char *input_ptr,
rdata->dlPtrsSymPos = dlPtrsSymPos;
rdata->n_ptrs = n_ptrs;
rdata->beam_nb = beam_nb;
rdata->pdsch_phase_comp_prb_mask = pdsch_phase_comp_prb_mask + beam_nb * frame_parms->symbols_per_slot * prb_mask_words;
rdata->prb_mask_words = prb_mask_words;
for (int s = l_symbol; s < l_symbol + rdata->numSymbols; s++) {
rdata->re_beginning_of_symbol[s] = re_beginning_of_symbol;
re_beginning_of_symbol += freq_alloc->num_rbs * NR_NB_SC_PER_RB;
@@ -828,13 +785,7 @@ static int do_one_dlsch(unsigned char *input_ptr,
return ((size_output_tb + 511) >> 9) << 6;
}
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
int n_dlsch,
NR_gNB_DLSCH_t *dlsch_array,
int frame,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words)
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot)
{
time_stats_t *dlsch_encoding_stats = &gNB->dlsch_encoding_stats;
time_stats_t *tinput = &gNB->tinput;
@@ -916,7 +867,7 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB,
unsigned char *output_ptr = output;
for (int i = 0; i < n_dlsch; i++) {
output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot, pdsch_phase_comp_prb_mask, prb_mask_words);
output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot);
}
}

View File

@@ -11,13 +11,7 @@
#include "PHY/defs_gNB.h"
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
int n_dlsch,
NR_gNB_DLSCH_t *dlsch_array,
int frame,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words);
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot);
int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
int n_dlsch,

View File

@@ -15,11 +15,8 @@
void init_nr_prach(PHY_VARS_gNB *gNB)
{
int num_prach = 16;
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);
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));
}
void reset_nr_prach(PHY_VARS_gNB *gNB)

View File

@@ -16,11 +16,11 @@ void nr_codeword_scrambling(uint8_t *in,
const int roundedSz = (size + 31) / 32;
uint32_t *seq = gold_cache((n_RNTI << 15) + (q << 14) + Nid, roundedSz);
unsigned int i_32 = 0;
#ifdef __AVX2__
#if defined(__AVX512F__) && defined(__AVX512VL__)
for (; i_32 < ((roundedSz >> 3) << 3); i_32 += 8) {
__m256i in_256 = _mm256_loadu_si256((void*)&((uint32_t *)in)[i_32]);
__m256i seq_256 = _mm256_loadu_si256((void*)&seq[i_32]);
_mm256_storeu_si256((void*)&out[i_32], _mm256_xor_si256(in_256, seq_256));
__m256i in_256 = _mm256_load_epi32(&((uint32_t *)in)[i_32]);
__m256i seq_256 = _mm256_load_epi32(&seq[i_32]);
_mm256_storeu_epi32(&out[i_32], _mm256_xor_si256(in_256, seq_256));
}
#endif
#if defined(__aarch64__)

View File

@@ -14,7 +14,6 @@
#include "common/utils/LOG/log.h"
#include "PHY/INIT/nr_phy_init.h"
#include "PHY/MODULATION/nr_modulation.h"
#include "PHY/NR_REFSIG/ss_pbch_nr.h"
#include "T.h"
#include "T_messages_creator.h"
#include "executables/nr-softmodem.h"
@@ -26,6 +25,10 @@
#include <stdint.h>
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
#ifdef E3_AGENT
#include <openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h>
#endif // E3_AGENT
//#define DEBUG_RXDATA
//#define SRS_IND_DEBUG
@@ -40,90 +43,6 @@ static void nr_fill_indication(const PHY_VARS_gNB *gNB,
nfapi_nr_crc_t *crc,
nfapi_nr_rx_data_pdu_t *pdu);
static inline bool prb_is_rotated(const uint64_t *prb_mask, int prb)
{
return (prb_mask[prb >> 6] >> (prb & 63)) & 0x1;
}
static inline void mark_prb(uint64_t *prb_mask, int prb_mask_words, int symbol, int prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static inline void mark_prb_range(uint64_t *prb_mask, int prb_mask_words, int symbol, int first_prb, int nb_prb)
{
for (int prb = first_prb; prb < first_prb + nb_prb; prb++)
mark_prb(prb_mask, prb_mask_words, symbol, prb);
}
static inline void apply_nr_rotation_TX_segment(const NR_DL_FRAME_PARMS *fp,
c16_t *txdataF,
const c16_t *symbol_rotation,
int slot,
int symbol,
int first_prb,
int nb_prb)
{
const int symb_offset = (slot % fp->slots_per_subframe) * fp->symbols_per_slot;
const c16_t *rot = &symbol_rotation[symb_offset + symbol];
c16_t *this_symbol = txdataF + symbol * fp->ofdm_symbol_size + first_prb * NR_NB_SC_PER_RB;
rotate_cpx_vector(this_symbol, rot, this_symbol, nb_prb * NR_NB_SC_PER_RB, 15);
}
static void merge_prb_mask(const NR_DL_FRAME_PARMS *fp,
const uint64_t *local_prb_mask,
uint64_t *global_prb_mask,
int prb_mask_words)
{
for (int symbol = 0; symbol < fp->symbols_per_slot; symbol++) {
const uint64_t *local_symbol_mask = local_prb_mask + symbol * prb_mask_words;
uint64_t *global_symbol_mask = global_prb_mask + symbol * prb_mask_words;
int prb = 0;
while (prb < fp->N_RB_DL) {
while (prb < fp->N_RB_DL && !prb_is_rotated(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < fp->N_RB_DL && prb_is_rotated(local_symbol_mask, prb))
prb++;
if (prb > start_prb) {
for (int rr = start_prb; rr < prb; rr++)
global_symbol_mask[rr >> 6] |= UINT64_C(1) << (rr & 63);
}
}
}
}
static void apply_nr_rotation_TX_masked(const NR_DL_FRAME_PARMS *fp,
c16_t **txdataF,
int num_tx_buffers,
const c16_t *symbol_rotation,
int slot,
const uint64_t *local_prb_mask,
uint64_t *global_prb_mask,
int prb_mask_words)
{
for (int aa = 0; aa < num_tx_buffers; aa++) {
if (!txdataF[aa])
continue;
for (int symbol = 0; symbol < fp->symbols_per_slot; symbol++) {
const uint64_t *local_symbol_mask = local_prb_mask + symbol * prb_mask_words;
int prb = 0;
while (prb < fp->N_RB_DL) {
while (prb < fp->N_RB_DL && !prb_is_rotated(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < fp->N_RB_DL && prb_is_rotated(local_symbol_mask, prb))
prb++;
if (prb > start_prb)
apply_nr_rotation_TX_segment(fp, txdataF[aa], symbol_rotation, slot, symbol, start_prb, prb - start_prb);
}
}
}
merge_prb_mask(fp, local_prb_mask, global_prb_mask, prb_mask_words);
}
int beam_index_allocation(bool das,
int fapi_beam_index,
NR_gNB_COMMON *common_vars,
@@ -163,12 +82,7 @@ int beam_index_allocation(bool das,
return idx;
}
void nr_common_signal_procedures(PHY_VARS_gNB *gNB,
int frame,
int slot,
const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu)
{
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
const nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu->ssb_pdu_rel15;
@@ -254,26 +168,6 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB,
frame,
cfg,
fp);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[fp->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
const int ssb_start_prb = fp->ssb_start_subcarrier / NR_NB_SC_PER_RB;
const int ssb_nb_prb = (fp->ssb_start_subcarrier % NR_NB_SC_PER_RB + 240 + NR_NB_SC_PER_RB - 1) / NR_NB_SC_PER_RB;
for (int symbol = ssb_start_symbol; symbol < ssb_start_symbol + NR_N_SYMBOLS_SSB; symbol++)
mark_prb_range(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, ssb_start_prb, ssb_nb_prb);
c16_t *ssb_txdataF[] = {txdataF[beam_nb][0]};
apply_nr_rotation_TX_masked(fp,
ssb_txdataF,
1,
fp->symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask + beam_nb * fp->symbols_per_slot * prb_mask_words,
prb_mask_words);
}
// clearing beam information to be provided to RU for all slots (DL and UL)
@@ -287,11 +181,7 @@ void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot)
}
}
static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB,
int slot,
const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, int slot, const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu)
{
const nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csi_params = &csi_rs_pdu->csi_rs_pdu_rel15;
if (csi_params->csi_type == 2) // ZP-CSI
@@ -325,60 +215,6 @@ static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB,
csi_params->power_control_offset_ss,
csi_params->cdm_type,
gNB->common_vars.txdataF[beam_nb]);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[gNB->frame_parms.symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
for (int rb = csi_params->start_rb; rb < csi_params->start_rb + csi_params->nr_of_rbs; rb++) {
if ((csi_params->freq_density <= 1) && (csi_params->freq_density != (rb % 2)))
continue;
for (int j = 0; j < mapping_parms.size; j++) {
for (int lp = 0; lp <= mapping_parms.lprime; lp++) {
const int symbol = mapping_parms.loverline[j] + lp;
mark_prb(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, rb);
}
}
}
apply_nr_rotation_TX_masked(&gNB->frame_parms,
gNB->common_vars.txdataF[beam_nb],
min(mapping_parms.ports, gNB->gNB_config.carrier_config.num_tx_ant.value),
gNB->frame_parms.symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask + beam_nb * gNB->frame_parms.symbols_per_slot * prb_mask_words,
prb_mask_words);
}
static void nr_generate_prs_gNB(PHY_VARS_gNB *gNB,
int slot,
int slot_prs,
prs_config_t *prs_config,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
{
const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
nr_generate_prs(slot_prs, gNB->common_vars.txdataF[0][0], AMP, prs_config, fp);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[fp->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
for (int symbol = prs_config->SymbolStart; symbol < prs_config->SymbolStart + prs_config->NumPRSSymbols; symbol++)
mark_prb_range(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, prs_config->RBOffset, prs_config->NumRB);
c16_t *prs_txdataF[] = {gNB->common_vars.txdataF[0][0]};
apply_nr_rotation_TX_masked(fp,
prs_txdataF,
1,
fp->symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask,
prb_mask_words);
}
void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
@@ -394,10 +230,6 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
if ((cfg->cell_config.frame_duplex_type.value == TDD) && (nr_slot_select(cfg,frame,slot) == NR_UPLINK_SLOT))
return;
const int prb_mask_words = (fp->N_RB_DL + 63) >> 6;
uint64_t phase_comp_prb_mask[gNB->common_vars.num_beams_period][fp->symbols_per_slot][prb_mask_words];
memset(phase_comp_prb_mask, 0, sizeof(phase_comp_prb_mask));
// clear the transmit data array and beam index for the current slot
for (int i = 0; i < gNB->common_vars.num_beams_period; i++) {
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
@@ -415,41 +247,26 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
{
int slot_prs = (slot - i * prs_config->PRSResourceTimeGap + fp->slots_per_frame) % fp->slots_per_frame;
LOG_D(PHY,"gNB_TX: frame %d, slot %d, slot_prs %d, PRS Resource ID %d\n",frame, slot, slot_prs, rsc_id);
nr_generate_prs_gNB(gNB, slot, slot_prs, prs_config, &phase_comp_prb_mask[0][0][0], prb_mask_words);
nr_generate_prs(slot_prs, gNB->common_vars.txdataF[0][0], AMP, prs_config, fp);
}
}
}
for (int i = 0; i < UL_dci_req->numPdus; ++i)
nr_generate_dci(gNB,
&UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15,
&gNB->frame_parms,
slot,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
nr_generate_dci(gNB, &UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
int num_pdsch = 0;
for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) {
const nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i];
switch (dl_tti_pdu->PDUType) {
case NFAPI_NR_DL_TTI_SSB_PDU_TYPE:
nr_common_signal_procedures(gNB,
frame,
slot,
&dl_tti_pdu->ssb_pdu,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
nr_common_signal_procedures(gNB, frame, slot, &dl_tti_pdu->ssb_pdu);
break;
case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE:
nr_generate_dci(gNB,
&dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15,
&gNB->frame_parms,
slot,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
nr_generate_dci(gNB, &dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
break;
case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE:
nr_generate_csi_rs_gNB(gNB, slot, &dl_tti_pdu->csi_rs_pdu, &phase_comp_prb_mask[0][0][0], prb_mask_words);
nr_generate_csi_rs_gNB(gNB, slot, &dl_tti_pdu->csi_rs_pdu);
break;
case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: {
int tx_data_idx = dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15.pduIndex;
@@ -474,12 +291,23 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
if (num_pdsch > 0) {
LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot);
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot, &phase_comp_prb_mask[0][0][0], prb_mask_words);
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot);
}
//apply the OFDM symbol rotation here
start_meas(&gNB->phase_comp_stats);
for (int i = 0; i < gNB->common_vars.num_beams_period; ++i) {
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
if (gNB->phase_comp) {
apply_nr_rotation_TX(fp,
gNB->common_vars.txdataF[i][aa],
true,
fp->symbol_rotation[0],
slot,
fp->N_RB_DL,
0,
fp->Ncp == NR_EXTENDED ? 12 : 14);
}
T(T_GNB_PHY_DL_OUTPUT_SIGNAL,
T_INT(0),
T_INT(frame),
@@ -1409,6 +1237,32 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N
stop_meas(&gNB->rx_srs_stats);
}
#ifdef E3_AGENT
// Spectrum Listening Symbols
c16_t **rxdataF_sen = gNB->common_vars.rxdataF[0];
if (nr_slot_select(&gNB->gNB_config, frame_rx, slot_rx) == NR_UPLINK_SLOT && slot_rx == 8) {
e3_sm_spectrum_control->sampling_counter++;
if (e3_sm_spectrum_control->sampling_counter > e3_sm_spectrum_control->sampling_threshold) {
const uint16_t n_symbols = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot;
// Extracting 12th symbol
uint64_t symbol_offset = (n_symbols)*gNB->frame_parms.ofdm_symbol_size + (12) * gNB->frame_parms.ofdm_symbol_size;
int32_t *rx_signal = (int32_t *)&rxdataF_sen[0][symbol_offset];
// for(int i = 0; i < gNB->frame_parms.ofdm_symbol_size; i++){
// LOG_D(E3AP, "i = %d, (%d %d) \n",i, ((c16_t*) rx_signal)[i].r, ((c16_t*) rx_signal)[i].i);
// }
T(T_GNB_PHY_UL_FREQ_SENSING_SYMBOL,
T_INT(0),
T_INT(frame_rx),
T_INT(slot_rx),
T_INT(0),
T_BUFFER(rx_signal, (gNB->frame_parms.ofdm_symbol_size) * sizeof(int32_t)));
e3_sm_spectrum_control->sampling_counter = 0;
}
}
#endif // E3_AGENT
stop_meas(&gNB->phy_proc_rx);
if (n_pucch > 0 || num_pusch > 0) {

View File

@@ -19,12 +19,7 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req);
int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO);
void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind);
void nr_common_signal_procedures(PHY_VARS_gNB *gNB,
int frame,
int slot,
const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words);
void nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu);
void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx);
void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa);
void nr_feptx_prec(RU_t *ru,int frame_tx,int tti_tx);

View File

@@ -722,6 +722,7 @@ int main(int argc, char **argv)
AssertFatal((gNB->if_inst = NR_IF_Module_init(0)) != NULL, "Cannot register interface");
gNB->if_inst->NR_PHY_config_req = nr_phy_config_request;
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
NR_ServingCellConfigCommon_t *scc = calloc(1,sizeof(*scc));;
prepare_scc(scc);
@@ -827,7 +828,6 @@ int main(int argc, char **argv)
// nr_mac_config_scc()
gNB_mac->pre_processor_dl = nr_dlsim_preprocessor;
phy_init_nr_gNB(gNB);
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
N_RB_DL = gNB->frame_parms.N_RB_DL;
NR_UE_info_t *UE_info = RC.nrmac[0]->UE_info.connected_ue_list[0];
@@ -1416,21 +1416,16 @@ int main(int argc, char **argv)
printStatIndent3(&gNB->toutput,"DLSCH LDPC output generation time");
printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Matching time");
printStatIndent3(&gNB->dlsch_interleaving_stats, "DLSCH Interleaving time");
printStatIndent2(&gNB->dlsch_modulation_stats,"DLSCH modulation time");
printStatIndent2(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time");
printStatIndent2(&gNB->dlsch_pdsch_generation_stats,"DLSCH PDSCH Generation time");
printStatIndent3(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time");
if (gNB->dlsch_layer_mapping_stats.trials > 0) {
printStatIndent3(&gNB->dlsch_modulation_stats,"DLSCH modulation time");
printStatIndent3(&gNB->dlsch_layer_mapping_stats,"DLSCH Layer Mapping time");
}
else {
printStatIndent3(&gNB->dlsch_modulation_stats,"DLSCH mod/lm time");
}
if (num_pdsch_symbols_per_thread == 0) {
gNB->dlsch_resource_mapping_stats.trials = gNB->dlsch_modulation_stats.trials;
if (gNB->dlsch_precoding_stats.trials > 0) printStatIndent3(&gNB->dlsch_resource_mapping_stats,"DLSCH Resource Mapping time");
gNB->dlsch_precoding_stats.trials = gNB->dlsch_modulation_stats.trials;
if (gNB->dlsch_precoding_stats.trials > 0) printStatIndent3(&gNB->dlsch_precoding_stats,"DLSCH Precoding time");
}
printStatIndent3(&gNB->dlsch_layer_mapping_stats,"DLSCH Layer Mapping time");
gNB->dlsch_resource_mapping_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
printStatIndent3(&gNB->dlsch_resource_mapping_stats,"DLSCH Resource Mapping time");
gNB->dlsch_precoding_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
printStatIndent3(&gNB->dlsch_precoding_stats,"DLSCH Precoding time");
if (gNB->phase_comp)
printStatIndent2(&gNB->phase_comp_stats, "Phase Compensation");
if (use_cuda) {
printStatIndent(&pipeline_stats, "GPU Channel Pipeline");

View File

@@ -395,7 +395,6 @@ int main(int argc, char **argv)
RC.gNB[0] = malloc16_clear(sizeof(*(RC.gNB[0])));
gNB = RC.gNB[0];
gNB->ofdm_offset_divisor = UINT_MAX;
gNB->phase_comp = true;
frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH)
frame_parms->nb_antennas_tx = n_tx;
frame_parms->nb_antennas_rx = n_rx;
@@ -489,9 +488,6 @@ int main(int argc, char **argv)
__attribute__ ((aligned(32))) c16_t rxdataF[UE->frame_parms.nb_antennas_rx][rxdataF_sz];
nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64] = {0};
if (input_fd==NULL) {
const int prb_mask_words = (frame_parms->N_RB_DL + 63) / 64;
uint64_t phase_comp_prb_mask[gNB->common_vars.num_beams_period][frame_parms->symbols_per_slot][prb_mask_words];
memset(phase_comp_prb_mask, 0, sizeof(phase_comp_prb_mask));
for (i=0; i<frame_parms->Lmax; i++) {
if((SSB_positions >> i) & 0x01) {
@@ -509,18 +505,22 @@ int main(int argc, char **argv)
for (aa=0; aa<gNB->frame_parms.nb_antennas_tx; aa++)
memset(gNB->common_vars.txdataF[0][aa], 0, frame_parms->samples_per_slot_wCP * sizeof(int32_t));
nr_common_signal_procedures(gNB,
frame,
slot,
&ssb_pdu[i],
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
nr_common_signal_procedures (gNB,frame,slot, &ssb_pdu[i]);
int samp = get_samples_slot_timestamp(frame_parms, slot);
for (aa = 0; aa < gNB->frame_parms.nb_antennas_tx; aa++) {
c16_t fft_in_buff[frame_parms->ofdm_symbol_size * frame_parms->symbols_per_slot] __attribute__((aligned(64)));
memset(fft_in_buff, 0, sizeof(fft_in_buff));
if (cyclic_prefix_type == 1) {
apply_nr_rotation_TX(frame_parms,
gNB->common_vars.txdataF[0][aa],
true,
frame_parms->symbol_rotation[0],
slot,
frame_parms->N_RB_DL,
0,
12);
fft_shift(gNB->common_vars.txdataF[0][aa],
frame_parms->ofdm_symbol_size,
frame_parms->N_RB_DL,
@@ -536,6 +536,15 @@ int main(int argc, char **argv)
frame_parms->nb_prefix_samples,
CYCLIC_PREFIX);
} else {
apply_nr_rotation_TX(frame_parms,
gNB->common_vars.txdataF[0][aa],
true,
frame_parms->symbol_rotation[0],
slot,
frame_parms->N_RB_DL,
0,
14);
fft_shift(gNB->common_vars.txdataF[0][aa],
frame_parms->ofdm_symbol_size,
frame_parms->N_RB_DL,

View File

@@ -5,6 +5,10 @@ add_subdirectory(E1AP)
if(E2_AGENT)
add_subdirectory(E2AP)
endif()
if(E3_AGENT)
message(STATUS "Add E3 Agent capabilities")
add_subdirectory(E3AP)
endif()
add_subdirectory(F1AP)
add_subdirectory(LAYER2)
add_subdirectory(M2AP)

View File

@@ -81,6 +81,12 @@ set(SM_ENCODING_GTP "PLAIN" CACHE STRING "The GTP SM encoding to use")
set_property(CACHE SM_ENCODING_GTP PROPERTY STRINGS "PLAIN")
message(STATUS "Selected GTP SM_ENCODING: ${SM_ENCODING_GTP}")
# DAPP Service Model
if(E3_AGENT)
set(SM_ENCODING_DAPP "ASN" CACHE STRING "The DAPP SM encoding to use")
set_property(CACHE SM_ENCODING_DAPP PROPERTY STRINGS "ASN")
message(STATUS "Selected DAPP SM_ENCODING: ${SM_ENCODING_DAPP}")
endif()
if(E2AP_VERSION STREQUAL "E2AP_V1")
add_library(e2ap_ran_func_obj OBJECT

View File

@@ -39,6 +39,14 @@ set(UE_ID_COMMON_E2SM_SRCS
../flexric/src/sm/rc_sm/ie/asn/UEID-NG-ENB.c
)
if(E3_AGENT)
set(DAPP_RAN_FUNC_SRC
O-RAN/ran_func_dapp.c
O-RAN/ran_func_dapp_subs.c
../flexric/src/sm/dapp_sm/ie/dapp_data_ie.c
)
endif()
add_library(e2_ran_func_cuup STATIC
${UE_ID_COMMON_E2SM_SRCS}
init_ran_func.c
@@ -58,6 +66,13 @@ add_library(e2_ran_func_cuup STATIC
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj kpm_ric_info_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj ds alg sm_common_enc_asn_obj_rc 3gpp_derived_ie_enc_asn_obj_rc)
target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP)
if(E3_AGENT)
target_sources(e2_ran_func_cuup PRIVATE ${DAPP_RAN_FUNC_SRC})
target_link_libraries(e2_ran_func_cuup PUBLIC e2sm_dapp_ir_obj e3sm_obj)
target_include_directories(e2_ran_func_cuup PUBLIC ${OPENAIR_DIR}/openair2/E3AP)
target_compile_definitions(e2_ran_func_cuup PUBLIC E3_AGENT)
endif()
add_library(e2_ran_func_du_cucp_cuup STATIC
${UE_ID_COMMON_E2SM_SRCS}
@@ -82,6 +97,13 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj kpm_ric_info_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj ds sm_common_enc_asn_obj_rc 3gpp_derived_ie_enc_asn_obj_rc)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
if(E3_AGENT)
target_sources(e2_ran_func_du_cucp_cuup PRIVATE ${DAPP_RAN_FUNC_SRC})
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC e2sm_dapp_ir_obj e3sm_obj)
target_include_directories(e2_ran_func_du_cucp_cuup PUBLIC ${OPENAIR_DIR}/openair2/E3AP)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC E3_AGENT)
endif()
# Current implementation:
# - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP
@@ -90,4 +112,4 @@ target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KP
# - CU-CP : e2_ran_func_cucp
# - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup)
# - CU: e2_ran_func_cucp + e2_ran_func_cuup
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup

View File

@@ -0,0 +1,478 @@
#include "ran_func_dapp.h"
static pthread_once_t once_dapp_mutex = PTHREAD_ONCE_INIT;
static dapp_subs_data_t dapp_subs_data = {0};
/**
* @brief Create a byte_array_t from a C-string.
*
* Allocates a new buffer, copies the content of @p s (without NUL terminator),
* and returns a byte_array_t owning that buffer.
*/
static byte_array_t ba_from_cstr(const char* s)
{
byte_array_t ba = {0};
size_t len = strlen(s);
ba.buf = malloc(len);
assert(ba.buf != NULL && "Memory exhausted");
memcpy(ba.buf, s, len);
ba.len = len;
return ba;
}
/**
* @brief Fill the DAPP RAN function name/description/oid fields.
*
* Initializes the name, OID, and description of the given DAPP function
* definition using constant strings; leaves instance as NULL.
*/
static void fill_dapp_ran_function_name(e2sm_dapp_func_def_t* def)
{
assert(def != NULL);
def->name.name = ba_from_cstr("E2SM-DAPP");
def->name.oid = ba_from_cstr("1.3.6.1.4.1.53148.1.1.255.3");
def->name.description = ba_from_cstr("E2SM-DAPP for xApp-dApp synchronization");
def->name.instance = NULL;
}
/**
* @brief Build the DAPP report style definitions.
*
* Allocates and initializes a ran_func_def_report_dapp_sm_t with three
* report styles:
* - Style 1 (DAPP-E3-DATA-REPORT): E3 data reports only.
* ind_hdr_type=1, ind_msg_type=1.
* - Style 2 (DAPP-E3-SUBSCRIPTION-MAP): E3 subscription map only.
* ind_hdr_type=2, ind_msg_type=2.
*/
static ran_func_def_report_dapp_sm_t* make_dapp_report_def(void)
{
ran_func_def_report_dapp_sm_t* rpt = calloc(1, sizeof(*rpt));
assert(rpt != NULL && "Memory exhausted");
rpt->sz_seq_report_sty = 2;
rpt->seq_report_sty = calloc(rpt->sz_seq_report_sty, sizeof(seq_report_sty_dapp_sm_t));
assert(rpt->seq_report_sty != NULL && "Memory exhausted");
/* Style 1: E3 data reports only */
rpt->seq_report_sty[0].report_type = DAPP_RIC_STYLE_E3_DATA_REPORT;
rpt->seq_report_sty[0].name = ba_from_cstr("DAPP-E3-DATA-REPORT");
rpt->seq_report_sty[0].ind_hdr_type = 1;
rpt->seq_report_sty[0].ind_msg_type = 1;
/* Style 2: E3 subscription map only */
rpt->seq_report_sty[1].report_type = DAPP_RIC_STYLE_E3_SUBSCRIPTION_MAP;
rpt->seq_report_sty[1].name = ba_from_cstr("DAPP-E3-SUBSCRIPTION-MAP");
rpt->seq_report_sty[1].ind_hdr_type = 2;
rpt->seq_report_sty[1].ind_msg_type = 2;
return rpt;
}
/**
* @brief Build the DAPP control style definition.
*
* Allocates and initializes a ran_func_def_ctrl_dapp_sm_t with a single
* control style (style 1) for the DAPP RAN function definition.
*/
static ran_func_def_ctrl_dapp_sm_t* make_dapp_ctrl_def(void)
{
ran_func_def_ctrl_dapp_sm_t* ctrl = calloc(1, sizeof(*ctrl));
assert(ctrl != NULL && "Memory exhausted");
ctrl->sz_seq_ctrl_style = 1;
ctrl->seq_ctrl_style = calloc(ctrl->sz_seq_ctrl_style, sizeof(seq_ctrl_style_dapp_sm_t));
assert(ctrl->seq_ctrl_style != NULL && "Memory exhausted");
seq_ctrl_style_dapp_sm_t* s = &ctrl->seq_ctrl_style[0];
s->style_type = DAPP_RIC_STYLE_E3_DATA_REPORT;
s->name = ba_from_cstr("DAPP-CONTROL-STYLE-1");
s->hdr = 1;
s->msg = 1;
s->out_frmt = 1;
return ctrl;
}
static void init_once_dapp(void)
{
init_dapp_subs_data(&dapp_subs_data);
}
/**
* @brief Build a dapp_e3_subscription_list_t from the current E3 subscription map.
*
* Queries the E3 agent for the current dApp subscription map, filters
* out dApps with zero subscriptions, and returns a heap-allocated
* subscription list. Returns NULL if no valid subscriptions exist.
*
* Caller takes ownership of the returned pointer and must free it
* via free_dapp_e3_subscription_list() + free().
*/
static dapp_e3_subscription_list_t* build_dapp_e3_subs_from_map(void)
{
e3_dapp_subscription_map_t map = e3_get_dapp_subscription_map();
size_t valid_count = 0;
for (size_t i = 0; i < map.num_dapps; i++) {
if (map.dapps[i].num_e3_ran_funcs > 0) {
valid_count++;
}
}
if (valid_count == 0) {
e3_free_dapp_subscription_map(&map);
return NULL;
}
dapp_e3_subscription_list_t* subs = calloc(1, sizeof(dapp_e3_subscription_list_t));
assert(subs != NULL && "Memory exhausted");
subs->sz_dapp_e3_subscriptions = valid_count;
subs->dapp_e3_subscriptions = calloc(valid_count, sizeof(dapp_e3_subscription_item_t));
assert(subs->dapp_e3_subscriptions != NULL && "Memory exhausted");
size_t idx = 0;
for (size_t i = 0; i < map.num_dapps; i++) {
if (map.dapps[i].num_e3_ran_funcs == 0) {
continue;
}
subs->dapp_e3_subscriptions[idx].dapp_id = map.dapps[i].dapp_id;
subs->dapp_e3_subscriptions[idx].sz_subscribed_e3_ran_functions = map.dapps[i].num_e3_ran_funcs;
size_t sz = map.dapps[i].num_e3_ran_funcs * sizeof(uint32_t);
subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions = malloc(sz);
assert(subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions != NULL && "Memory exhausted");
memcpy(subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions, map.dapps[i].e3_ran_func_ids, sz);
idx++;
}
e3_free_dapp_subscription_map(&map);
return subs;
}
/**
* @brief Build the complete DAPP RAN function definition.
*
* Fills the RAN function name, event trigger, report styles, and control
* styles. The current dApp E3 subscription map is attached to:
* - Report style 1 (E3 data reports)
* - Control style 1
*
* Report style 2 (subscription map management) does not carry subscriptions.
*/
static e2sm_dapp_func_def_t fill_dapp_ran_def(void)
{
e2sm_dapp_func_def_t def = (e2sm_dapp_func_def_t){0};
fill_dapp_ran_function_name(&def);
def.ev_trig = NULL;
def.report = make_dapp_report_def();
def.ctrl = make_dapp_ctrl_def();
dapp_e3_subscription_list_t* subs = build_dapp_e3_subs_from_map();
if (subs != NULL) {
assert(def.report != NULL && def.report->sz_seq_report_sty >= 1);
def.report->seq_report_sty[0].dapp_e3_subs = subs;
assert(def.ctrl != NULL && def.ctrl->sz_seq_ctrl_style >= 1);
def.ctrl->seq_ctrl_style[0].dapp_e3_subs = calloc(1, sizeof(dapp_e3_subscription_list_t));
assert(def.ctrl->seq_ctrl_style[0].dapp_e3_subs != NULL && "Memory exhausted");
*def.ctrl->seq_ctrl_style[0].dapp_e3_subs = cp_dapp_e3_subscription_list(subs);
}
return def;
}
/**
* @brief Populate the DAPP RAN function definition during E2 setup.
*
* This is called by the E2 agent when building the DAPP RAN function
* setup structure. It:
* - Fills dapp->ran_func_def with the DAPP RAN function definition.
* - Ensures global DAPP subscription state is initialized once.
*/
void read_dapp_setup_sm(void* data)
{
assert(data != NULL);
dapp_e2_setup_t* dapp = (dapp_e2_setup_t*)data;
dapp->ran_func_def = fill_dapp_ran_def();
const int ret = pthread_once(&once_dapp_mutex, init_once_dapp);
DevAssert(ret == 0);
}
/**
* @brief Send the dApp E3 subscription map to all Format-2 subscribers.
*
* Queries the current dApp E3 subscription map, wraps it in an
* E2SM-DAPP IndicationHeader (format 2) + IndicationMessage (format 2),
* and pushes one indication per registered Format-2 RIC subscription.
*/
static void generate_e2_indication_dapp_e3_subscriptions(void)
{
uint32_t* ric_ids = NULL;
size_t count = ric_subs_frmt2_snapshot(&ric_ids);
if (count == 0 || ric_ids == NULL) {
return;
}
e3_dapp_subscription_map_t map = e3_get_dapp_subscription_map();
size_t valid_count = 0;
for (size_t i = 0; i < map.num_dapps; i++) {
if (map.dapps[i].num_e3_ran_funcs > 0) {
valid_count++;
}
}
const f1ap_setup_req_t* f1_req = RC.nrmac[0]->f1_config.setup_req;
const f1ap_served_cell_info_t* cell = (f1_req && f1_req->num_cells_available > 0) ? &f1_req->cell[0].info : NULL;
for (size_t s = 0; s < count; ++s) {
dapp_ind_data_t* ind = calloc(1, sizeof(*ind));
assert(ind != NULL);
// Header format 2: node identity only
ind->hdr.format = FORMAT_2_E2SM_DAPP_IND_HDR;
e2sm_dapp_ind_hdr_frmt_2_t* hdr = &ind->hdr.frmt_2;
if (f1_req != NULL && cell != NULL) {
hdr->node_type = (uint8_t)RC.nrrrc[0]->node_type;
hdr->node_nb_id = (uint32_t)(cell->nr_cellid >> 14);
hdr->node_plmn_id[0] = (uint8_t)(cell->plmn.mcc >> 8);
hdr->node_plmn_id[1] = (uint8_t)(cell->plmn.mcc & 0xFF);
hdr->node_plmn_id[2] = (uint8_t)(cell->plmn.mnc & 0xFF);
hdr->node_cu_du_id_present = true;
hdr->node_cu_du_id = f1_req->gNB_DU_id;
}
// Message format 2: subscription map
ind->msg.format = FORMAT_2_E2SM_DAPP_IND_MSG;
e2sm_dapp_ind_msg_frmt_2_t* msg = &ind->msg.frmt_2;
msg->dapp_e3_subs.sz_dapp_e3_subscriptions = valid_count;
msg->dapp_e3_subs.dapp_e3_subscriptions = valid_count > 0 ? calloc(valid_count, sizeof(dapp_e3_subscription_item_t)) : NULL;
size_t idx = 0;
for (size_t i = 0; i < map.num_dapps; i++) {
if (map.dapps[i].num_e3_ran_funcs == 0) {
continue;
}
dapp_e3_subscription_item_t* dst = &msg->dapp_e3_subs.dapp_e3_subscriptions[idx];
dst->dapp_id = map.dapps[i].dapp_id;
dst->sz_subscribed_e3_ran_functions = map.dapps[i].num_e3_ran_funcs;
size_t sz = map.dapps[i].num_e3_ran_funcs * sizeof(uint32_t);
dst->subscribed_e3_ran_functions = malloc(sz);
assert(dst->subscribed_e3_ran_functions != NULL);
memcpy(dst->subscribed_e3_ran_functions, map.dapps[i].e3_ran_func_ids, sz);
idx++;
}
/* No E3 payload for format 2 */
ind->e3.type = DAPP_E3_SM_NONE;
async_event_agent_api(ric_ids[s], ind);
}
e3_free_dapp_subscription_map(&map);
free(ric_ids);
}
void notify_dapp_status_changed(void)
{
trigger_ric_service_update_api();
generate_e2_indication_dapp_e3_subscriptions();
}
/**
* @brief Convert an E3 DAPP report into one or more E2 indications.
*
* For each RIC subscription currently registered:
* - Allocates a dapp_ind_data_t,
* - Fills the E2SM-DAPP IndicationHeader (RAN function ID, dApp ID),
* - Wraps the E3 report bytes in the IndicationMessage format 1 payload,
* - Enqueues the indication on the FlexRIC async path.
*
* If @p report_data is NULL or @p report_size == 0, it returns immediately.
*/
void generate_e2_indication_from_e3_dapp_report(uint32_t ran_function_id,
uint32_t dapp_id,
size_t report_size,
const uint8_t* report_data)
{
if (report_data == NULL || report_size == 0) {
return;
}
uint32_t* ric_ids = NULL;
size_t count = ric_subs_frmt1_snapshot(&ric_ids);
if (count == 0 || ric_ids == NULL) {
return;
}
const f1ap_setup_req_t* f1_req = RC.nrmac[0]->f1_config.setup_req;
const f1ap_served_cell_info_t* cell = (f1_req && f1_req->num_cells_available > 0) ? &f1_req->cell[0].info : NULL;
for (size_t i = 0; i < count; ++i) {
uint32_t ric_req_id = ric_ids[i];
dapp_ind_data_t* ind = calloc(1, sizeof(*ind));
assert(ind);
ind->hdr.format = FORMAT_1_E2SM_DAPP_IND_HDR;
e2sm_dapp_ind_hdr_frmt_1_t* hdr = &ind->hdr.frmt_1;
hdr->ran_function_id = ran_function_id;
hdr->dapp_id = dapp_id;
if (f1_req != NULL && cell != NULL) {
hdr->node_type = (uint8_t)RC.nrrrc[0]->node_type;
hdr->node_nb_id = (uint32_t)(cell->nr_cellid >> 14);
hdr->node_plmn_id[0] = (uint8_t)(cell->plmn.mcc >> 8);
hdr->node_plmn_id[1] = (uint8_t)(cell->plmn.mcc & 0xFF);
hdr->node_plmn_id[2] = (uint8_t)(cell->plmn.mnc & 0xFF);
hdr->node_cu_du_id_present = true;
hdr->node_cu_du_id = f1_req->gNB_DU_id;
}
ind->msg.format = FORMAT_1_E2SM_DAPP_IND_MSG;
e2sm_dapp_ind_msg_frmt_1_t* msg = &ind->msg.frmt_1;
msg->data_size = report_size;
msg->data = malloc(report_size);
assert(msg->data != NULL);
memcpy(msg->data, report_data, report_size);
// Push to FlexRIC async path
async_event_agent_api(ric_req_id, ind);
}
free(ric_ids);
}
/**
* @brief Callback invoked when an aperiodic subscription is released.
*
* Removes the given RIC request ID from the internal DAPP subscription
* bookkeeping.
*/
static void free_aperiodic_subscription(uint32_t ric_req_id)
{
remove_dapp_subs_data(&dapp_subs_data, ric_req_id);
}
/**
* @brief Handle DAPP SM subscription requests at the agent.
*
* Called when the RIC installs a subscription for the DAPP SM. For
* - Stores the RIC request ID in DAPP subscription data,
* - Registers the RIC subscription,
* - Returns an aperiodic subscription outcome with a custom free callback.
* - Supported ActionDefinition formats (currently FORMAT_1 only), it:
*/
sm_ag_if_ans_t write_subs_dapp_sm(void const* src)
{
assert(src != NULL);
wr_dapp_sub_data_t* wr_dapp = (wr_dapp_sub_data_t*)src;
sm_ag_if_ans_t ans = {0};
const uint32_t ric_req_id = wr_dapp->ric_req_id;
assert(wr_dapp->dapp.action_def != NULL && "Action definition required");
const uint32_t style = wr_dapp->dapp.action_def->ric_style_type;
switch (style) {
case DAPP_RIC_STYLE_E3_DATA_REPORT:
/* E3 data reports only */
insert_frmt_1_ric_id(&dapp_subs_data, ric_req_id);
ric_subs_frmt1_add(ric_req_id);
break;
case DAPP_RIC_STYLE_E3_SUBSCRIPTION_MAP:
/* E3 subscription map only */
insert_frmt_2_ric_id(&dapp_subs_data, ric_req_id);
ric_subs_frmt2_add(ric_req_id);
break;
default:
AssertError(false, return ans, "[RAN FUNC DAPP SUBS] Unsupported Action Definition format %d\n", style);
}
ans.type = SUBS_OUTCOME_SM_AG_IF_ANS_V0;
ans.subs_out.type = APERIODIC_SUBSCRIPTION_FLRC;
ans.subs_out.aper.free_aper_subs = free_aperiodic_subscription;
return ans;
}
/**
* @brief Handle DAPP SM control requests at the agent.
*
* For format-1 E2SM-DAPP ControlMessages, and when compiled with E3_AGENT:
* - Extracts RAN function ID, dApp ID, and control payload,
* - Forwards it to the E3 agent via libe3 C API wrapper.
*
* Always returns a control outcome of type DAPP_AGENT_IF_CTRL_ANS_V0.
*/
sm_ag_if_ans_t write_ctrl_dapp_sm(void const* data)
{
assert(data != NULL);
dapp_ctrl_req_data_t const* ctrl = (dapp_ctrl_req_data_t const*)data;
sm_ag_if_ans_t ans = {0};
switch (ctrl->msg.format) {
case FORMAT_1_E2SM_DAPP_CTRL_MSG: {
#if defined(E3_AGENT)
uint32_t ran_function_id = ctrl->hdr.frmt_1.ran_function_id;
uint32_t dapp_id = ctrl->hdr.frmt_1.dapp_id;
uint32_t data_size = ctrl->msg.frmt_1.data_size;
const uint8_t* payload = ctrl->msg.frmt_1.data;
if (e3_send_xapp_control(dapp_id, ran_function_id, payload, data_size) != 0) {
printf("[RAN FUNC DAPP CTRL] Failed to forward xApp control via E3 agent\n");
}
#endif
break;
}
default:
AssertError(false, return ans, "[RAN FUNC DAPP CTRL] Unsupported Control Message format %d", ctrl->msg.format);
}
ans.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0;
ans.ctrl_out.type = DAPP_AGENT_IF_CTRL_ANS_V0;
return ans;
}
/**
* @brief Read DAPP SM state from the agent (not implemented).
*
* Placeholder for a future read operation for the DAPP SM. Currently it
* asserts unconditionally and returns true to satisfy the interface.
*/
bool read_dapp_sm(void* data)
{
assert(data != NULL);
assert(0 != 0 && "Not implemented");
return true;
}

View File

@@ -0,0 +1,80 @@
#ifndef RAN_FUNC_SM_DAPP_READ_WRITE_AGENT_H
#define RAN_FUNC_SM_DAPP_READ_WRITE_AGENT_H
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
#include "ran_func_dapp_subs.h"
#include "ran_func_dapp_extern.h"
#include "ran_e2sm_ue_id.h"
#include "../../flexric/src/agent/e2_agent_api.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "common/ran_context.h"
#if defined(E3_AGENT)
#include "e3_agent.h"
#include <endian.h>
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#endif
/**
* @brief Populate the DAPP RAN function definition during E2 setup.
*
* Called by the E2 agent when building the E2 SETUP REQUEST.
* Fills the RAN function definition with:
* - RAN function name, OID, and description
* - Three report styles:
* Style 1 (DAPP-ALL): registers for both E3 data reports and subscription map
* Style 2 (DAPP-E3-DATA-REPORT): E3 data reports only (ind format 1)
* Style 3 (DAPP-E3-SUBSCRIPTION-MAP): subscription map only (ind format 2)
* - One control style (format 1)
* - Current dApp E3 subscription map (if any dApps are connected)
* Also initializes the global DAPP subscription state (once).
*
* @param data Pointer to a dapp_e2_setup_t to be filled.
*/
void read_dapp_setup_sm(void* data);
/**
* @brief Handle DAPP SM subscription requests at the agent.
*
* Routes the subscription into the appropriate indication lists based on
* the report style type in the action definition:
* - Style 1: registers for both Format 1 and Format 2 indications
* - Style 2: registers for Format 1 indications only (E3 data reports)
* - Style 3: registers for Format 2 indications only (subscription map)
*
* Returns an aperiodic subscription outcome.
*
* @param src Pointer to a wr_dapp_sub_data_t containing the subscription request.
* @return Subscription outcome (APERIODIC_SUBSCRIPTION_FLRC).
*/
sm_ag_if_ans_t write_subs_dapp_sm(void const* src);
/**
* @brief Handle DAPP SM control requests at the agent.
*
* For Format 1 control messages (when compiled with E3_AGENT):
* - Extracts RAN function ID, dApp ID, and control payload
* - Forwards the control to the E3 agent via e3_send_xapp_control()
*
* @param data Pointer to a dapp_ctrl_req_data_t containing the control request.
* @return Control outcome of type DAPP_AGENT_IF_CTRL_ANS_V0.
*/
sm_ag_if_ans_t write_ctrl_dapp_sm(void const* data);
/**
* @brief Read DAPP SM state from the agent (not implemented).
*
* Placeholder — asserts unconditionally if called.
*
* @param data Unused.
* @return Never returns (asserts).
*/
bool read_dapp_sm(void*);
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RAN_FUNC_SM_DAPP_EXTERN_AGENT_H
#define RAN_FUNC_SM_DAPP_EXTERN_AGENT_H
#include "openair2/RRC/NR/nr_rrc_defs.h"
#if defined(E3_AGENT)
#include "e3_agent.h"
#include <endian.h>
#include <string.h>
#endif
void generate_e2_indication_from_e3_dapp_report(uint32_t ran_function_id,
uint32_t dapp_id,
size_t report_size,
const uint8_t *report_data);
void notify_dapp_status_changed(void);
#endif

View File

@@ -0,0 +1,241 @@
#include "ran_func_dapp_subs.h"
#include "common/utils/assertions.h"
#include "common/utils/alg/find.h"
#include <assert.h>
#include <pthread.h>
/** @brief Mutex protecting all per-SM dapp_subs_data_t operations. */
static pthread_mutex_t dapp_mutex = PTHREAD_MUTEX_INITIALIZER;
/** @brief Mutex protecting the global Format 1 and Format 2 registries. */
static pthread_mutex_t g_map_mtx = PTHREAD_MUTEX_INITIALIZER;
/** @brief Global list of RIC request IDs subscribed to Format 1 (E3 data reports). */
static seq_arr_t g_all_frmt1_subs;
/** @brief Global list of RIC request IDs subscribed to Format 2 (subscription map). */
static seq_arr_t g_all_frmt2_subs;
/** @brief Guard for one-time initialization of the global lists. */
static bool g_map_inited = false;
/* ── Helpers ── */
/**
* @brief Predicate: compare two uint32_t values for equality.
*
* Used with find_if() to locate a RIC request ID in a seq_arr_t.
*/
static bool eq_uint32(const void *value, const void *it)
{
return *(const uint32_t *)value == *(const uint32_t *)it;
}
/**
* @brief Check whether a seq_arr_t contains a given uint32_t value.
*
* @param s The sequence to search.
* @param v The value to look for.
* @return true if found, false otherwise.
*/
static bool seq_contains(const seq_arr_t *s, uint32_t v)
{
for (void *it = seq_arr_front((seq_arr_t *)s); it != seq_arr_end((seq_arr_t *)s); it = seq_arr_next(s, it))
if (*(uint32_t *)it == v)
return true;
return false;
}
/**
* @brief Append a uint32_t to a seq_arr_t if not already present.
*
* @param s The sequence to modify.
* @param v The value to insert.
*/
static void seq_push_unique(seq_arr_t *s, uint32_t v)
{
if (!seq_contains(s, v))
seq_arr_push_back(s, &v, sizeof(v));
}
/**
* @brief Remove the first occurrence of a uint32_t from a seq_arr_t.
*
* @param s The sequence to modify.
* @param v The value to remove.
* @return true if an element was removed, false if not found.
*/
static bool seq_remove_first(seq_arr_t *s, uint32_t v)
{
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it)) {
if (*(uint32_t *)it == v) {
seq_arr_erase(s, it);
return true;
}
}
return false;
}
/**
* @brief Copy all uint32_t elements from a seq_arr_t into a heap-allocated array.
*
* Must be called while g_map_mtx is held. Sets *out to NULL and returns 0
* if the sequence is empty or allocation fails.
*
* @param s The sequence to snapshot.
* @param[out] out Set to the allocated array of IDs.
* @return Number of elements copied.
*/
static size_t seq_snapshot_locked(seq_arr_t *s, uint32_t **out)
{
size_t n = 0;
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it))
++n;
if (n == 0) {
*out = NULL;
return 0;
}
uint32_t *ids = malloc(n * sizeof(*ids));
if (ids == NULL) {
*out = NULL;
return 0;
}
size_t i = 0;
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it))
ids[i++] = *(uint32_t *)it;
*out = ids;
return n;
}
/**
* @brief Lazily initialize both global subscription lists.
*
* Called under g_map_mtx before any global list operation.
*/
static void subs_init_once(void)
{
if (!g_map_inited) {
seq_arr_init(&g_all_frmt1_subs, sizeof(uint32_t));
seq_arr_init(&g_all_frmt2_subs, sizeof(uint32_t));
g_map_inited = true;
}
}
/* ── Per-SM init / remove ── */
void init_dapp_subs_data(dapp_subs_data_t *dapp_subs_data)
{
pthread_mutex_lock(&dapp_mutex);
seq_arr_init(&dapp_subs_data->frmt_1_subs, sizeof(uint32_t));
seq_arr_init(&dapp_subs_data->frmt_2_subs, sizeof(uint32_t));
pthread_mutex_unlock(&dapp_mutex);
}
void remove_dapp_subs_data(dapp_subs_data_t *dapp_subs_data, uint32_t ric_req_id)
{
pthread_mutex_lock(&dapp_mutex);
elm_arr_t elm_f1 = find_if(&dapp_subs_data->frmt_1_subs, &ric_req_id, eq_uint32);
const bool had_f1 = (elm_f1.it != NULL);
elm_arr_t elm_f2 = find_if(&dapp_subs_data->frmt_2_subs, &ric_req_id, eq_uint32);
const bool had_f2 = (elm_f2.it != NULL);
if (had_f1)
seq_arr_erase(&dapp_subs_data->frmt_1_subs, elm_f1.it);
if (had_f2)
seq_arr_erase(&dapp_subs_data->frmt_2_subs, elm_f2.it);
if (!had_f1 && !had_f2)
printf("[E2 AGENT][WARN] Tried to remove unknown RIC request ID: %u\n", ric_req_id);
pthread_mutex_unlock(&dapp_mutex);
if (had_f1)
ric_subs_frmt1_remove(ric_req_id);
if (had_f2)
ric_subs_frmt2_remove(ric_req_id);
}
/* ── Per-SM insert ── */
void insert_frmt_1_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id)
{
pthread_mutex_lock(&dapp_mutex);
seq_arr_push_back(&d->frmt_1_subs, &ric_req_id, sizeof(ric_req_id));
pthread_mutex_unlock(&dapp_mutex);
}
void insert_frmt_2_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id)
{
pthread_mutex_lock(&dapp_mutex);
seq_arr_push_back(&d->frmt_2_subs, &ric_req_id, sizeof(ric_req_id));
pthread_mutex_unlock(&dapp_mutex);
}
/* ── Global Format 1 ── */
void ric_subs_frmt1_add(uint32_t ric_req_id)
{
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
seq_push_unique(&g_all_frmt1_subs, ric_req_id);
pthread_mutex_unlock(&g_map_mtx);
}
size_t ric_subs_frmt1_snapshot(uint32_t **out)
{
assert(out != NULL);
*out = NULL;
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
size_t n = seq_snapshot_locked(&g_all_frmt1_subs, out);
pthread_mutex_unlock(&g_map_mtx);
return n;
}
void ric_subs_frmt1_remove(uint32_t ric_req_id)
{
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
(void)seq_remove_first(&g_all_frmt1_subs, ric_req_id);
pthread_mutex_unlock(&g_map_mtx);
}
/* ── Global Format 2 ── */
void ric_subs_frmt2_add(uint32_t ric_req_id)
{
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
seq_push_unique(&g_all_frmt2_subs, ric_req_id);
pthread_mutex_unlock(&g_map_mtx);
}
size_t ric_subs_frmt2_snapshot(uint32_t **out)
{
assert(out != NULL);
*out = NULL;
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
size_t n = seq_snapshot_locked(&g_all_frmt2_subs, out);
pthread_mutex_unlock(&g_map_mtx);
return n;
}
void ric_subs_frmt2_remove(uint32_t ric_req_id)
{
pthread_mutex_lock(&g_map_mtx);
subs_init_once();
(void)seq_remove_first(&g_all_frmt2_subs, ric_req_id);
pthread_mutex_unlock(&g_map_mtx);
}

View File

@@ -0,0 +1,114 @@
#ifndef RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
#define RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
#include "openair2/E2AP/flexric/src/sm/dapp_sm/ie/dapp_data_ie.h"
#include "common/utils/ds/seq_arr.h"
/**
* @brief Per-SM subscription bookkeeping for the DAPP service model.
*
* Tracks which RIC request IDs are registered for each indication format.
* Used by the DAPP agent to remove subscriptions when the RIC deletes them.
*/
typedef struct {
seq_arr_t frmt_1_subs; /**< RIC request IDs subscribed to Format 1 (E3 data reports) */
seq_arr_t frmt_2_subs; /**< RIC request IDs subscribed to Format 2 (E3 subscription map) */
} dapp_subs_data_t;
/**
* @brief Initialize both per-SM subscription lists.
*
* Must be called once before any insert/remove operations.
*
* @param dapp_subs_data Pointer to the per-SM subscription state.
*/
void init_dapp_subs_data(dapp_subs_data_t *dapp_subs_data);
/**
* @brief Remove a RIC request ID from all per-SM lists and global registries.
*
* Searches both frmt_1_subs and frmt_2_subs for @p ric_req_id.
* For each list where it is found, erases it locally and calls the
* corresponding global remove function. Logs a warning if the ID
* is not found in either list.
*
* @param dapp_subs_data Pointer to the per-SM subscription state.
* @param ric_req_id The RIC request ID to remove.
*/
void remove_dapp_subs_data(dapp_subs_data_t *dapp_subs_data, uint32_t ric_req_id);
/**
* @brief Record a RIC request ID in the per-SM Format 1 list.
*
* @param d Pointer to the per-SM subscription state.
* @param ric_req_id The RIC request ID to insert.
*/
void insert_frmt_1_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
/**
* @brief Add a RIC request ID to the global Format 1 registry.
*
* The global registry is used by generate_e2_indication_from_e3_dapp_report()
* to find all subscribers that should receive E3 data report indications.
* Duplicates are silently ignored.
*
* @param ric_req_id The RIC request ID to register.
*/
void ric_subs_frmt1_add(uint32_t ric_req_id);
/**
* @brief Remove a RIC request ID from the global Format 1 registry.
*
* @param ric_req_id The RIC request ID to unregister.
*/
void ric_subs_frmt1_remove(uint32_t ric_req_id);
/**
* @brief Snapshot all RIC request IDs in the global Format 1 registry.
*
* Allocates and returns a caller-owned array of RIC request IDs.
* The caller must free() the returned array.
*
* @param[out] out Set to the allocated array, or NULL if empty.
* @return Number of IDs in the snapshot (0 if none).
*/
size_t ric_subs_frmt1_snapshot(uint32_t **out);
/**
* @brief Record a RIC request ID in the per-SM Format 2 list.
*
* @param d Pointer to the per-SM subscription state.
* @param ric_req_id The RIC request ID to insert.
*/
void insert_frmt_2_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
/**
* @brief Add a RIC request ID to the global Format 2 registry.
*
* The global registry is used by generate_e2_indication_dapp_e3_subscriptions()
* to find all subscribers that should receive subscription map indications.
* Duplicates are silently ignored.
*
* @param ric_req_id The RIC request ID to register.
*/
void ric_subs_frmt2_add(uint32_t ric_req_id);
/**
* @brief Remove a RIC request ID from the global Format 2 registry.
*
* @param ric_req_id The RIC request ID to unregister.
*/
void ric_subs_frmt2_remove(uint32_t ric_req_id);
/**
* @brief Snapshot all RIC request IDs in the global Format 2 registry.
*
* Allocates and returns a caller-owned array of RIC request IDs.
* The caller must free() the returned array.
*
* @param[out] out Set to the allocated array, or NULL if empty.
* @return Number of IDs in the snapshot (0 if none).
*/
size_t ric_subs_frmt2_snapshot(uint32_t **out);
#endif

View File

@@ -20,6 +20,10 @@
#include "O-RAN/ran_func_kpm.h"
#include "O-RAN/ran_func_rc.h"
#ifdef E3_AGENT
#include "O-RAN/ran_func_dapp.h"
#endif
static
void init_read_ind_tbl(read_ind_fp (*read_ind_tbl)[SM_AGENT_IF_READ_V0_END])
{
@@ -36,6 +40,9 @@ void init_read_ind_tbl(read_ind_fp (*read_ind_tbl)[SM_AGENT_IF_READ_V0_END])
(*read_ind_tbl)[KPM_STATS_V3_0] = read_kpm_sm ;
(*read_ind_tbl)[RAN_CTRL_STATS_V1_03] = read_rc_sm;
#ifdef E3_AGENT
(*read_ind_tbl)[DAPP_STATS_V0] = read_dapp_sm;
#endif
}
static
@@ -54,6 +61,9 @@ void init_read_setup_tbl(read_e2_setup_fp (*read_setup_tbl)[SM_AGENT_IF_E2_SETUP
(*read_setup_tbl)[KPM_V3_0_AGENT_IF_E2_SETUP_ANS_V0] = read_kpm_setup_sm ;
(*read_setup_tbl)[RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0] = read_rc_setup_sm;
#ifdef E3_AGENT
(*read_setup_tbl)[DAPP_AGENT_IF_E2_SETUP_ANS_V0] = read_dapp_setup_sm;
#endif
}
static
@@ -71,6 +81,9 @@ void init_write_ctrl( write_ctrl_fp (*write_ctrl_tbl)[SM_AGENT_IF_WRITE_CTRL_V0_
#endif
(*write_ctrl_tbl)[RAN_CONTROL_CTRL_V1_03] = write_ctrl_rc_sm;
#ifdef E3_AGENT
(*write_ctrl_tbl)[DAPP_CTRL_V0] = write_ctrl_dapp_sm;
#endif
}
static
@@ -89,6 +102,9 @@ void init_write_subs(write_subs_fp (*write_subs_tbl)[SM_AGENT_IF_WRITE_SUBS_V0_E
(*write_subs_tbl)[KPM_SUBS_V3_0] = NULL;
(*write_subs_tbl)[RAN_CTRL_SUBS_V1_03] = write_subs_rc_sm;
#ifdef E3_AGENT
(*write_subs_tbl)[DAPP_SUBS_V0] = write_subs_dapp_sm;
#endif
}
sm_io_ag_ran_t init_ran_func_ag(void)

View File

@@ -0,0 +1,49 @@
find_package(PkgConfig REQUIRED)
pkg_check_modules(CLIBE3 REQUIRED libe3)
if(NOT CLIBE3_FOUND)
message(FATAL_ERROR "libe3 not found and required for E3 Agent")
endif()
message(STATUS "Using for E3AP libe3 version: ${CLIBE3_VERSION}")
include_directories(${CLIBE3_INCLUDE_DIRS})
# Set default encoding format if not specified
if(NOT DEFINED E3_ENCODING_FORMAT)
set(E3_ENCODING_FORMAT "ASN1" CACHE STRING "E3AP encoding format (ASN1 or JSON)")
endif()
# Validate encoding format
if(NOT E3_ENCODING_FORMAT MATCHES "^(ASN1|JSON)$")
message(FATAL_ERROR "Invalid E3_ENCODING_FORMAT: ${E3_ENCODING_FORMAT}. Must be ASN1 or JSON")
endif()
message(STATUS "E3AP encoding format: ${E3_ENCODING_FORMAT}")
add_subdirectory(service_models)
add_library(e3ap e3_agent.c config/e3_config.c)
# target_link_libraries(e3ap PUBLIC OCP_ITTI) # TODO
# Set encoding format compile definitions - both string value and format flags
target_compile_definitions(e3ap PRIVATE E3_ENCODING_FORMAT="${E3_ENCODING_FORMAT}")
if(E3_ENCODING_FORMAT STREQUAL "ASN1")
target_compile_definitions(e3ap PRIVATE E3_ASN1_FORMAT)
target_link_libraries(e3ap PUBLIC ${CLIBE3_LIBRARIES} asn1_e3ap ${SERVICE_MODELS_LIBRARY})
elseif(E3_ENCODING_FORMAT STREQUAL "JSON")
target_compile_definitions(e3ap PRIVATE E3_JSON_FORMAT)
pkg_check_modules(JSON_C REQUIRED json-c)
if(NOT JSON_C_FOUND)
message(FATAL_ERROR "JSON-C not found and required for JSON encoding, install it with apt-get install libjson-c-dev")
endif()
target_link_libraries(e3ap PUBLIC ${CLIBE3_LIBRARIES} ${JSON_C_LIBRARIES} ${SERVICE_MODELS_LIBRARY})
target_include_directories(e3ap PUBLIC ${JSON_C_INCLUDE_DIRS})
endif()
if(E2_AGENT)
target_link_libraries(e3ap PUBLIC e2_ran_func_cuup e2_ran_func_du_cucp_cuup)
target_compile_definitions(e3ap PRIVATE E2_AGENT)
target_include_directories(e3ap PUBLIC ${OPENAIR_DIR}/openair2/E2AP/RAN_FUNCTION/O-RAN)
endif()

View File

@@ -0,0 +1,83 @@
#include "e3_config.h"
#include "configuration.h"
#include "common/utils/LOG/log.h"
#include "config.h"
#include "common/config/config_paramdesc.h"
#include "common/config/config_userapi.h"
#include <string.h>
#include <stdlib.h>
#define E3CONFIG_SECTION "E3Configuration"
#define CONFIG_STRING_E3_LINK_PARAM "link"
#define CONFIG_STRING_E3_TRANSPORT_PARAM "transport"
#define CONFIG_STRING_E3_SAMPLING_PARAM "sampling"
// clang-format off
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* configuration parameters for the E3AP device */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define simOpt PARAMFLAG_NOFREE|PARAMFLAG_CMDLINE_NOPREFIXENABLED
#define E3_PARAMS_DESC { \
{"link", "Link layer for E3", simOpt, .strptr=&config->link, .defstrval="posix", TYPE_STRING, 0 },\
{"transport", "Transport layer for E3", simOpt, .strptr=&config->transport, .defstrval="ipc", TYPE_STRING, 0 },\
};
// clang-format on
const char *E3_VALID_CONFIGURATIONS[][2] = {
{"zmq", "ipc"},
{"zmq", "tcp"},
// {"zmq", "sctp"}, // implemented but not working because zeromq does not support it (yet)
{"posix", "tcp"},
{"posix", "sctp"},
{"posix", "ipc"}};
void e3_readconfig(e3_cmdline_config_t *config)
{
paramdef_t e3_params[] = E3_PARAMS_DESC;
int ret = config_get(config_get_if(), e3_params, sizeof(e3_params) / sizeof(*(e3_params)), E3CONFIG_SECTION);
AssertFatal(ret >= 0, "configuration couldn't be performed\n");
LOG_I(E3AP,
"this is the configuration extracted: link %s transport %s\n",
config->link,
config->transport);
}
void validate_configuration(e3_cmdline_config_t *config)
{
if (!config) {
LOG_E(E3AP, "Configuration is null");
abort();
}
// Check if link is "posix" or "zmq" using strncmp
if (strncmp(config->link, "posix", 5) != 0 && strncmp(config->link, "zmq", 3) != 0) {
LOG_E(E3AP, "Wrong link");
abort();
}
// Check if transport is "tcp", "sctp", or "ipc" using strncmp
if (strncmp(config->transport, "tcp", 3) != 0 && strncmp(config->transport, "sctp", 4) != 0
&& strncmp(config->transport, "ipc", 3) != 0) {
LOG_E(E3AP, "Wrong transport");
abort();
}
// Validate the combination of link and transport
int combo_valid = 0;
for (size_t i = 0; i < sizeof(E3_VALID_CONFIGURATIONS) / sizeof(E3_VALID_CONFIGURATIONS[0]); i++) {
if (strncmp(config->link, E3_VALID_CONFIGURATIONS[i][0], strlen(E3_VALID_CONFIGURATIONS[i][0])) == 0
&& strncmp(config->transport, E3_VALID_CONFIGURATIONS[i][1], strlen(E3_VALID_CONFIGURATIONS[i][1])) == 0) {
combo_valid = 1;
break;
}
}
if (!combo_valid) {
LOG_E(E3AP, "Wrong combination");
abort();
}
}

View File

@@ -0,0 +1,27 @@
#ifndef E3_CONFIG_H
#define E3_CONFIG_H
#include <stddef.h>
// Configuration structure for E3AP
typedef struct {
char *link; // Link layer (posix, zmq)
char *transport; // Transport layer (tcp, sctp, ipc)
} e3_cmdline_config_t;
// Valid configuration combinations
extern const char *E3_VALID_CONFIGURATIONS[][2];
/**
* @brief Read E3 configuration from the OAI config file
* @param e3_configs Pointer to configuration structure to fill
*/
void e3_readconfig(e3_cmdline_config_t *config);
/**
* @brief Validate E3AP configuration parameters
* @param config Pointer to configuration structure to validate
*/
void validate_configuration(e3_cmdline_config_t *config);
#endif // E3_CONFIG_H

267
openair2/E3AP/e3_agent.c Normal file
View File

@@ -0,0 +1,267 @@
#include "e3_agent.h"
#include "config/e3_config.h"
#include "service_models/spectrum_sm/spectrum_sm.h"
// TODO replace pthreads with itti or use a faster way
// #include "intertask_interface.h"
// #include "create_tasks.h"
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <libe3/c_api.h>
#include "common/utils/system.h"
#include "common/ran_context.h"
#include "common/utils/LOG/log.h"
e3_agent_global_t e3 = {0};
static void e2_e3_bridge(uint32_t dapp_id,
uint32_t ran_function_id,
const uint8_t *report_data,
size_t report_size)
{
LOG_D(E3AP,
"Received dApp report for RAN function %u from dApp %u (%zu bytes)\n",
ran_function_id,
dapp_id,
report_size);
#ifdef E2_AGENT
if (!report_data && report_size > 0) {
LOG_E(E3AP, "Invalid dApp report payload: report_data is NULL while report_size=%zu\n", report_size);
return;
}
generate_e2_indication_from_e3_dapp_report(ran_function_id,
dapp_id,
report_size,
report_data);
#else
(void)report_data;
#endif
}
void on_dapp_status_changed(void)
{
LOG_I(E3AP, "dApp status changed, triggering RIC Service Update\n");
#ifdef E2_AGENT
notify_dapp_status_changed();
#endif
}
int e3_init()
{
LOG_D(E3AP, "Read configuration\n");
e3_cmdline_config_t *e3_cmdline_configs =
(e3_cmdline_config_t *)calloc(1, sizeof(e3_cmdline_config_t));
if (!e3_cmdline_configs) {
LOG_E(E3AP, "Failed to allocate E3 cmdline config\n");
return -1;
}
e3_readconfig(e3_cmdline_configs);
LOG_D(E3AP, "Validate configuration\n");
validate_configuration(e3_cmdline_configs);
// Create e3_config_t from e3_cmdline_configs (only link and transport, rest defaults)
e3_config_t config = {0};
config.ran_identifier = "OAI DU";
config.log_level = 3; // INFO
#ifdef E3_ASN1_FORMAT
config.encoding = 0;
#else
config.encoding = 1;
#endif
// Map string values to enum/int as needed for link_layer and transport_layer
if (e3_cmdline_configs->link && strcmp(e3_cmdline_configs->link, "zmq") == 0) {
config.link_layer = 0; // ZMQ
} else if (e3_cmdline_configs->link && strcmp(e3_cmdline_configs->link, "posix") == 0) {
config.link_layer = 1; // POSIX
} else {
config.link_layer = -1; // default
}
if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "sctp") == 0) {
config.transport_layer = 0; // SCTP
} else if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "tcp") == 0) {
config.transport_layer = 1; // TCP
} else if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "ipc") == 0) {
config.transport_layer = 2; // IPC
} else {
config.transport_layer = -1; // default
}
// All other fields left as default (0 or NULL)
// Create E3Agent with config
e3.agent = e3_agent_create_with_config(&config);
free(e3_cmdline_configs);
e3_cmdline_configs = NULL;
if (!e3.agent) {
LOG_E(E3AP, "Failed to create E3Agent with config\n");
return -1;
}
// Initialize agent
e3_error_t err = e3_agent_init(e3.agent);
if (err != 0) {
LOG_E(E3AP, "Failed to initialize E3Agent (err=%d)\n", err);
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
// Start agent
err = e3_agent_start(e3.agent);
if (err != 0) {
LOG_E(E3AP, "Failed to start E3Agent (err=%d)\n", err);
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
err = e3_agent_set_dapp_report_handler(e3.agent, e2_e3_bridge);
if (err != 0) {
LOG_E(E3AP, "Failed to set dApp report handler (err=%d: %s)\n", err, e3_error_to_string(err));
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
err = e3_agent_set_dapp_status_changed_handler(e3.agent, on_dapp_status_changed);
if (err != 0) {
LOG_E(E3AP, "Failed to set dApp status changed handler (err=%d: %s)\n", err, e3_error_to_string(err));
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
// Register the SMs
// SM Spectrum
e3_c_service_model_desc_t *desc_sm_spectrum = create_spectrum_sm_model();
if (!desc_sm_spectrum) {
LOG_E(E3AP, "Failed to create Spectrum SM descriptor\n");
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
e3_service_model_handle_t *sm_spectrum = e3_service_model_create_from_c(desc_sm_spectrum);
if (!sm_spectrum) {
LOG_E(E3AP, "Failed to create Spectrum SM handle\n");
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
spectrum_sm_set_handle(sm_spectrum);
err = e3_agent_register_sm(e3.agent, sm_spectrum);
if (err != 0) {
LOG_E(E3AP, "Failed to register Spectrum SM (err=%d: %s)\n", err, e3_error_to_string(err));
e3_service_model_destroy(sm_spectrum);
e3_agent_destroy(e3.agent);
e3.agent = NULL;
return -1;
}
return 0;
}
int e3_destroy()
{
// Stop and destroy the E3Agent if it exists
if (e3.agent) {
e3_agent_stop(e3.agent);
e3_agent_destroy(e3.agent);
e3.agent = NULL;
}
return 0;
}
int e3_send_xapp_control(uint32_t dapp_id,
uint32_t ran_function_id,
const uint8_t *data,
size_t len)
{
if (!e3.agent) {
LOG_E(E3AP, "E3 agent not initialized: cannot send xApp control\n");
return -1;
}
if (data == NULL && len > 0) {
LOG_E(E3AP, "data is not initialized, but len > 0\n");
return -1;
}
e3_error_t err = e3_agent_send_xapp_control(e3.agent, dapp_id, ran_function_id, data, len);
if (err != E3_SUCCESS) {
LOG_E(E3AP,
"Failed to send xApp control to dApp %u for RAN function %u (err=%d)\n",
dapp_id,
ran_function_id,
err);
return -1;
}
return 0;
}
e3_dapp_subscription_map_t e3_get_dapp_subscription_map(void)
{
e3_dapp_subscription_map_t map = {0};
if (!e3.agent) {
LOG_W(E3AP, "E3 agent not initialized: cannot query dApp subscriptions\n");
return map;
}
size_t num_dapps = 0;
uint32_t *dapp_ids = e3_agent_get_registered_dapps(e3.agent, &num_dapps);
if (!dapp_ids || num_dapps == 0) {
e3_agent_free_uint32_array(dapp_ids);
return map;
}
map.dapps = (e3_dapp_info_t *)calloc(num_dapps, sizeof(e3_dapp_info_t));
if (!map.dapps) {
LOG_E(E3AP, "Failed to allocate dApp subscription map\n");
e3_agent_free_uint32_array(dapp_ids);
return map;
}
map.num_dapps = num_dapps;
for (size_t i = 0; i < num_dapps; i++) {
map.dapps[i].dapp_id = dapp_ids[i];
size_t num_subs = 0;
uint32_t *subs = e3_agent_get_dapp_subscriptions(e3.agent, dapp_ids[i], &num_subs);
if (subs && num_subs > 0) {
map.dapps[i].e3_ran_func_ids = subs; // transfer ownership from libe3 malloc
map.dapps[i].num_e3_ran_funcs = num_subs;
} else {
map.dapps[i].e3_ran_func_ids = NULL;
map.dapps[i].num_e3_ran_funcs = 0;
e3_agent_free_uint32_array(subs);
}
}
e3_agent_free_uint32_array(dapp_ids);
return map;
}
void e3_free_dapp_subscription_map(e3_dapp_subscription_map_t *map)
{
if (!map || !map->dapps)
return;
for (size_t i = 0; i < map->num_dapps; i++) {
e3_agent_free_uint32_array(map->dapps[i].e3_ran_func_ids);
}
free(map->dapps);
map->dapps = NULL;
map->num_dapps = 0;
}

59
openair2/E3AP/e3_agent.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef E3_AGENT_H
#define E3_AGENT_H
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <libe3/c_api.h>
#include "config/e3_config.h"
#ifdef E2_AGENT
#include "ran_func_dapp_extern.h"
#endif
typedef struct {
e3_agent_handle_t *agent;
e3_service_model_handle_t **service_models;
size_t num_service_models;
} e3_agent_global_t;
typedef struct {
uint32_t dapp_id;
uint32_t *e3_ran_func_ids;
size_t num_e3_ran_funcs;
} e3_dapp_info_t;
typedef struct {
e3_dapp_info_t *dapps;
size_t num_dapps;
} e3_dapp_subscription_map_t;
extern e3_agent_global_t e3;
int e3_init();
int e3_destroy();
int e3_send_xapp_control(uint32_t dapp_id,
uint32_t ran_function_id,
const uint8_t *data,
size_t len);
/**
* @brief Get all connected dApps and their RAN function subscriptions.
*
* Caller must free the result with e3_free_dapp_subscription_map().
*
* @return populated map on success, {NULL, 0} on error or if no dApps
*/
e3_dapp_subscription_map_t e3_get_dapp_subscription_map(void);
/**
* @brief Free a subscription map returned by e3_get_dapp_subscription_map().
*/
void e3_free_dapp_subscription_map(e3_dapp_subscription_map_t *map);
#endif // E3_AGENT_H

View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.10)
add_subdirectory(spectrum_sm)
add_library(service_models INTERFACE)
target_include_directories(service_models INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/common/utils/LOG
${CMAKE_SOURCE_DIR}/openair2/E3AP
${CLIBE3_INCLUDE_DIRS}
)
target_link_libraries(service_models INTERFACE
${SPECTRUM_SM_LIBRARY}
)
set(SERVICE_MODELS_LIBRARY service_models PARENT_SCOPE)

View File

@@ -0,0 +1,44 @@
# Spectrum Service Model CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
add_subdirectory(MESSAGES)
# Spectrum SM source files
set(SPECTRUM_SM_SOURCES
spectrum_sm.c
spectrum_enc.c
spectrum_dec.c
)
# Create spectrum SM library
add_library(spectrum_sm STATIC ${SPECTRUM_SM_SOURCES})
# Include directories
target_include_directories(spectrum_sm PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_BINARY_DIR}/MESSAGES # ASN.1 generated headers
${CMAKE_SOURCE_DIR}/common/utils/LOG
${CMAKE_SOURCE_DIR}/openair2/E3AP
${CMAKE_SOURCE_DIR}/common/utils/T/tracer # T-tracer headers
${CLIBE3_INCLUDE_DIRS}
)
target_compile_definitions(spectrum_sm PRIVATE T_MESSAGES_PATH="${OPENAIR_DIR}/common/utils/T/T_messages.txt")
target_link_libraries(spectrum_sm spectrum_sm_asn1 tracer_utils)
add_dependencies(spectrum_sm spectrum_sm_asn1)
# Compile-time format selection (inherit from E3AP)
if(E3_ENCODING_FORMAT STREQUAL "ASN1")
target_compile_definitions(spectrum_sm PRIVATE SPECTRUM_SM_ASN1_FORMAT)
message(STATUS "Spectrum SM: Using ASN.1 encoding")
else()
target_compile_definitions(spectrum_sm PRIVATE SPECTRUM_SM_JSON_FORMAT)
target_link_libraries(spectrum_sm json-c)
message(STATUS "Spectrum SM: Using JSON encoding")
endif()
# Export spectrum SM library
set(SPECTRUM_SM_LIBRARY spectrum_sm PARENT_SCOPE)

View File

@@ -0,0 +1,65 @@
-- Spectrum Service Model ASN.1 Definitions
-- This service model extends E3 messages for spectrum sharing applications
--
-- Usage with E3 Protocol:
-- - Spectrum-IndicationData structures are encoded into the protocolData field of E3-IndicationMessage
-- - Spectrum-ControlData structures are encoded into the actionData field of E3-ControlAction
-- - Use E3-PDU as the top-level message structure
SPECTRUM-SM DEFINITIONS AUTOMATIC TAGS ::= BEGIN
-- ============================================================================
-- SM SPECTRUM RAN FUNCTION DATA STRUCTURES
-- ============================================================================
-- Spectrum RAN function data
Spectrum-RanFunctionData ::= SEQUENCE {
name OCTET STRING (SIZE (1..32)),
version INTEGER (0..65535),
description OCTET STRING (SIZE (1..128))
}
-- ============================================================================
-- INDICATION MESSAGE STRUCTURES
-- ============================================================================
-- Spectrum IQ Data Indication (raw I/Q samples from RAN)
Spectrum-IQDataIndication ::= SEQUENCE {
iqSamples OCTET STRING (SIZE (1..16384)), -- Raw I/Q data as int16 array
sampleCount INTEGER (0..32768), -- Number of I/Q samples
timestamp INTEGER (0..2147483647) OPTIONAL -- Unix timestamp when samples were captured
}
-- ============================================================================
-- CONTROL ACTION STRUCTURES
-- ============================================================================
-- PRB Blacklist Control (main control message used by spectrum dApp)
Spectrum-PRBBlacklistControl ::= SEQUENCE {
blacklistedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex), -- List of PRB indices
samplingThreshold INTEGER (0..100) OPTIONAL, -- Sampling ratio for the IQ captures, one delivery each samplingThreshold
validityPeriod INTEGER (1..3600) OPTIONAL -- How long this blacklist is valid (seconds)
}
-- ============================================================================
-- xAPP CONTROL ACTION STRUCTURES
-- ============================================================================
-- Spectrum Configuration Control
Spectrum-ConfigControl ::= SEQUENCE {
noiseFloorThreshold INTEGER (-100..100) OPTIONAL, -- Update noise floor threshold
averagingFrames INTEGER (1..255) OPTIONAL, -- Update averaging window
enable BOOLEAN OPTIONAL -- Enable/disable spectrum monitoring
}
-- ============================================================================
-- CONSTANTS AND CONSTRAINTS
-- ============================================================================
-- Maximum values based on 5G NR specifications
maxPRBs INTEGER ::= 273 -- Maximum PRBs in 5G NR
maxPRBIndex INTEGER ::= 272 -- maxPRBs - 1
END

View File

@@ -0,0 +1,102 @@
set(SPECTRUM_SM_GRAMMAR ASN1/V1/e3sm_spectrum.asn)
set(spectrum_sm_source
ANY_aper.c
ANY.c
ANY_uper.c
ANY_xer.c
aper_decoder.c
aper_encoder.c
aper_opentype.c
aper_support.c
asn_application.c
asn_bit_data.c
asn_codecs_prim.c
asn_codecs_prim_xer.c
asn_internal.c
asn_random_fill.c
ber_tlv_length.c
ber_tlv_tag.c
constr_CHOICE.c
constr_SEQUENCE.c
constr_TYPE.c
constraints.c
OCTET_STRING_aper.c
OCTET_STRING.c
OCTET_STRING_uper.c
OCTET_STRING_xer.c
per_decoder.c
per_encoder.c
per_opentype.c
per_support.c
BOOLEAN.c
BOOLEAN_aper.c
BOOLEAN_uper.c
BOOLEAN_xer.c
INTEGER.c
INTEGER_aper.c
INTEGER_uper.c
INTEGER_xer.c
uper_decoder.c
uper_encoder.c
uper_opentype.c
uper_support.c
xer_decoder.c
xer_encoder.c
xer_support.c
# Spectrum SM specific generated files
Spectrum-IQDataIndication.c
Spectrum-PRBBlacklistControl.c
Spectrum-ConfigControl.c
Spectrum-RanFunctionData.c
)
set(spectrum_sm_headers
ANY_aper.h
ANY.h
ANY_uper.h
ANY_xer.h
aper_decoder.h
aper_encoder.h
aper_opentype.h
aper_support.h
asn_application.h
asn_bit_data.h
asn_codecs_prim.h
asn_codecs_prim_xer.h
asn_internal.h
asn_random_fill.h
ber_tlv_length.h
ber_tlv_tag.h
constr_CHOICE.h
constr_SEQUENCE.h
constr_TYPE.h
constraints.h
OCTET_STRING_aper.h
OCTET_STRING.h
OCTET_STRING_uper.h
OCTET_STRING_xer.h
per_decoder.h
per_encoder.h
per_opentype.h
per_support.h
BOOLEAN.h
BOOLEAN_aper.h
BOOLEAN_uper.h
BOOLEAN_xer.h
INTEGER.h
INTEGER_aper.h
INTEGER_uper.h
INTEGER_xer.h
uper_decoder.h
uper_encoder.h
uper_opentype.h
uper_support.h
xer_decoder.h
xer_encoder.h
xer_support.h
Spectrum-IQDataIndication.h
Spectrum-PRBBlacklistControl.h
Spectrum-ConfigControl.h
Spectrum-RanFunctionData.h
)

View File

@@ -0,0 +1,26 @@
set(SPECTRUM_SM_VERSION 1 0 0)
make_version(SPECTRUM_SM_cc ${SPECTRUM_SM_VERSION})
string(REPLACE ";" "." SPECTRUM_SM_RELEASE "${SPECTRUM_SM_VERSION}")
if(SPECTRUM_SM_RELEASE VERSION_EQUAL "1.0.0")
include(ASN1/V1/e3sm_spectrum.cmake)
else()
message(FATAL_ERROR "unknown SPECTRUM_SM_RELEASE ${SPECTRUM_SM_RELEASE}")
endif()
message(STATUS "Selected SPECTRUM_SM_VERSION: ${SPECTRUM_SM_RELEASE}")
run_asn1c(${CMAKE_CURRENT_SOURCE_DIR}/${SPECTRUM_SM_GRAMMAR}
""
OUTPUT ${spectrum_sm_source} ${spectrum_sm_headers}
OPTIONS -pdu=all -gen-APER -gen-UPER -no-gen-JER -no-gen-BER -no-gen-OER -fno-include-deps -fcompound-names -findirect-choice -no-gen-example
COMMENT "Spectrum SM source files"
)
add_library(spectrum_sm_asn1 ${spectrum_sm_source})
target_include_directories(spectrum_sm_asn1 PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_compile_options(spectrum_sm_asn1 PRIVATE -DASN_DISABLE_OER_SUPPORT -w)
# Export variables to parent scope
set(SPECTRUM_SM_ASN1_LIB spectrum_sm_asn1 PARENT_SCOPE)
set(SPECTRUM_SM_ASN1_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}" PARENT_SCOPE)

View File

@@ -0,0 +1,375 @@
# Spectrum Service Model (SM) Architecture
This document describes the E3 Service Model Spectrum implementation in OpenAirInterface, detailing the IQ data collection mechanism, the PRB control loop, and the xApp-dApp coordination path.
## Overview
The Spectrum SM provides three key functionalities:
1. **Indication (Sensing)**: Collection and forwarding of frequency-domain IQ samples for spectrum monitoring
2. **Control**: Dynamic PRB (Physical Resource Block) blacklisting based on external dApp decisions
3. **xApp-dApp Interaction**: Bidirectional PRB-level information exchange between xApp (via E2) and dApp (via E3), enabling coordinated RAN control from both interfaces
---
## 1. IQ Data Collection (Indication Path)
### 1.1 Data Source Location
The IQ samples are collected in the PHY layer at:
- **File**: `openair1/SCHED_NR/phy_procedures_nr_gNB.c`
- **Function**: `phy_procedures_gNB_uespec_RX()`
- **Data Variable**: `gNB->common_vars.rxdataF[0]`
### 1.2 Signal Processing Stage
The collected IQs are **PRE-EQUALIZATION** samples in the **frequency domain (post-FFT)**.
```
┌─────────────┐ ┌─────────┐ ┌──────────────────────────────────┐ ┌─────────────────┐ ┌─────────────┐
│ RF Frontend │ → │ ADC │ → │ Time Domain Processing + FFT │ → │ rxdataF │ → │ Equalization │
│ │ │ │ │ (CP Removal, Synchronization) │ │ ◄── E3 COLLECTS │ │ │
└─────────────┘ └─────────┘ └──────────────────────────────────┘ └─────────────────┘ └─────────────┘
┌─────────────────┐
│ Channel Est. │
│ rxdataF_comp │
│ (post-equal.) │
└─────────────────┘
```
### 1.3 Position in 5G NR PHY Uplink Processing Chain
| Stage | Description | E3 Agent |
|-------|-------------|----------|
| ADC | Analog-to-Digital Conversion | ✗ Before |
| RF Frontend | Filtering, amplification | ✗ Before |
| Time-domain RX | Sample buffering | ✗ Before |
| CP Removal | Cyclic prefix removal | ✗ Before |
| **FFT** | Time → Frequency transform | ✗ Before |
| **rxdataF** | **Frequency-domain samples** | **✓ COLLECTED HERE** |
| Channel Estimation | Estimate channel response | ✗ After |
| Equalization | Compensate channel effects | ✗ After |
| rxdataF_comp | Equalized samples | ✗ After |
| Demodulation | Symbol → bits | ✗ After |
| LDPC Decoding | Error correction | ✗ After |
### 1.4 Collection Parameters
| Parameter | Value | Description |
|-----------|-------|-------------|
| **Domain** | Frequency | Post-FFT samples |
| **Stage** | Pre-equalization | Raw channel + signal + noise |
| **Slot filter** | Slot 8 only | `slot_rx == 8` |
| **Symbol** | 12th OFDM symbol | Fixed symbol within slot |
| **Size** | `ofdm_symbol_size` samples | One full OFDM symbol |
| **Antenna** | First antenna (index 0) | Single antenna capture |
| **Data type** | `c16_t` (complex 16-bit) | I/Q interleaved |
| **Sampling control** | `sampling_threshold` | Decimation factor (default: 5) |
### 1.5 Code Reference
```c
// From phy_procedures_nr_gNB.c (lines ~1137-1161)
#ifdef E3_AGENT
c16_t **rxdataF_sen = gNB->common_vars.rxdataF[0];
if (nr_slot_select(&gNB->gNB_config, frame_rx, slot_rx) == NR_UPLINK_SLOT && slot_rx == 8) {
e3_sm_spectrum_control->sampling_counter++;
if (e3_sm_spectrum_control->sampling_counter > e3_sm_spectrum_control->sampling_threshold) {
const uint16_t n_symbols = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot;
uint64_t symbol_offset = (n_symbols)*gNB->frame_parms.ofdm_symbol_size + (12) * gNB->frame_parms.ofdm_symbol_size;
int32_t *rx_signal = (int32_t *)&rxdataF_sen[0][symbol_offset];
T(T_GNB_PHY_UL_FREQ_SENSING_SYMBOL, ...);
e3_sm_spectrum_control->sampling_counter = 0;
}
}
#endif
```
### 1.6 Implications
Since the samples are **pre-equalization**:
- They contain the **combined effect** of: transmitted signal + channel response + noise + interference
- They are **NOT** compensated for channel distortion
- Ideal for **spectrum sensing/monitoring** applications
- Can be used to analyze: interference levels, spectral occupancy, raw signal power per subcarrier
---
## 2. PRB Control (Control Path)
### 2.1 Control Flow Overview
```
┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────────┐
│ dApp/xApp │ → │ E3 Agent │ → │ spectrum_sm_process_ │ → │ e3_sm_spectrum_ │
│ (external) │ │ Interface │ │ dapp_control_action() │ │ control struct │
└──────────────┘ └──────────────┘ └─────────────────────────┘ └──────────────────┘
┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────────┐
│ Scheduling │ ← │ gNB-> │ ← │ nr_update_prb_policy() │ ← │ dyn_prbbl[] │
│ Decisions │ │ ulprbbl[] │ │ (every 128 frames) │ │ (blacklist) │
└──────────────┘ └──────────────┘ └─────────────────────────┘ └──────────────────┘
```
### 2.2 Control Data Structure
```c
typedef struct e3_sm_spectrum_control {
uint16_t* action_list; // Temporary buffer (array) for incoming PRB indices
int action_size; // Number of PRBs in action_list
uint16_t dyn_prbbl[MAX_BWP_SIZE]; // Dynamic PRB blacklist bitmap (per-PRB marker)
int ready; // Flag: new policy available
uint32_t sampling_threshold; // IQ sampling decimation factor
uint32_t sampling_counter; // Current sample count
pthread_mutex_t mutex; // Thread synchronization
} e3_sm_spectrum_control_t;
```
### 2.3 Control Processing
When a control message arrives from the dApp:
1. **Decode**: `spectrum_sm_process_control()` decodes the PRB control message (E3 SM control handler)
2. **Parse**: Extract blacklisted PRB indices from the message
3. **Update**: Set `dyn_prbbl[prb_index] = 0x3FFF` for each blacklisted PRB
4. **Signal**: Set `ready = 1` to indicate new policy is available
```c
// From spectrum_sm.c (actual control handler: `spectrum_sm_process_control()`)
for (size_t j = 0; j < elems; ++j) {
uint16_t prb = alist16[j];
if (prb < MAX_BWP_SIZE) {
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
}
}
e3_sm_spectrum_control->ready = 1;
```
### 2.4 Slot 8 Reservation for Spectrum Sensing
To ensure clean IQ samples for spectrum sensing (without UE transmissions), slot 8 is reserved by modifying the ULSCH TDA selection in `gNB_scheduler_ulsch.c`:
```c
// From gNB_scheduler_ulsch.c - get_ul_tda()
#ifdef E3_AGENT
// Empty symbol in slot 8 for spectrum sensing. Symbol 12.
// TODO handle when SRS is present
if (slot == 8) {
return 1; // Use TDA index 1 which avoids symbol 12
}
#endif // E3_AGENT
```
This forces the scheduler to use **TDA index 1** for slot 8, which results in:
- **No UE PUSCH transmission** on symbol 12 of slot 8
- The 12th OFDM symbol remains **free of scheduled UE traffic**
- IQ samples captured are purely **noise + external interference** (no OAI-scheduled signals)
### 2.5 PRB Blacklist Application in MAC Scheduler
The PRB blacklist is applied at multiple points in the MAC scheduler:
#### 2.5.1 Policy Update (Periodic)
```c
// Called every 128 frames (slot 0, frame & 127 == 0) in gNB_scheduler.c
#ifdef E3_AGENT
nr_update_prb_policy(module_idP, frame, slot);
#endif
```
The `nr_update_prb_policy()` function (in `gNB_scheduler_dlsch.c`):
- Acquires the mutex lock
- Copies `dyn_prbbl` to `gNB->ulprbbl` if `ready == 1`
- Resets `ready = 0`
- Logs the barred PRBs
#### 2.5.2 VRB Map Application (Every Slot)
```c
// Applied every slot during vrb_map clearing in gNB_scheduler.c
#ifdef E3_AGENT
uint16_t *vrb_map = cc[CC_id].vrb_map[i];
memcpy(vrb_map, &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
#endif
```
This ensures:
- **Uplink**: `vrb_map_UL` is initialized with the blacklist
- **Downlink**: `vrb_map` is initialized with the blacklist (when E3_AGENT enabled)
### 2.6 Effect on Scheduling
When a PRB is blacklisted (`ulprbbl[prb] = 0x3FFF`):
- The scheduler treats those PRBs as **already occupied**
- No UE transmissions (PUSCH/PUCCH) will be scheduled on those PRBs
- No gNB transmissions (PDSCH/PDCCH) will be scheduled on those PRBs
### 2.7 Control Timing
| Event | Timing |
|-------|--------|
| Control message reception | Asynchronous (from dApp) |
| Policy update check | Every 128 frames (~1.28s at 10ms frame) |
| VRB map application | Every slot |
---
## 3. Complete Closed-Loop Architecture
```
┌─────────────────────────────────────────┐
│ dApp │
│ (Spectrum analysis, ML inference, etc.)│
└────────────────┬────────────────────────┘
┌───────────┴───────────┐
│ E3 Interface │
│ (encode/decode) │
└───────────┬───────────┘
┌────────────────────────────────┼────────────────────────────────┐
│ │ │
▼ │ ▼
┌───────────────────────────┐ │ ┌───────────────────────────┐
│ INDICATION PATH │ │ │ CONTROL PATH │
│ (PHY Layer) │ │ │ (MAC Layer) │
│ │ │ │ │
│ rxdataF (freq domain) │ │ │ dyn_prbbl[] → ulprbbl[] │
│ │ │ │ │ │ │
│ ▼ │ │ │ ▼ │
│ T-Tracer event │ │ │ vrb_map initialization │
│ │ │ │ │ │ │
│ ▼ │ │ │ ▼ │
│ spectrum_sm encode │───────────────────┘ │ Scheduler decisions │
│ │ │ │ │
│ ▼ │ └───────────────────────────┘
│ sm_indication_data_set │
└───────────────────────────┘
```
| Parameter | Default | Location | Description |
|-----------|---------|----------|-------------|
| `sampling_threshold` | 5 | spectrum_sm.c | IQ capture every N×10ms |
| `slot_rx` filter | 8 | phy_procedures_nr_gNB.c | Only capture from slot 8 |
| Symbol index | 12 | phy_procedures_nr_gNB.c | 12th symbol of the slot |
| Policy update period | 128 frames | gNB_scheduler.c | ~1.28s between updates |
| `MAX_BWP_SIZE` | 275 | nr_common.h | Maximum PRBs supported |
**Exported functions (SM entry points)**
- `create_spectrum_sm_model()` : Build and return the `e3_c_service_model_desc_t` descriptor for registration with the E3 agent. Encodes RAN function metadata (ASN.1 or JSON) via the SM encoder.
- `spectrum_sm_set_handle()` : Provide the SM with the `e3_service_model_handle_t` used to emit indications and acknowledgements.
---
## 4. xApp-dApp Interaction (E2-E3 Bridge)
When the E3 Agent is compiled with `E2_AGENT` support, the Spectrum SM participates in a closed loop between an external xApp (via E2) and the local dApp (via E3). Two additional ASN.1 structures support this interaction.
### 4.1 xApp → dApp Control (`Spectrum-PRBBlockedControl`)
The xApp sends a `Spectrum-PRBBlockedControl` message to instruct the dApp about which PRBs should be blocked. This message travels through the E2 interface into the E3 Agent, which forwards it to the connected dApp.
```asn1
Spectrum-PRBBlockedControl ::= SEQUENCE {
blockedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex)
}
```
**Flow**:
```
┌──────────┐ ┌──────────┐ ┌─────────────────────────┐ ┌──────────┐
│ xApp │ → │ E2 Agent │ → │ E3 Agent │ → │ dApp │
│ │ │ │ │ (direct E2→E3 handoff) │ │ │
└──────────┘ └──────────┘ └─────────────────────────┘ └──────────┘
```
- The E2 Agent delivers xApp control actions to the E3 Agent using the E3 library APIs.
- The E3 Agent registers an internal dApp-report handler (`e2_e3_bridge()`) using `e3_agent_set_dapp_report_handler()` which is invoked when a dApp report arrives.
- `e2_e3_bridge()` forwards the received report to the E2-side translation function `generate_e2_indication_from_e3_dapp_report(ran_function_id, dapp_id, report_size, report_data)`, which emits the E2 indication to subscribed xApps.
### 4.2 dApp → xApp Report (`Spectrum-PRBBlacklistReport`)
The dApp sends a `Spectrum-PRBBlacklistReport` message back to the xApp to communicate the current PRB blacklist state. This message arrives at the E3 Agent as a `E3AP_PDU_TYPE_DAPP_REPORT` and is forwarded to the E2 interface.
```asn1
Spectrum-PRBBlacklistReport ::= SEQUENCE {
blacklistedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex)
}
```
**Flow**:
```
┌──────────┐ ┌──────────────────────────────────────┐ ┌──────────┐ ┌──────────┐
│ dApp │ → │ E3 Agent (dApp-report handler) │ → │ E2 Agent │ → │ xApp │
│ │ │ generate_e2_indication_from_e3_dapp │ │ │ │ │
│ │ │ report() │ │ │ │ │
└──────────┘ └──────────────────────────────────────┘ └──────────┘ └──────────┘
```
- The E3 Agent receives the dApp report via the registered report handler (`e3_agent.c`) and forwards it to the E2 translation function.
- When `E2_AGENT` is enabled, `generate_e2_indication_from_e3_dapp_report()` translates the E3 dApp report into an E2 indication which the E2 Agent delivers to subscribed xApps.
### 4.3 Complete xApp-dApp Closed Loop
```
┌──────────┐ ┌──────────┐
│ │ ──► Spectrum-PRBBlockedControl ── E2 Agent ──► E3 ──►│ │
│ xApp │ │ dApp │
│ │ ◄── Spectrum-PRBBlacklistReport ── E2 Agent ◄── E3 ◄──│ │
└──────────┘ └──────────┘
│ │
│ E2 Control │ E3 Control
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ gNB (RAN) │
└─────────────────────────────────────────────────────────────────────────────────┘
```
Both the xApp and the dApp can independently issue control actions to the RAN. The xApp sends controls via the E2 interface, while the dApp sends controls via the E3 interface. The bidirectional xApp-dApp channel (`Spectrum-PRBBlockedControl` / `Spectrum-PRBBlacklistReport`) allows them to exchange PRB-level information, enabling coordinated decision-making before either entity acts on the RAN.
---
## 5. Files Reference
RAN
| File | Purpose |
|------|---------|
| `openair1/SCHED_NR/phy_procedures_nr_gNB.c` | IQ sample collection point |
| `openair2/E3AP/service_models/spectrum_sm/spectrum_sm.c` | SM main logic, T-tracer interface |
| `openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h` | Data structures, function declarations |
| `openair2/E3AP/service_models/spectrum_sm/spectrum_enc.c` | Indication message encoding |
| `openair2/E3AP/service_models/spectrum_sm/spectrum_dec.c` | Control message decoding |
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c` | Policy application in scheduler |
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c` | `nr_update_prb_policy()` implementation |
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c` | Slot 8 TDA reservation for sensing |
xApp-dApp Bridge
| File | Purpose |
|------|---------|
| `openair2/E3AP/e3_agent.h` | E3 agent global state and initialization (`e3_init()`, `e3_destroy()`); E3 agent registers a dApp-report handler in `e3_init()`.
| `openair2/E3AP/e3_agent.c` | Implements the E3 agent lifecycle; installs `e2_e3_bridge()` as the dApp-report handler and forwards reports to the E2 translation function.
| `openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_extern.h` | Declares `generate_e2_indication_from_e3_dapp_report()` used to translate E3 dApp reports into E2 indications.
---
## Key functions
- `spectrum_encode_indication()` — encode raw IQ/buffer data into the SM indication payload (used in the worker loop to prepare dApp indications).
- `spectrum_decode_prb_control()` — decode incoming PRB control messages from dApps into `spectrum_prb_control_t` (used by the SM control handler).
- `spectrum_encode_ran_function_data()` — encode the RAN function metadata (ASN.1 or JSON) returned by `create_spectrum_sm_model()`.
- `spectrum_free_decoded_control()` — free the decoded control payload returned by `spectrum_decode_prb_control()`.
- `spectrum_sm_process_control()` — SM control handler invoked by the E3 framework to apply incoming control messages.
- `create_spectrum_sm_model()` and `spectrum_sm_set_handle()` — SM registration and handle plumbing functions.

View File

@@ -0,0 +1,130 @@
#include "spectrum_dec.h"
#include <stdlib.h>
#include <string.h>
#include "common/utils/LOG/log.h"
#ifdef SPECTRUM_SM_ASN1_FORMAT
#include "Spectrum-PRBBlacklistControl.h"
#else
#include <json-c/json.h>
#endif
spectrum_prb_control_t* spectrum_decode_prb_control(uint8_t *encoded_data, size_t encoded_size) {
if (!encoded_data || encoded_size == 0) {
LOG_E(E3AP, "[SPECTRUM] tried to decode an empty buffer\n");
return NULL;
}
spectrum_prb_control_t *prb_control;
#ifdef SPECTRUM_SM_ASN1_FORMAT
// ASN.1 decoding implementation
// Decode ASN.1 structure
Spectrum_PRBBlacklistControl_t *asn_control = NULL;
asn_dec_rval_t dec_ret = aper_decode(0, &asn_DEF_Spectrum_PRBBlacklistControl,
(void **)&asn_control, encoded_data, encoded_size, 0, 0);
if (dec_ret.code != RC_OK || !asn_control) {
LOG_E(E3AP, "Failed to decode PRB blacklist control\n");
if (asn_control) {
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
}
return NULL;
}
// LOG_E(E3AP, "----------------- ASN1 DECODER PRINT START----------------- \n");
// xer_fprint(stdout, &asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
// LOG_E(E3AP, "----------------- ASN1 DECODER PRINT END ----------------- \n");
// Allocate decoded control structure
prb_control = malloc(sizeof(spectrum_prb_control_t));
if (!prb_control) {
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
LOG_E(E3AP, "Failed to malloc structure\n");
return NULL;
}
prb_control->blacklisted_prbs = NULL;
prb_control->prb_count = 0;
const int n = asn_control->blacklistedPRBs.list.count;
if (n < 0 || n > 273) {
LOG_E(E3AP, "Invalid PRB list count: %d\n", n);
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
free(prb_control);
return NULL;
}
prb_control->prb_count = (uint32_t)n;
if (prb_control->prb_count > 0) {
prb_control->blacklisted_prbs = malloc((size_t)prb_control->prb_count * sizeof(uint16_t));
if (!prb_control->blacklisted_prbs) {
LOG_E(E3AP, "Failed to malloc PRB list\n");
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
free(prb_control);
return NULL;
}
for (int i = 0; i < n; ++i) {
if (asn_control->blacklistedPRBs.list.array[i] == NULL) {
prb_control->blacklisted_prbs[i] = 0;
continue;
}
long v = *(asn_control->blacklistedPRBs.list.array[i]);
if (v < 0 || v > 272) {
LOG_E(E3AP, "Invalid PRB value at index %d: %ld\n", i, v);
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
free(prb_control->blacklisted_prbs);
free(prb_control);
return NULL;
}
prb_control->blacklisted_prbs[i] = (uint16_t)v;
}
}
// Extract optional sampling threshold
prb_control->sampling_threshold = 0;
if (asn_control->samplingThreshold) {
prb_control->sampling_threshold = *(asn_control->samplingThreshold);
}
// Extract optional validity period
prb_control->validity_period = 0;
if (asn_control->validityPeriod) {
prb_control->validity_period = *(asn_control->validityPeriod);
}
// Cleanup ASN.1 structure
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
LOG_D(E3AP, "Decoded PRB blacklist control: %u PRBs, sampling threshold %u, validity %u seconds\n",
prb_control->prb_count, prb_control->sampling_threshold, prb_control->validity_period);
return prb_control;
#else
// JSON decoding implementation TBD
return NULL;
#endif
}
void spectrum_free_decoded_control(spectrum_prb_control_t *prb_control) {
if (!prb_control) {
return;
}
// Free the dynamically allocated blacklisted_prbs array
if (prb_control->blacklisted_prbs) {
free(prb_control->blacklisted_prbs);
prb_control->blacklisted_prbs = NULL;
}
// Free the structure itself
free(prb_control);
}

View File

@@ -0,0 +1,43 @@
#ifndef SPECTRUM_DEC_H
#define SPECTRUM_DEC_H
#include <stdint.h>
#include <stddef.h>
/**
* Spectrum SM Decoding Functions
*
* Provides decoding for spectrum control messages
* with compile-time format selection (ASN.1 or JSON)
*/
// Forward declarations for decoded structures
typedef struct spectrum_prb_control_s spectrum_prb_control_t;
/**
* Decoded PRB blacklist control structure
*/
typedef struct spectrum_prb_control_s {
uint16_t *blacklisted_prbs;
uint32_t prb_count;
uint32_t sampling_threshold; // 0 if not specified
uint32_t validity_period; // 0 if not specified
} spectrum_prb_control_t;
/**
* Decode PRB blacklist control specifically
*
* @param encoded_data Input encoded data
* @param encoded_size Size of encoded data
* @return spectrum_decode_prb_control on success, NULL on error
*/
spectrum_prb_control_t* spectrum_decode_prb_control(uint8_t *encoded_data, size_t encoded_size);
/**
* Free a decoded PRB control structure
*
* @param prb_control Pointer to the structure to free
*/
void spectrum_free_decoded_control(spectrum_prb_control_t *prb_control);
#endif // SPECTRUM_DEC_H

View File

@@ -0,0 +1,291 @@
#include "spectrum_enc.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <libe3/error_codes.h>
#include "common/utils/LOG/log.h"
#define ENCODE_BUFFER_SIZE 32768 // 32KB - allows 16KB IQ data + ASN.1 overhead
#define RAN_FUNCTION_BUFFER_SIZE 512
#define SPECTRUM_RAN_FUNCTION_NAME "spectrum_sm"
#define SPECTRUM_RAN_FUNCTION_DESCRIPTION "Spectrum service model for IQ indication and PRB blacklist control"
#define SPECTRUM_RAN_FUNCTION_VERSION 1
#ifdef SPECTRUM_SM_ASN1_FORMAT
/**
* ASN.1 encoding implementation
*/
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
uint8_t **encoded_data, size_t *encoded_size) {
LOG_D(E3AP, "[SPECTRUM_ENC] Starting encoding: iq_size=%zu, timestamp=%u\n", iq_size, timestamp);
if (!iq_data || iq_size == 0 || !encoded_data || !encoded_size) {
LOG_E(E3AP, "[SPECTRUM_ENC] Invalid parameters: iq_data=%p, iq_size=%zu, encoded_data=%p, encoded_size=%p\n",
iq_data, iq_size, encoded_data, encoded_size);
return E3_SM_ERROR_INVALID_PARAM;
}
// Create ASN.1 structure
Spectrum_IQDataIndication_t *indication = calloc(1, sizeof(Spectrum_IQDataIndication_t));
if (!indication) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate indication structure\n");
return E3_SM_ERROR_MEMORY;
}
LOG_D(E3AP, "[SPECTRUM_ENC] Allocated indication structure at %p\n", indication);
// Set IQ samples
indication->iqSamples.buf = malloc(iq_size);
if (!indication->iqSamples.buf) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate IQ buffer of size %zu\n", iq_size);
free(indication);
return E3_SM_ERROR_MEMORY;
}
memcpy(indication->iqSamples.buf, iq_data, iq_size);
indication->iqSamples.size = iq_size;
LOG_D(E3AP, "[SPECTRUM_ENC] Set IQ samples: buf=%p, size=%zu\n",
indication->iqSamples.buf, indication->iqSamples.size);
// Set sample count (assuming int32_t samples)
indication->sampleCount = iq_size / sizeof(int32_t);
LOG_D(E3AP, "[SPECTRUM_ENC] Sample count: %ld (iq_size=%zu / sizeof(int32_t)=%zu)\n",
indication->sampleCount, iq_size, sizeof(int32_t));
// Set optional timestamp
if (timestamp > 0) {
indication->timestamp = malloc(sizeof(long));
if (indication->timestamp) {
*(indication->timestamp) = (long)timestamp;
LOG_D(E3AP, "[SPECTRUM_ENC] Set timestamp: %ld\n", *(indication->timestamp));
} else {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate timestamp\n");
return E3_SM_ERROR_MEMORY;
}
} else {
LOG_D(E3AP, "[SPECTRUM_ENC] No timestamp set (timestamp=0)\n");
}
// Validate structure before encoding
LOG_D(E3AP, "[SPECTRUM_ENC] Pre-encode validation:\n");
LOG_D(E3AP, "[SPECTRUM_ENC] - iqSamples.buf: %p\n", indication->iqSamples.buf);
LOG_D(E3AP, "[SPECTRUM_ENC] - iqSamples.size: %zu\n", indication->iqSamples.size);
LOG_D(E3AP, "[SPECTRUM_ENC] - sampleCount: %ld\n", indication->sampleCount);
LOG_D(E3AP, "[SPECTRUM_ENC] - timestamp: %p\n", indication->timestamp);
// Allocate buffer for encoding
uint8_t *buffer = malloc(ENCODE_BUFFER_SIZE);
if (!buffer) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate encoding buffer of size %d\n", ENCODE_BUFFER_SIZE);
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
return E3_SM_ERROR_MEMORY;
}
LOG_D(E3AP, "[SPECTRUM_ENC] Allocated encoding buffer: %p, size: %d\n", buffer, ENCODE_BUFFER_SIZE);
// Encode to buffer
LOG_D(E3AP, "[SPECTRUM_ENC] Calling aper_encode_to_buffer...\n");
asn_enc_rval_t enc_ret = aper_encode_to_buffer(&asn_DEF_Spectrum_IQDataIndication,
NULL, indication, buffer, ENCODE_BUFFER_SIZE);
LOG_D(E3AP, "[SPECTRUM_ENC] Encoding result: encoded=%ld, failed_type=%p, structure_ptr=%p\n",
enc_ret.encoded, enc_ret.failed_type, enc_ret.structure_ptr);
if (enc_ret.encoded == -1) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to encode Spectrum IQ indication\n");
if (enc_ret.failed_type) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed type: %s\n", enc_ret.failed_type->name);
}
if (enc_ret.structure_ptr) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed at structure: %p\n", enc_ret.structure_ptr);
}
LOG_E(E3AP, "[SPECTRUM_ENC] Data size may exceed buffer (iq_size=%zu, buffer=%d)\n",
iq_size, ENCODE_BUFFER_SIZE);
free(buffer);
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
return E3_SM_ERROR_MEMORY;
}
*encoded_size = (enc_ret.encoded + 7) / 8; // Convert bits to bytes
LOG_D(E3AP, "[SPECTRUM_ENC] Encoded bits: %ld, bytes: %zu\n", enc_ret.encoded, *encoded_size);
*encoded_data = malloc(*encoded_size);
if (!(*encoded_data)) {
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate output buffer of size %zu\n", *encoded_size);
free(buffer);
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
return E3_SM_ERROR_INVALID_PARAM;
}
memcpy(*encoded_data, buffer, *encoded_size);
LOG_D(E3AP, "[SPECTRUM_ENC] Copied %zu bytes to output buffer at %p\n", *encoded_size, *encoded_data);
free(buffer);
// Cleanup
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
LOG_D(E3AP, "[SPECTRUM_ENC] Successfully encoded spectrum indication: %zu bytes\n", *encoded_size);
return E3_SUCCESS;
}
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size)
{
if (!encoded_data || !encoded_size) {
return E3_SM_ERROR_INVALID_PARAM;
}
*encoded_data = NULL;
*encoded_size = 0;
Spectrum_RanFunctionData_t rf_data;
memset(&rf_data, 0, sizeof(rf_data));
const size_t name_len = strlen(SPECTRUM_RAN_FUNCTION_NAME);
const size_t desc_len = strlen(SPECTRUM_RAN_FUNCTION_DESCRIPTION);
rf_data.name.buf = malloc(name_len);
rf_data.description.buf = malloc(desc_len);
if (!rf_data.name.buf || !rf_data.description.buf) {
free(rf_data.name.buf);
free(rf_data.description.buf);
return E3_SM_ERROR_MEMORY;
}
memcpy(rf_data.name.buf, SPECTRUM_RAN_FUNCTION_NAME, name_len);
rf_data.name.size = name_len;
rf_data.version = SPECTRUM_RAN_FUNCTION_VERSION;
memcpy(rf_data.description.buf, SPECTRUM_RAN_FUNCTION_DESCRIPTION, desc_len);
rf_data.description.size = desc_len;
uint8_t buffer[RAN_FUNCTION_BUFFER_SIZE];
asn_enc_rval_t enc_ret = aper_encode_to_buffer(&asn_DEF_Spectrum_RanFunctionData,
NULL,
&rf_data,
buffer,
sizeof(buffer));
free(rf_data.name.buf);
free(rf_data.description.buf);
if (enc_ret.encoded == -1) {
return E3_ENCODE_FAILED;
}
*encoded_size = (enc_ret.encoded + 7) / 8;
*encoded_data = malloc(*encoded_size);
if (!(*encoded_data)) {
*encoded_size = 0;
return E3_SM_ERROR_MEMORY;
}
memcpy(*encoded_data, buffer, *encoded_size);
return E3_SUCCESS;
}
#else
/**
* JSON encoding implementation
*/
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
uint8_t **encoded_data, size_t *encoded_size) {
if (!iq_data || iq_size == 0 || !encoded_data || !encoded_size) {
return E3_SM_ERROR_INVALID_PARAM;
}
// Create JSON object
json_object *indication = json_object_new_object();
if (!indication) {
return E3_SM_ERROR_MEMORY;
}
// Convert IQ data to base64 or hex string for JSON
// For simplicity, we'll use hex encoding
size_t hex_size = iq_size * 2 + 1;
char *hex_string = malloc(hex_size);
if (!hex_string) {
json_object_put(indication);
return E3_SM_ERROR_MEMORY;
}
uint8_t *data_bytes = (uint8_t *)iq_data;
for (size_t i = 0; i < iq_size; i++) {
sprintf(hex_string + (i * 2), "%02x", data_bytes[i]);
}
hex_string[hex_size - 1] = '\0';
// Add fields to JSON
json_object_object_add(indication, "iqSamples", json_object_new_string(hex_string));
json_object_object_add(indication, "sampleCount",
json_object_new_int64(iq_size / sizeof(int32_t)));
if (timestamp > 0) {
json_object_object_add(indication, "timestamp", json_object_new_int64(timestamp));
}
// Convert JSON to string
const char *json_string = json_object_to_json_string(indication);
if (!json_string) {
free(hex_string);
json_object_put(indication);
return E3_SM_ERROR_INVALID_PARAM;
}
// Allocate output buffer
*encoded_size = strlen(json_string);
*encoded_data = malloc(*encoded_size);
if (!*encoded_data) {
free(hex_string);
json_object_put(indication);
return E3_SM_ERROR_MEMORY;
}
memcpy(*encoded_data, json_string, *encoded_size);
// Cleanup
free(hex_string);
json_object_put(indication);
LOG_D(E3AP, "Encoded spectrum indication (JSON): %zu bytes\n", *encoded_size);
return E3_SUCCESS;
}
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size)
{
if (!encoded_data || !encoded_size) {
return E3_SM_ERROR_INVALID_PARAM;
}
*encoded_data = NULL;
*encoded_size = 0;
json_object *rf_data = json_object_new_object();
if (!rf_data) {
return E3_SM_ERROR_MEMORY;
}
json_object_object_add(rf_data, "name", json_object_new_string(SPECTRUM_RAN_FUNCTION_NAME));
json_object_object_add(rf_data, "version", json_object_new_int(SPECTRUM_RAN_FUNCTION_VERSION));
json_object_object_add(rf_data, "description",
json_object_new_string(SPECTRUM_RAN_FUNCTION_DESCRIPTION));
const char *json_string = json_object_to_json_string(rf_data);
if (!json_string) {
json_object_put(rf_data);
return E3_ENCODE_FAILED;
}
*encoded_size = strlen(json_string);
*encoded_data = malloc(*encoded_size);
if (!(*encoded_data)) {
json_object_put(rf_data);
*encoded_size = 0;
return E3_SM_ERROR_MEMORY;
}
memcpy(*encoded_data, json_string, *encoded_size);
json_object_put(rf_data);
return E3_SUCCESS;
}
#endif

View File

@@ -0,0 +1,46 @@
#ifndef SPECTRUM_ENC_H
#define SPECTRUM_ENC_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/**
* Spectrum SM Encoding Functions
*
* Provides encoding for spectrum indication and control messages
* with compile-time format selection (ASN.1 or JSON)
*/
// Compile-time format selection
#ifdef SPECTRUM_SM_ASN1_FORMAT
#include "Spectrum-IQDataIndication.h"
#include "Spectrum-PRBBlacklistControl.h"
#include "Spectrum-ConfigControl.h"
#include "Spectrum-RanFunctionData.h"
#endif
#ifdef SPECTRUM_SM_JSON_FORMAT
#include <json-c/json.h>
#endif
/**
* Encode spectrum IQ data indication message
*
* @param iq_data Raw IQ data buffer
* @param iq_size Size of IQ data
* @param timestamp Timestamp when data was captured
* @param encoded_data Output buffer (allocated by function)
* @param encoded_size Output size
* @return 0 on success, negative on error
*/
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
uint8_t **encoded_data, size_t *encoded_size);
/**
* Encode Spectrum RAN Function data for setup/registration.
*/
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size);
#endif // SPECTRUM_ENC_H

View File

@@ -0,0 +1,607 @@
#include "spectrum_sm.h"
#include "spectrum_enc.h"
#include "spectrum_dec.h"
#include "../../e3_agent.h"
#include "database.h"
#include "event.h"
#include "handler.h"
#include "utils.h"
#include "event_selector.h"
#include "configuration.h"
#include "logger/logger.h"
#include "common/utils/LOG/log.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
// T-tracer constants
#define DEFAULT_REMOTE_IP "127.0.0.1"
#define DEFAULT_REMOTE_PORT 2021
// SM default constants
#define DEFAULT_SM_SAMPLING_THRESHOLD 5
e3_sm_spectrum_control_t *e3_sm_spectrum_control = NULL;
static spectrum_sm_context_t spectrum_ctx = {0};
static uint8_t *spectrum_ran_function_data = NULL;
static size_t spectrum_ran_function_data_len = 0;
static int spectrum_ran_function_data_ready = 0;
// Spectrum SM RAN function IDs
#define SPECTRUM_SM_RAN_FUNCTION_ID 1
static uint32_t spectrum_telemetry_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
static uint32_t spectrum_control_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
/* this macro looks for a particular element and checks its type */
#define GET_DATA_FROM_TRACER(var_name, var_type, var) \
if (!strcmp(f.name[i], var_name)) { \
if (strcmp(f.type[i], var_type)) { \
LOG_E(E3AP, "bad type for %s\n", var_name); \
return NULL; \
} \
var = i; \
continue; \
}
static e3_error_t spectrum_sm_init(void *sm_context);
static void spectrum_sm_destroy(void *sm_context);
static e3_error_t spectrum_sm_start(void *sm_context);
static void spectrum_sm_stop(void *sm_context);
static int spectrum_sm_is_running(void *sm_context);
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
void* sm_context,
uint32_t request_message_id,
uint32_t dapp_id,
uint32_t ran_function_id,
uint32_t control_id,
const uint8_t* data,
size_t data_len);
static e3_c_service_model_desc_t spectrum_sm_desc = {
.name = "spectrum_sm",
.version = 1,
.ran_function_id = SPECTRUM_SM_RAN_FUNCTION_ID,
.telemetry_ids = spectrum_telemetry_ids,
.telemetry_ids_len = sizeof(spectrum_telemetry_ids) / sizeof(spectrum_telemetry_ids[0]),
.control_ids = spectrum_control_ids,
.control_ids_len = sizeof(spectrum_control_ids) / sizeof(spectrum_control_ids[0]),
.ran_function_data = NULL,
.ran_function_data_len = 0,
.sm_init = spectrum_sm_init,
.sm_destroy = spectrum_sm_destroy,
.sm_start = spectrum_sm_start,
.sm_stop = spectrum_sm_stop,
.sm_is_running = spectrum_sm_is_running,
.sm_process_control = spectrum_sm_process_control,
.sm_context = &spectrum_ctx,
};
static int is_running_locked(const spectrum_sm_context_t *ctx)
{
return ctx->running ? 1 : 0;
}
void spectrum_sm_set_handle(e3_service_model_handle_t *sm_handle)
{
pthread_mutex_lock(&spectrum_ctx.lock);
spectrum_ctx.sm_handle = sm_handle;
pthread_mutex_unlock(&spectrum_ctx.lock);
}
e3_c_service_model_desc_t *create_spectrum_sm_model(void)
{
if (!spectrum_ran_function_data_ready) {
#ifdef SPECTRUM_SM_ASN1_FORMAT
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with ASN.1 encoder\n");
#else
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with JSON encoder\n");
#endif
int rc = spectrum_encode_ran_function_data(&spectrum_ran_function_data,
&spectrum_ran_function_data_len);
if (rc != E3_SUCCESS) {
LOG_E(E3AP, "[SPECTRUM] Failed to encode RAN function data (%d)\n", rc);
spectrum_ran_function_data = NULL;
spectrum_ran_function_data_len = 0;
}
spectrum_ran_function_data_ready = 1;
}
spectrum_sm_desc.ran_function_data = spectrum_ran_function_data;
spectrum_sm_desc.ran_function_data_len = spectrum_ran_function_data_len;
return &spectrum_sm_desc;
}
static void activate_traces(int socket, int number_of_events, int *is_on)
{
char t = 1;
if (socket_send(socket, &t, 1) == -1 ||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1) {
LOG_E(E3AP, "[SPECTRUM] Failed to activate T-tracer events\n");
return;
}
LOG_I(E3AP, "[SPECTRUM] T-tracer events activated\n");
}
static void *t_tracer_connector_thread_main(void *context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
if (!ctx) {
return NULL;
}
while (1) {
pthread_mutex_lock(&ctx->lock);
int keep_running = ctx->connector_running ? 1 : 0;
int has_socket = (ctx->t_tracer_socket >= 0) ? 1 : 0;
int prepared = (ctx->database != NULL && ctx->event_flags != NULL) ? 1 : 0;
pthread_mutex_unlock(&ctx->lock);
if (!keep_running) {
break;
}
if (!prepared) {
char *database_filename = T_MESSAGES_PATH;
LOG_D(E3AP, "[SPECTRUM] Loading T-tracer database\n");
void *db = parse_database(database_filename);
if (!db) {
LOG_E(E3AP, "[SPECTRUM] Failed to parse T-tracer database\n");
sleep(1);
continue;
}
load_config_file(database_filename);
int n_events = number_of_ids(db);
int *flags = calloc(n_events, sizeof(int));
if (!flags) {
LOG_E(E3AP, "[SPECTRUM] Failed to allocate event flags array\n");
free_database(db);
sleep(1);
continue;
}
pthread_mutex_lock(&ctx->lock);
if (!ctx->database && !ctx->event_flags) {
ctx->database = db;
ctx->number_of_events = n_events;
ctx->event_flags = flags;
} else {
free(flags);
free_database(db);
}
pthread_mutex_unlock(&ctx->lock);
continue;
}
if (has_socket) {
sleep(1);
continue;
}
int s = try_connect_to((char *)DEFAULT_REMOTE_IP, DEFAULT_REMOTE_PORT);
if (s < 0) {
sleep(1);
continue;
}
int should_activate = 0;
pthread_mutex_lock(&ctx->lock);
if (ctx->t_tracer_socket < 0) {
ctx->t_tracer_socket = s;
ctx->tracer_ready = true;
should_activate = 1;
LOG_I(E3AP, "[SPECTRUM] Connected to T-tracer at %s:%d\n",
DEFAULT_REMOTE_IP,
DEFAULT_REMOTE_PORT);
} else {
close(s);
}
pthread_mutex_unlock(&ctx->lock);
if (should_activate) {
/* Keep old init behavior: activate traces right after successful connect. */
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
}
}
return NULL;
}
static int wait_for_t_tracer_ready(spectrum_sm_context_t *ctx)
{
while (1) {
pthread_mutex_lock(&ctx->lock);
int running = is_running_locked(ctx);
int sock = ctx->t_tracer_socket;
int ready = ctx->tracer_ready ? 1 : 0;
pthread_mutex_unlock(&ctx->lock);
if (!running) {
return -1;
}
if (ready && sock >= 0) {
return sock;
}
sleep(1);
}
}
static void *spectrum_sm_thread_main(void *context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
OBUF ebuf = {osize : 0, omaxsize : 0, obuf : NULL};
if (!ctx || !ctx->initialized) {
LOG_E(E3AP, "[SPECTRUM] SM not initialized, exiting worker\n");
return NULL;
}
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
LOG_E(E3AP, "[SPECTRUM] Failed to set SIGPIPE handler\n");
return NULL;
}
if (wait_for_t_tracer_ready(ctx) < 0) {
LOG_E(E3AP, "[SPECTRUM] T-tracer not ready, exiting worker\n");
return NULL;
}
on_off(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL", ctx->event_flags, 1);
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
ctx->iq_data_event_id = event_id_from_name(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL");
database_event_format f = get_format(ctx->database, ctx->iq_data_event_id);
int data_index = -1;
for (int i = 0; i < f.count; i++) {
GET_DATA_FROM_TRACER("rxdata", "buffer", data_index);
}
if (data_index == -1) {
LOG_E(E3AP, "[SPECTRUM] Failed to find rxdata in event format\n");
return NULL;
}
LOG_I(E3AP, "[SPECTRUM] Start main extraction loop\n");
while (1) {
pthread_mutex_lock(&ctx->lock);
int running = is_running_locked(ctx);
e3_service_model_handle_t *sm_handle = ctx->sm_handle;
pthread_mutex_unlock(&ctx->lock);
if (!running) {
LOG_D(E3AP, "[SPECTRUM] Worker loop stopping (running=0)\n");
break;
}
if (ctx->t_tracer_socket < 0 && wait_for_t_tracer_ready(ctx) < 0) {
LOG_E(E3AP, "[SPECTRUM] T-tracer became unavailable, stopping worker loop\n");
break;
}
event e = get_event(ctx->t_tracer_socket, &ebuf, ctx->database);
if (e.type == -1) {
LOG_W(E3AP, "[SPECTRUM] T-tracer connection lost\n");
pthread_mutex_lock(&ctx->lock);
if (ctx->t_tracer_socket >= 0) {
close(ctx->t_tracer_socket);
ctx->t_tracer_socket = -1;
ctx->tracer_ready = false;
}
pthread_mutex_unlock(&ctx->lock);
continue;
}
if (e.type != ctx->iq_data_event_id) {
continue;
}
if (!sm_handle) {
LOG_E(E3AP, "[SPECTRUM] Missing SM handle, cannot emit indication\n");
continue;
}
LOG_D(E3AP,
"[SPECTRUM] IQ event received (sampling handled in PHY producer, threshold=%u)\n",
e3_sm_spectrum_control->sampling_threshold);
uint8_t *encoded_data = NULL;
size_t encoded_size = 0;
LOG_D(E3AP, "[SPECTRUM] Encoding indication from rxdata buffer (%d bytes)\n",
e.e[data_index].bsize);
int rc = spectrum_encode_indication(e.e[data_index].b,
e.e[data_index].bsize,
0,
&encoded_data,
&encoded_size);
if (rc != E3_SUCCESS || !encoded_data) {
LOG_E(E3AP,
"[SPECTRUM] Failed to encode indication (rc=%d, rxdata_size=%d)\n",
rc,
e.e[data_index].bsize);
continue;
}
LOG_D(E3AP, "[SPECTRUM] Encoded indication payload (%zu bytes)\n", encoded_size);
size_t dapp_len = 0;
uint32_t *dapp_ids = e3_agent_get_ran_function_subscribers(e3.agent,
SPECTRUM_SM_RAN_FUNCTION_ID,
&dapp_len);
LOG_D(E3AP, "[SPECTRUM] Found %zu subscribers for ran_function_id=%u\n",
dapp_len,
SPECTRUM_SM_RAN_FUNCTION_ID);
if (dapp_ids) {
if (dapp_len > 0) {
for (size_t i = 0; i < dapp_len; ++i) {
e3_error_t emit_rc =
e3_service_model_emit_indication(sm_handle, dapp_ids[i], SPECTRUM_SM_RAN_FUNCTION_ID, encoded_data, encoded_size);
if (emit_rc != E3_SUCCESS) {
LOG_E(E3AP, "[SPECTRUM] Failed to emit indication for dApp %u (err=%d)\n", dapp_ids[i], emit_rc);
} else {
LOG_D(E3AP, "[SPECTRUM] Emitted indication to dApp %u (%zu bytes)\n", dapp_ids[i], encoded_size);
}
}
} else {
size_t total_subscriptions = e3_agent_subscription_count(e3.agent);
size_t registered_dapp_len = 0;
uint32_t *registered_dapps = e3_agent_get_registered_dapps(e3.agent, &registered_dapp_len);
LOG_E(E3AP,
"[SPECTRUM] No subscribers for ran_function_id=%u "
"(total_subscriptions=%zu, registered_dapps=%zu)\n",
SPECTRUM_SM_RAN_FUNCTION_ID,
total_subscriptions,
registered_dapp_len);
if (registered_dapps) {
e3_agent_free_uint32_array(registered_dapps);
}
}
}
e3_agent_free_uint32_array(dapp_ids);
free(encoded_data);
}
if (ebuf.obuf) {
free(ebuf.obuf);
}
return NULL;
}
static e3_error_t spectrum_sm_init(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return E3_SM_ERROR_INVALID_PARAM;
}
/* Preserve handle set before SM registration; memset below would clear it. */
e3_service_model_handle_t *preset_sm_handle = ctx->sm_handle;
memset(ctx, 0, sizeof(*ctx));
ctx->t_tracer_socket = -1;
ctx->sm_handle = preset_sm_handle;
pthread_mutex_init(&ctx->lock, NULL);
ctx->initialized = true;
LOG_D(E3AP, "[SPECTRUM] SM init complete (preset_sm_handle=%p)\n", (void *)ctx->sm_handle);
ctx->connector_running = true;
if (pthread_create(&ctx->connector_thread, NULL, t_tracer_connector_thread_main, ctx) != 0) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_THREAD_FAILED;
}
ctx->connector_started = true;
e3_sm_spectrum_control = calloc(1, sizeof(*e3_sm_spectrum_control));
if (!e3_sm_spectrum_control) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_MEMORY;
}
e3_sm_spectrum_control->action_list = calloc(MAX_BWP_SIZE, sizeof(uint16_t));
if (!e3_sm_spectrum_control->action_list) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_MEMORY;
}
pthread_mutex_init(&e3_sm_spectrum_control->mutex, NULL);
e3_sm_spectrum_control->sampling_threshold = DEFAULT_SM_SAMPLING_THRESHOLD;
return E3_SUCCESS;
}
static e3_error_t spectrum_sm_start(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx || !ctx->initialized) {
return E3_NOT_INITIALIZED;
}
pthread_mutex_lock(&ctx->lock);
if (ctx->running) {
pthread_mutex_unlock(&ctx->lock);
return E3_SUCCESS;
}
ctx->running = true;
pthread_mutex_unlock(&ctx->lock);
if (pthread_create(&ctx->worker_thread, NULL, spectrum_sm_thread_main, ctx) != 0) {
pthread_mutex_lock(&ctx->lock);
ctx->running = false;
pthread_mutex_unlock(&ctx->lock);
return E3_SM_ERROR_THREAD_FAILED;
}
return E3_SUCCESS;
}
static void spectrum_sm_stop(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return;
}
pthread_mutex_lock(&ctx->lock);
int was_running = ctx->running;
ctx->running = false;
pthread_mutex_unlock(&ctx->lock);
if (was_running) {
pthread_cancel(ctx->worker_thread);
pthread_join(ctx->worker_thread, NULL);
}
if (ctx->connector_started) {
pthread_mutex_lock(&ctx->lock);
ctx->connector_running = false;
pthread_mutex_unlock(&ctx->lock);
pthread_join(ctx->connector_thread, NULL);
ctx->connector_started = false;
}
}
static int spectrum_sm_is_running(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return 0;
}
pthread_mutex_lock(&ctx->lock);
int running = is_running_locked(ctx);
pthread_mutex_unlock(&ctx->lock);
return running;
}
static void spectrum_sm_destroy(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return;
}
spectrum_sm_stop(sm_context);
if (ctx->event_flags) {
free(ctx->event_flags);
ctx->event_flags = NULL;
}
if (ctx->database) {
free_database(ctx->database);
ctx->database = NULL;
}
if (ctx->t_tracer_socket >= 0) {
close(ctx->t_tracer_socket);
ctx->t_tracer_socket = -1;
}
if (e3_sm_spectrum_control) {
if (e3_sm_spectrum_control->action_list) {
free(e3_sm_spectrum_control->action_list);
e3_sm_spectrum_control->action_list = NULL;
}
pthread_mutex_destroy(&e3_sm_spectrum_control->mutex);
free(e3_sm_spectrum_control);
e3_sm_spectrum_control = NULL;
}
if (ctx->initialized) {
pthread_mutex_destroy(&ctx->lock);
}
if (spectrum_ran_function_data) {
free(spectrum_ran_function_data);
spectrum_ran_function_data = NULL;
spectrum_ran_function_data_len = 0;
spectrum_ran_function_data_ready = 0;
spectrum_sm_desc.ran_function_data = NULL;
spectrum_sm_desc.ran_function_data_len = 0;
}
memset(ctx, 0, sizeof(*ctx));
ctx->t_tracer_socket = -1;
}
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
void* sm_context,
uint32_t request_message_id,
uint32_t dapp_id,
uint32_t ran_function_id,
uint32_t control_id,
const uint8_t* data,
size_t data_len)
{
(void)dapp_id;
int response_code = 0; // Positive
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx || !sm_handle || ran_function_id != SPECTRUM_SM_RAN_FUNCTION_ID ||
control_id != SPECTRUM_SM_RAN_FUNCTION_ID) {
if (sm_handle) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
}
return E3_SM_ERROR_INVALID_PARAM;
}
pthread_mutex_lock(&ctx->lock);
ctx->sm_handle = sm_handle;
pthread_mutex_unlock(&ctx->lock);
if (!data || data_len == 0 || !e3_sm_spectrum_control) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SM_ERROR_INVALID_PARAM;
}
spectrum_prb_control_t* control_payload =
spectrum_decode_prb_control((uint8_t *)data, data_len);
if (!control_payload) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SM_ERROR_INVALID_PARAM;
}
const size_t max_elems = MAX_BWP_SIZE;
size_t elems = control_payload->prb_count;
if (elems > max_elems) {
elems = max_elems;
}
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
uint16_t *alist16 = e3_sm_spectrum_control->action_list;
memcpy(alist16, control_payload->blacklisted_prbs, elems * sizeof(uint16_t));
e3_sm_spectrum_control->action_size = (int)elems;
memset(e3_sm_spectrum_control->dyn_prbbl, 0, max_elems * sizeof(uint16_t));
for (size_t j = 0; j < elems; ++j) {
uint16_t prb = alist16[j];
if (prb < MAX_BWP_SIZE) {
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
}
}
memset(alist16, 0, max_elems * sizeof(uint16_t));
if (control_payload->sampling_threshold > 0) {
e3_sm_spectrum_control->sampling_threshold = control_payload->sampling_threshold;
}
e3_sm_spectrum_control->ready = 1;
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
spectrum_free_decoded_control(control_payload);
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SUCCESS;
}

View File

@@ -0,0 +1,52 @@
#ifndef SPECTRUM_SM_H
#define SPECTRUM_SM_H
#include <libe3/c_api.h>
#include "common/utils/nr/nr_common.h"
#include <stdbool.h>
#include <pthread.h>
#include <stdint.h>
#include <stddef.h>
e3_c_service_model_desc_t* create_spectrum_sm_model(void);
void spectrum_sm_set_handle(e3_service_model_handle_t *sm_handle);
/**
* @brief E3 agent control variables
* This struct is responsible of handling all the shared variables to enable intercommunication between the E3 agent and the rest of
* the codebase
*
*/
typedef struct e3_sm_spectrum_control {
uint16_t* action_list;
int action_size;
uint16_t dyn_prbbl[MAX_BWP_SIZE];
int ready;
uint32_t sampling_threshold;
uint32_t sampling_counter;
pthread_mutex_t mutex;
} e3_sm_spectrum_control_t;
extern e3_sm_spectrum_control_t* e3_sm_spectrum_control;
// Spectrum SM specific structures
typedef struct {
int t_tracer_socket;
void *database;
int *event_flags;
int number_of_events;
int iq_data_event_id;
pthread_t connector_thread;
pthread_t worker_thread;
pthread_mutex_t lock;
bool connector_started;
bool connector_running;
bool tracer_ready;
bool running;
bool initialized;
e3_service_model_handle_t *sm_handle;
} spectrum_sm_context_t;
#endif // SPECTRUM_SM_H

View File

@@ -462,10 +462,11 @@ 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 = scs == 1 || scs == 3 ? sf * 2 : sf;
int sl = 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)
@@ -527,14 +528,6 @@ 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

@@ -144,6 +144,11 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
for (int i = 0; i < num_beams; i++) {
uint16_t *vrb_map_UL = cc[CC_id].vrb_map_UL[i];
memcpy(&vrb_map_UL[prev_slot % size * MAX_BWP_SIZE], &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
#ifdef E3_AGENT
// This for downlink PRBs
uint16_t *vrb_map = cc[CC_id].vrb_map[i];
memcpy(vrb_map, &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
#endif // E3_AGENT
}
clear_nr_nfapi_information(gNB, CC_id, frame, slot);
}
@@ -166,6 +171,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
"periodical UE stats deactivated after reaching %d UEs, please check nrMAC_stats.log, or increase MACRLCs.[0].stats_max_ue\n",
gNB->stats_max_ue);
}
#ifdef E3_AGENT
nr_update_prb_policy(module_idP, frame, slot);
#endif // E3_AGENT
}
nr_measgap_scheduling(gNB, frame, slot);

View File

@@ -20,6 +20,10 @@
#include "executables/softmodem-common.h"
#include "../../../nfapi/oai_integration/vendor_ext.h"
#ifdef E3_AGENT
#include <openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h>
#endif // E3_AGENT
////////////////////////////////////////////////////////
/////* DLSCH MAC PDU generation (6.1.2 TS 38.321) */////
////////////////////////////////////////////////////////
@@ -49,6 +53,30 @@ int get_dl_tda(const gNB_MAC_INST *nrmac, int slot)
return 0; // if FDD or not mixed slot in TDD, for now use default TDA
}
#ifdef E3_AGENT
void nr_update_prb_policy(module_id_t module_idP, frame_t frame, sub_frame_t slot)
{
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock);
LOG_W(NR_MAC, "Barred_PRBs ");
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
if (e3_sm_spectrum_control->ready) {
memcpy(gNB->ulprbbl, e3_sm_spectrum_control->dyn_prbbl, MAX_BWP_SIZE * sizeof(uint16_t));
e3_sm_spectrum_control->ready = 0; // Reset ready flag for next production
}
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
for (int i = 0; i < MAX_BWP_SIZE; i++) {
if (gNB->ulprbbl[i] == 0x3FFF)
printf("%d ", i);
}
printf("\n");
}
#endif // E3_AGENT
// Compute and write all MAC CEs and subheaders, and return number of written bytes
int nr_write_ce_dlsch_pdu(module_id_t module_idP,
const NR_UE_sched_ctrl_t *ue_sched_ctl,

View File

@@ -110,6 +110,15 @@ const NR_tda_info_t *get_best_ul_tda(const gNB_MAC_INST *nrmac, int beam, const
int start = check_rb_start;
int len = check_rb_len;
uint16_t tda_mask = SL_to_bitmap(tdas->startSymbolIndex, tdas->nrOfSymbols);
#ifdef E3_AGENT
// Skip TDAs that use symbol 12 in slot 8 (reserved for spectrum sensing)
if (slot == 8 && (tda_mask & (1 << 12))) {
LOG_D(NR_MAC, "%4d.%2d skipping TDA (mask 0x%04x) overlapping spectrum sensing symbol\n", frame, slot, tda_mask);
continue;
}
#endif
get_max_rb_range(vrb_map_UL, nrmac->ulprbbl, tda_mask, &start, &len);
uint64_t s = (uint64_t)tdas->nrOfSymbols * len;
if (s > score) {

View File

@@ -49,6 +49,10 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, s
void nr_mac_update_timers(module_id_t module_id);
#ifdef E3_AGENT
void nr_update_prb_policy(module_id_t module_id, frame_t frame, sub_frame_t slot);
#endif // E3_AGENT
void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, slot_t slot_rxP, NR_Sched_Rsp_t *sched_info);
/* \brief main DL scheduler function. Calls a preprocessor to decide on

View File

@@ -1099,7 +1099,13 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay, i
asn1cSeqAdd(&tda_list->list, tda);
// UL TDA index 1 in case of SRS
if (do_SRS) {
bool need_no_sym12_tda = do_SRS;
#ifdef E3_AGENT
// E3 spectrum sensing reads symbol 12 of slot 8, so a TDA without symbol 12
// must always be available for get_best_ul_tda() to pick in slot 8.
need_no_sym12_tda = true;
#endif
if (need_no_sym12_tda) {
tda = set_TimeDomainResourceAllocation(k2, get_SLIV(0, 12));
asn1cSeqAdd(&tda_list->list, tda);
}
@@ -1153,7 +1159,7 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay, i
for (int i = k2 + 1; i <= N_ul; ++i) {
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 13));
asn1cSeqAdd(&tda_list->list, tda);
if (do_SRS) {
if (need_no_sym12_tda) {
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 12));
asn1cSeqAdd(&tda_list->list, tda);
}

View File

@@ -272,12 +272,6 @@ 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,7 +5,6 @@
/*!
* \brief common APIs for different RF frontend device
*/
#include <pthread.h>
#include <stdio.h>
#include <strings.h>
#include <dlfcn.h>
@@ -127,8 +126,6 @@ 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;
}
@@ -226,15 +223,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
@@ -277,13 +274,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");
else
pthread_mutex_unlock(&ctx->mutex_write);
pthread_mutex_destroy(&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);
}
ctx->initDone = false;
pthread_mutex_unlock(&ctx->mutex_store);
pthread_mutex_destroy(&ctx->mutex_store);
ctx->initDone = false;
}

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

View File

@@ -25,7 +25,6 @@ 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,11 +96,6 @@ 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,7 +15,6 @@ 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,7 +76,6 @@ 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} \
@@ -107,7 +106,6 @@ typedef struct {
int model_id;
double ds_ns;
double speed_mps;
double aoa_deg;
} cirdb_conf_t;
typedef struct {
@@ -150,7 +148,6 @@ 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;
@@ -326,16 +323,13 @@ 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) {
@@ -363,13 +357,12 @@ 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, AoA %.1fdeg\n",
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\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, aoa_deg);
model_id, 'A' + model_id, ds_ns, speed_mps);
}
}
@@ -457,17 +450,14 @@ 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, AoA %.1fdeg\n",
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\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_aoa_deg);
vrtsim_state->cirdb_speed_mps);
}
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;
@@ -517,7 +507,6 @@ 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",
@@ -531,7 +520,6 @@ 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

@@ -0,0 +1,295 @@
Active_gNBs = ( "oai-gnb");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
sa = 1;
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "oai-gnb";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({
mcc = 001;
mnc = 01;
mnc_length = 2;
snssaiList = (
{
sst = 1, sd=0x0;
},
{
sst = 1, sd=0xffffff;
}
);
});
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pusch_AntennaPorts = 2;
pdsch_AntennaPorts_N1 = 1;
maxMIMO_layers = 2;
do_CSIRS = 1;
do_SRS = 0;
do_SINR = 1;
# sib1_tda = 15;
pdcch_ConfigSIB1 = (
{
controlResourceSetZero = 10;
searchSpaceZero = 0;
}
);
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741)
absoluteFrequencySSB = 649920;
dl_frequencyBand = 78;
# this is 3300.24 MHz
dl_absoluteFrequencyPointA = 648728;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
initialDLBWPlocationAndBandwidth = 28875;
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 10;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 151;# testMAC 158; OAI gNB 98
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#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;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6; #6;
nrofDownlinkSlots = 7; #7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2; #2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.30.134.111"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.130.0.20";
GNB_IPV4_ADDRESS_FOR_NGU = "10.130.0.20";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 120;
pucch_TargetSNRx10 = 260;
ul_bler_target_upper=.15;
ul_bler_target_lower=.05;
pusch_FailureThres = 100;
ul_max_mcs = 28;
ulsch_max_frame_inactivity = 1;
#pusch_RSSI_Threshold = -500;
#pucch_RSSI_Threshold = -600;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 180;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 24; # needs to match O-RU configuration
L1_rx_thread_core = 47;
L1_tx_thread_core = 49; # relevant after merge of l1_tx_thread
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 51;
sl_ahead = 5;
##beamforming 1x2 matrix: 1 layer x 2 antennas
bf_weights = [0x00007fff, 0x0000,0x00007fff, 0x0000];
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="info";
f1ap_log_level ="info";
e3ap_log_level ="info";
};
E3Configuration : {
link = "zmq";
transport = "tcp";
};
fhi_72 = {
dpdk_devices = ("0000:a0:00.7", "0000:a0:01.7");
system_core = 53;
io_core = 55;
worker_cores = (57);
du_addr = ("56:89:79:bc:cd:3c", "56:89:79:bc:cd:3c");
# ru_addr = ("b8:ce:f6:5e:6b:4a", "b8:ce:f6:5e:6b:4a");
ru_addr = ("6c:ad:ad:00:04:de", "6c:ad:ad:00:04:de");
mtu = 8000;
file_prefix = "fhi_72";
fh_config = ({
Tadv_cp_dl = 25;
T2a_cp_dl = (285, 429); # (min, max)
T2a_cp_ul = (285, 429); # (min, max)
T2a_up = (71, 428); # (min, max)
Ta3 = (20, 32); # (min, max)
T1a_cp_dl = (285, 470); # (min, max)
T1a_cp_ul = (285, 429); # (min, max)
T1a_up = (96, 196); # (min, max) #96
Ta4 = (0, 75); # (min, max)
ru_config = {
iq_width = 9;
iq_width_prach = 9;
fft_size = 12;
};
prach_config = {
eAxC_offset = 2; # 4 if you want to use 4x4
};
});
};

View File

@@ -0,0 +1,290 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe01;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1 }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts = 1;
pusch_AntennaPorts = 1;
do_CSIRS = 1;
pdcch_ConfigSIB1 = (
{
controlResourceSetZero = 12;
searchSpaceZero = 0;
}
);
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.60 MHz + 81*12*30e-3 MHz = 3329.76
absoluteFrequencySSB = 641280;
# this is 3300.60 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
initialDLBWPlocationAndBandwidth = 28875;
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#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;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -70;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ( { ipv4 = "10.188.63.150";
ipv6 = "192:168:30::17";
active = "yes";
preference = "ipv4";
}
);
NETWORK_INTERFACES :
{
GNB_INTERFACE_NAME_FOR_NG_AMF = "eno1";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.188.63.150/24";
GNB_INTERFACE_NAME_FOR_NGU = "eno1";
GNB_IPV4_ADDRESS_FOR_NGU = "10.188.63.150/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 300;
pucch_TargetSNRx10 = 200;
pusch_FailureThres = 1000;
ulsch_max_frame_inactivity = 0;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 80;
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1;
nb_rx = 1;
att_tx = 0;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 85;
eNB_instances = [0];
##beamforming 1x2 matrix: 1 layer x 2 antennas
bf_weights = [0x00007fff, 0x0000];
# Comment these two lines below for colosseum
clock_src = "internal";
time_source = "internal";
# sdr_addrs = "addr=192.168.40.2"; # colosseum USRP IP
sdr_addrs = "addr=192.168.40.34"; # local setup
}
);
e2_agent = {
near_ric_ip_addr = "127.0.0.1";
sm_dir = "/usr/local/lib/flexric/"
}
E3Configuration : {
link = "zmq";
transport = "ipc";
};
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = "4043";
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
e3ap_log_level ="info";
}

View File

@@ -0,0 +1,281 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe01;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1 }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts = 1;
pusch_AntennaPorts = 1;
do_CSIRS = 1;
pdcch_ConfigSIB1 = (
{
controlResourceSetZero = 12;
searchSpaceZero = 0;
}
);
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.60 MHz + 81*12*30e-3 MHz = 3329.76
absoluteFrequencySSB = 641280;
# this is 3300.60 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
initialDLBWPlocationAndBandwidth = 28875;
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#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;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -70;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ( { ipv4 = "10.188.63.150";
ipv6 = "192:168:30::17";
active = "yes";
preference = "ipv4";
}
);
NETWORK_INTERFACES :
{
GNB_INTERFACE_NAME_FOR_NG_AMF = "eno1";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.188.63.150/24";
GNB_INTERFACE_NAME_FOR_NGU = "eno1";
GNB_IPV4_ADDRESS_FOR_NGU = "10.188.63.150/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 300;
pucch_TargetSNRx10 = 200;
pusch_FailureThres = 1000;
ulsch_max_frame_inactivity = 0;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 80;
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1;
nb_rx = 1;
att_tx = 0;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 60; # max gain is 60 for x410
eNB_instances = [0];
##beamforming 1x2 matrix: 1 layer x 2 antennas
bf_weights = [0x00007fff, 0x0000];
sdr_addrs = "mgmt_addr=192.168.40.18,addr=192.168.40.18,clock_source=internal,time_source=internal"
}
);
E3Configuration : {
link = "zmq";
transport = "ipc";
};
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = "4043";
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
e3ap_log_level ="info";
}

View File

@@ -0,0 +1,295 @@
Active_gNBs = ( "oai-gnb");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
sa = 1;
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "oai-gnb";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({
mcc = 001;
mnc = 01;
mnc_length = 2;
snssaiList = (
{
sst = 1, sd=0x0;
},
{
sst = 1, sd=0xffffff;
}
);
});
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pusch_AntennaPorts = 2;
pdsch_AntennaPorts_N1 = 1;
maxMIMO_layers = 2;
do_CSIRS = 1;
do_SRS = 0;
do_SINR = 1;
# sib1_tda = 15;
pdcch_ConfigSIB1 = (
{
controlResourceSetZero = 10;
searchSpaceZero = 0;
}
);
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741)
absoluteFrequencySSB = 649920;
dl_frequencyBand = 78;
# this is 3300.24 MHz
dl_absoluteFrequencyPointA = 646724;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 273;
#initialDownlinkBWP
#genericParameters
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
initialDLBWPlocationAndBandwidth = 1099;
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 273;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 1099;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 151;# testMAC 158; OAI gNB 98
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#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;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6; #6;
nrofDownlinkSlots = 7; #7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2; #2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.30.134.111"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.130.0.20";
GNB_IPV4_ADDRESS_FOR_NGU = "10.130.0.20";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 120;
pucch_TargetSNRx10 = 260;
ul_bler_target_upper=.15;
ul_bler_target_lower=.05;
pusch_FailureThres = 100;
ul_max_mcs = 28;
ulsch_max_frame_inactivity = 1;
#pusch_RSSI_Threshold = -500;
#pucch_RSSI_Threshold = -600;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 180;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 24; # needs to match O-RU configuration
L1_rx_thread_core = 47;
L1_tx_thread_core = 49; # relevant after merge of l1_tx_thread
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 51;
sl_ahead = 5;
##beamforming 1x2 matrix: 1 layer x 2 antennas
bf_weights = [0x00007fff, 0x0000,0x00007fff, 0x0000];
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="info";
f1ap_log_level ="info";
e3ap_log_level ="info";
};
E3Configuration : {
link = "zmq";
transport = "tcp";
};
fhi_72 = {
dpdk_devices = ("0000:a0:00.7", "0000:a0:01.7");
system_core = 53;
io_core = 55;
worker_cores = (57);
du_addr = ("56:89:79:bc:cd:3c", "56:89:79:bc:cd:3c");
# ru_addr = ("b8:ce:f6:5e:6b:4a", "b8:ce:f6:5e:6b:4a");
ru_addr = ("6c:ad:ad:00:04:de", "6c:ad:ad:00:04:de");
mtu = 8000;
file_prefix = "fhi_72";
fh_config = ({
Tadv_cp_dl = 25;
T2a_cp_dl = (285, 429); # (min, max)
T2a_cp_ul = (285, 429); # (min, max)
T2a_up = (71, 428); # (min, max)
Ta3 = (20, 32); # (min, max)
T1a_cp_dl = (285, 470); # (min, max)
T1a_cp_ul = (285, 429); # (min, max)
T1a_up = (96, 196); # (min, max) #96
Ta4 = (0, 75); # (min, max)
ru_config = {
iq_width = 9;
iq_width_prach = 9;
fft_size = 12;
};
prach_config = {
eAxC_offset = 2; # 4 if you want to use 4x4
};
});
};

View File

@@ -26,7 +26,7 @@ gNBs =
maxMIMO_layers = 2;
do_CSIRS = 1;
do_SRS = 0;
min_rxtxtime = 3;
min_rxtxtime = 4;
servingCellConfigCommon = (
{
@@ -37,20 +37,20 @@ gNBs =
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 28701.12 MHz
# selected SSB frequency = 28656.48 MHz
absoluteFrequencySSB = 2090107;
# selected SSB frequency = 28691.04 MHz
absoluteFrequencySSB = 2090683;
dl_frequencyBand = 257;
# frequency point A = 28606.08 MHz
dl_absoluteFrequencyPointA = 2089267;
# frequency point A = 28653.6 MHz
dl_absoluteFrequencyPointA = 2090059;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 3;
dl_carrierBandwidth = 132;
dl_carrierBandwidth = 66;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 36025;
initialDLBWPlocationAndBandwidth = 17875;
# 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 = 132;
ul_carrierBandwidth = 66;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 36025;
initialULBWPlocationAndBandwidth = 17875;
# 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 = 4;
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
#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 = 4;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 4;
ssb_periodicityServingCell = 1;
# 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 = 1;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 10;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 2;
dl_UL_TransmissionPeriodicity = 3;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
@@ -166,8 +166,8 @@ gNBs =
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.18.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.18.51";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.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 = 250;
pucch_TargetSNRx10 = 200;
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 = 150;
pucch0_dtx_threshold = 100;
pusch_dtx_threshold = -200;
max_ldpc_iterations = 10;
tx_amp_backoff_dB = 18;
L1_rx_thread_core = 37;
L1_tx_thread_core = 36;
L1_rx_thread_core = 10;
L1_tx_thread_core = 11;
phase_compensation = 1;
}
@@ -219,8 +219,8 @@ RUs = (
max_rxgain = 60;
#sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 35;
sl_ahead = 5;
ru_thread_core = 9;
sl_ahead = 10;
tr_preference = "raw_if4p5";
do_precoding = 0;
}
@@ -257,10 +257,10 @@ log_config :
};
fhi_72 = {
dpdk_devices = ("01:01.0","01:01.1");
system_core = 32;
io_core = 33;
worker_cores = (34);
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);
ru_addr = ("E8:C7:4F:1E:C7:5B", "E8:C7:4F:1E:C7:5B");
mtu = 9216;
fh_config = ({

View File

@@ -241,6 +241,7 @@ log_config :
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
e3ap_log_level ="debug";
};
e2_agent = {