Merge branch 'integration_2025_w32' into 'develop'
Integration: `2025.w32` * !3548 NR UE: fix reliable SIB19 reception when ntn-UlSyncValidityDuration-r17 expires * !3559 numelt in place of config_isparamset * !3487 T hacks: add new tracer ant0 and a tool to plot ofdm data from usrp or rfsim (output or input) * !3541 simplify and make more local variables in nr_rx_pdcch() and fix small bugs * !3547 Add a CU-UP load tester and improve GTP performance * !3552 CI: refactor iperf3 UDP analysis function * !3557 Fix PDSCH rate-matching for periodic CSI in UE * !3571 fix time manager tests in the CI * !3344 LDPC BBDEV/AAL rework * !3560 CI: Refactor log collection, collect also (COTS) UE logs See merge request oai/openairinterface5g!3565
This commit is contained in:
+9
-5
@@ -36,6 +36,8 @@ if(NOT DEFINED ENV{CPM_SOURCE_CACHE})
|
||||
endif()
|
||||
include("cmake_targets/CPM.cmake")
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
|
||||
|
||||
##############################
|
||||
### CCache: reduce compilation time
|
||||
##############################
|
||||
@@ -1407,12 +1409,14 @@ add_library(MME_APP ${MME_APP_SRC})
|
||||
target_link_libraries(MME_APP PRIVATE m2ap m3ap)
|
||||
target_link_libraries(MME_APP PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
|
||||
find_package(sctp REQUIRED)
|
||||
set(SCTP_SRC
|
||||
${OPENAIR3_DIR}/SCTP/sctp_common.c
|
||||
${OPENAIR3_DIR}/SCTP/sctp_eNB_task.c
|
||||
${OPENAIR3_DIR}/SCTP/sctp_eNB_itti_messaging.c
|
||||
)
|
||||
add_library(SCTP_CLIENT ${SCTP_SRC})
|
||||
target_link_libraries(SCTP_CLIENT PRIVATE sctp::sctp)
|
||||
target_link_libraries(SCTP_CLIENT PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
|
||||
set(NAS_SRC ${OPENAIR3_DIR}/NAS/)
|
||||
@@ -1711,7 +1715,6 @@ include_directories("${NFAPI_DIR}/pnf_sim/inc")
|
||||
add_library(oai_iqplayer MODULE
|
||||
${OPENAIR_DIR}/radio/iqplayer/iqplayer_lib.c
|
||||
)
|
||||
set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
|
||||
|
||||
#################################
|
||||
# add executables for operation
|
||||
@@ -1758,7 +1761,7 @@ target_link_libraries(lte-softmodem PRIVATE
|
||||
${NAS_UE_LIB} ITTI SIMU shlib_loader
|
||||
-Wl,--end-group z dl)
|
||||
|
||||
target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt sctp)
|
||||
target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt)
|
||||
target_link_libraries(lte-softmodem PRIVATE ${T_LIB})
|
||||
target_link_libraries(lte-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
target_link_libraries(lte-softmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES})
|
||||
@@ -1814,7 +1817,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE
|
||||
${NAS_UE_LIB} ITTI shlib_loader
|
||||
-Wl,--end-group z dl)
|
||||
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt sctp)
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt)
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB})
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES})
|
||||
@@ -1851,7 +1854,7 @@ target_link_libraries(nr-softmodem PRIVATE
|
||||
time_management
|
||||
-Wl,--end-group z dl)
|
||||
|
||||
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt sctp)
|
||||
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt)
|
||||
target_link_libraries(nr-softmodem PRIVATE ${T_LIB})
|
||||
target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
@@ -1888,7 +1891,7 @@ target_link_libraries(nr-cuup PRIVATE
|
||||
CONFIG_LIB ITTI SCTP_CLIENT
|
||||
GTPV1U e1ap f1ap
|
||||
time_management
|
||||
z sctp dl pthread shlib_loader ${T_LIB})
|
||||
z dl pthread shlib_loader ${T_LIB})
|
||||
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)
|
||||
@@ -2182,3 +2185,4 @@ add_subdirectory(openair1)
|
||||
add_subdirectory(openair2)
|
||||
add_subdirectory(openair3)
|
||||
add_subdirectory(radio)
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -216,24 +216,6 @@ pipeline {
|
||||
}
|
||||
stage('Log Collection') {
|
||||
parallel {
|
||||
stage('Log Collection (eNB - Build)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
|
||||
]) {
|
||||
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
|
||||
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
|
||||
|
||||
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
|
||||
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
|
||||
}
|
||||
script {
|
||||
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
|
||||
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Log Collection (eNB - Run)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
|
||||
@@ -222,24 +222,6 @@ pipeline {
|
||||
}
|
||||
stage('Log Collection') {
|
||||
parallel {
|
||||
stage('Log Collection (eNB - Build)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
|
||||
]) {
|
||||
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
|
||||
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
|
||||
|
||||
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
|
||||
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
|
||||
}
|
||||
script {
|
||||
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
|
||||
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Log Collection (eNB - Run)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
|
||||
@@ -210,24 +210,6 @@ pipeline {
|
||||
}
|
||||
stage('Log Collection') {
|
||||
parallel {
|
||||
stage('Log Collection (eNB - Build)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
|
||||
]) {
|
||||
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
|
||||
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
|
||||
|
||||
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
|
||||
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
|
||||
}
|
||||
script {
|
||||
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
|
||||
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Log Collection (eNB - Run)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
|
||||
@@ -227,24 +227,6 @@ pipeline {
|
||||
}
|
||||
stage('Log Collection') {
|
||||
parallel {
|
||||
stage('Log Collection (eNB - Build)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password']
|
||||
]) {
|
||||
echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m'
|
||||
sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}"
|
||||
|
||||
echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m'
|
||||
sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true"
|
||||
}
|
||||
script {
|
||||
if(fileExists("build.log.${env.BUILD_ID}.zip")) {
|
||||
archiveArtifacts "build.log.${env.BUILD_ID}.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Log Collection (eNB - Run)') {
|
||||
steps {
|
||||
withCredentials([
|
||||
|
||||
@@ -5,10 +5,12 @@ idefix:
|
||||
AttachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 wup
|
||||
DetachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 detach
|
||||
NetworkScript: ip a show dev wwan0
|
||||
#Tracing:
|
||||
# Start: 'sudo rm -f ci_qlog/*; nohup sudo -b QLog/QLog -s ci_qlog -f NR5G.cfg &'
|
||||
# Stop: 'sudo killall QLog'
|
||||
# Collect: 'sudo mv ci_qlog/* %%log_dir%%/'
|
||||
IF: wwan0
|
||||
MTU: 1500
|
||||
Trace: True
|
||||
LogStore: /media/usb-drive/ci_qlogs
|
||||
|
||||
up2:
|
||||
Host: up2
|
||||
@@ -46,7 +48,6 @@ adb_ue_1:
|
||||
NetworkScript: adb -s R3CM40LZPHT shell "ip address show | grep rmnet_data0"
|
||||
CmdPrefix: adb -s R3CM40LZPHT shell
|
||||
MTU: 1500
|
||||
LogStore: /media/usb-drive/ci_adb_1-logs
|
||||
adb_ue_2:
|
||||
Host: nano
|
||||
InitScript: /home/oaicicd/ci_ctl_adb.sh initialize 5200c00db4413517
|
||||
@@ -59,7 +60,6 @@ adb_ue_2:
|
||||
NetworkScript: adb -s 5200c00db4413517 shell "ip address show | grep rmnet"
|
||||
CmdPrefix: adb -s 5200c00db4413517 shell
|
||||
MTU: 1500
|
||||
LogStore: /media/usb-drive/ci_adb_2-logs
|
||||
|
||||
oc-cn5g:
|
||||
Host: avra
|
||||
@@ -150,6 +150,10 @@ amarisoft_ue:
|
||||
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz/aw2s-multi-00102-20.cfg &
|
||||
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
|
||||
NetworkScript: ip netns exec ue1 ip a show dev pdn0
|
||||
#Tracing:
|
||||
# Start: '' # nothing to be done
|
||||
# Stop: '' # nothing to be done
|
||||
# Collect: 'cp /tmp/ue.log %%log_dir%%'
|
||||
amarisoft_ue_2x2:
|
||||
Host: amariue
|
||||
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz_2x2/aw2s-multi-00102-2x2-v2.cfg &
|
||||
|
||||
@@ -84,7 +84,7 @@ class Analysis():
|
||||
|
||||
return success,msg
|
||||
|
||||
def analyze_oc_physim(result_junit, details_json):
|
||||
def analyze_oc_physim(result_junit, details_json, logPath):
|
||||
try:
|
||||
tree = ET.parse(result_junit)
|
||||
root = tree.getroot()
|
||||
@@ -124,7 +124,7 @@ class Analysis():
|
||||
output = test.findtext("system-out")
|
||||
output_check = "exceeds the threshold" not in output
|
||||
# collect logs
|
||||
log_dir = f'../cmake_targets/log/{test_exec}'
|
||||
log_dir = f'{logPath}/{test_exec}'
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
with open(f'{log_dir}/{test_name}.log', 'w') as f:
|
||||
f.write(output)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
|
||||
import os
|
||||
from typing import NamedTuple
|
||||
import logging
|
||||
|
||||
class TestCaseCtx(NamedTuple):
|
||||
count: int
|
||||
test_id: int
|
||||
logPath: str
|
||||
|
||||
def Default(logPath):
|
||||
return TestCaseCtx(123, 112233, logPath)
|
||||
def baseFilename(self):
|
||||
# typically, the test ID is of form 001234 (6 digits)
|
||||
return f"{self.logPath}/{self.count}-{self.test_id:06d}"
|
||||
|
||||
def archiveArtifact(cmd, ctx, remote_path):
|
||||
base = os.path.basename(remote_path)
|
||||
local = f"{ctx.baseFilename()}-{base}"
|
||||
logging.info(f"Archive artifact '{local}'")
|
||||
success = cmd.copyin(remote_path, local)
|
||||
if success:
|
||||
cmd.run(f'rm {remote_path}', silent=True)
|
||||
return local if success else None
|
||||
+57
-52
@@ -39,6 +39,7 @@ import constants as CONST
|
||||
import helpreadme as HELP
|
||||
import cls_containerize
|
||||
import cls_cmd
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
IMAGE_REGISTRY_SERVICE_NAME = "image-registry.openshift-image-registry.svc"
|
||||
NAMESPACE = "oaicicd-ran"
|
||||
@@ -207,7 +208,12 @@ class Cluster:
|
||||
HTML.CreateHtmlTestRowQueue(param, 'KO', [msg])
|
||||
return success
|
||||
|
||||
def BuildClusterImage(self, HTML):
|
||||
def _retrieveOCLog(self, ctx, job, lSourcePath, image):
|
||||
fn = f'{lSourcePath}/cmake_targets/log/{image}.log'
|
||||
self.cmd.run(f'oc logs {job} &> {fn}')
|
||||
return (image, archiveArtifact(self.cmd, ctx, fn))
|
||||
|
||||
def BuildClusterImage(self, ctx, HTML):
|
||||
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {ranBranch} ranCommitID {self.ranCommitID}')
|
||||
@@ -280,22 +286,27 @@ class Cluster:
|
||||
self._recreate_entitlements()
|
||||
|
||||
status = True # flag to abandon compiling if any image fails
|
||||
attemptedImages = []
|
||||
log_files = []
|
||||
build_metrics = f"{lSourcePath}/cmake_targets/log/build-metrics.log"
|
||||
if forceBaseImageBuild:
|
||||
self._recreate_is_tag('ran-base', baseTag, 'openshift/ran-base-is.yaml')
|
||||
self._recreate_bc('ran-base', baseTag, 'openshift/ran-base-bc.yaml')
|
||||
ranbase_job = self._start_build('ran-base')
|
||||
attemptedImages += ['ran-base']
|
||||
status = ranbase_job is not None and self._wait_build_end([ranbase_job], 1000)
|
||||
if not status: logging.error('failure during build of ran-base')
|
||||
self.cmd.run(f'oc logs {ranbase_job} &> cmake_targets/log/ran-base.log') # cannot use cmd.run because of redirect
|
||||
log_files.append(self._retrieveOCLog(ctx, ranbase_job, lSourcePath, 'ran-base'))
|
||||
|
||||
# recover logs by mounting image
|
||||
self._retag_image_statement('ran-base', 'ran-base', baseTag, 'openshift/ran-base-log-retrieval.yaml')
|
||||
pod = self._deploy_pod('openshift/ran-base-log-retrieval.yaml')
|
||||
if pod is not None:
|
||||
self.cmd.run(f'mkdir -p cmake_targets/log/ran-base')
|
||||
self.cmd.run(f'oc rsync {pod}:/oai-ran/cmake_targets/log/ cmake_targets/log/ran-base')
|
||||
logdir = f'{lSourcePath}/cmake_targets/log/ran-base'
|
||||
self.cmd.run(f'mkdir -p {logdir}')
|
||||
self.cmd.run(f'oc rsync {pod}:/oai-ran/cmake_targets/log/ {logdir}')
|
||||
self._undeploy_pod('openshift/ran-base-log-retrieval.yaml')
|
||||
ret = self.cmd.run(f'ls {logdir}')
|
||||
for f in ret.stdout.splitlines():
|
||||
archiveArtifact(self.cmd, ctx, f"{logdir}/{f}")
|
||||
else:
|
||||
status = False
|
||||
|
||||
@@ -304,27 +315,24 @@ class Cluster:
|
||||
self._recreate_bc('oai-physim', imageTag, 'openshift/oai-physim-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.phySim.rhel9')
|
||||
physim_job = self._start_build('oai-physim')
|
||||
attemptedImages += ['oai-physim']
|
||||
|
||||
self._recreate_is_tag('ran-build', imageTag, 'openshift/ran-build-is.yaml')
|
||||
self._recreate_bc('ran-build', imageTag, 'openshift/ran-build-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.rhel9')
|
||||
ranbuild_job = self._start_build('ran-build')
|
||||
attemptedImages += ['ran-build']
|
||||
|
||||
self._recreate_is_tag('oai-clang', imageTag, 'openshift/oai-clang-is.yaml')
|
||||
self._recreate_bc('oai-clang', imageTag, 'openshift/oai-clang-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.clang.rhel9')
|
||||
clang_job = self._start_build('oai-clang')
|
||||
attemptedImages += ['oai-clang']
|
||||
|
||||
wait = ranbuild_job is not None and physim_job is not None and clang_job is not None and self._wait_build_end([ranbuild_job, physim_job, clang_job], 1200)
|
||||
if not wait: logging.error('error during build of ranbuild_job or physim_job or clang_job')
|
||||
status = status and wait
|
||||
self.cmd.run(f'oc logs {ranbuild_job} &> cmake_targets/log/ran-build.log')
|
||||
self.cmd.run(f'oc logs {physim_job} &> cmake_targets/log/oai-physim.log')
|
||||
self.cmd.run(f'oc logs {clang_job} &> cmake_targets/log/oai-clang.log')
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
|
||||
log_files.append(self._retrieveOCLog(ctx, ranbuild_job, lSourcePath, 'ran-build'))
|
||||
log_files.append(self._retrieveOCLog(ctx, physim_job, lSourcePath, 'oai-physim'))
|
||||
log_files.append(self._retrieveOCLog(ctx, clang_job, lSourcePath, 'oai-clang'))
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
|
||||
|
||||
if status:
|
||||
self._recreate_is_tag('oai-enb', imageTag, 'openshift/oai-enb-is.yaml')
|
||||
@@ -332,72 +340,66 @@ class Cluster:
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.eNB.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.eNB.rhel9')
|
||||
enb_job = self._start_build('oai-enb')
|
||||
attemptedImages += ['oai-enb']
|
||||
|
||||
self._recreate_is_tag('oai-gnb', imageTag, 'openshift/oai-gnb-is.yaml')
|
||||
self._recreate_bc('oai-gnb', imageTag, 'openshift/oai-gnb-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.rhel9')
|
||||
gnb_job = self._start_build('oai-gnb')
|
||||
attemptedImages += ['oai-gnb']
|
||||
|
||||
self._recreate_is_tag('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-is.yaml')
|
||||
self._recreate_bc('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.aw2s.rhel9')
|
||||
gnb_aw2s_job = self._start_build('oai-gnb-aw2s')
|
||||
attemptedImages += ['oai-gnb-aw2s']
|
||||
|
||||
wait = enb_job is not None and gnb_job is not None and gnb_aw2s_job is not None and self._wait_build_end([enb_job, gnb_job, gnb_aw2s_job], 800)
|
||||
if not wait: logging.error('error during build of eNB/gNB')
|
||||
status = status and wait
|
||||
# recover logs
|
||||
self.cmd.run(f'oc logs {enb_job} &> cmake_targets/log/oai-enb.log')
|
||||
self.cmd.run(f'oc logs {gnb_job} &> cmake_targets/log/oai-gnb.log')
|
||||
self.cmd.run(f'oc logs {gnb_aw2s_job} &> cmake_targets/log/oai-gnb-aw2s.log')
|
||||
log_files.append(self._retrieveOCLog(ctx, enb_job, lSourcePath, 'oai-enb'))
|
||||
log_files.append(self._retrieveOCLog(ctx, gnb_job, lSourcePath, 'oai-gnb'))
|
||||
log_files.append(self._retrieveOCLog(ctx, gnb_aw2s_job, lSourcePath, 'oai-gnb-aw2s'))
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
|
||||
|
||||
self._recreate_is_tag('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-is.yaml')
|
||||
self._recreate_bc('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nr-cuup.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nr-cuup.rhel9')
|
||||
nr_cuup_job = self._start_build('oai-nr-cuup')
|
||||
attemptedImages += ['oai-nr-cuup']
|
||||
|
||||
self._recreate_is_tag('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-is.yaml')
|
||||
self._recreate_bc('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.lteUE.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.lteUE.rhel9')
|
||||
lteue_job = self._start_build('oai-lte-ue')
|
||||
attemptedImages += ['oai-lte-ue']
|
||||
|
||||
self._recreate_is_tag('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-is.yaml')
|
||||
self._recreate_bc('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nrUE.rhel9')
|
||||
self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nrUE.rhel9')
|
||||
nrue_job = self._start_build('oai-nr-ue')
|
||||
attemptedImages += ['oai-nr-ue']
|
||||
|
||||
wait = nr_cuup_job is not None and lteue_job is not None and nrue_job is not None and self._wait_build_end([nr_cuup_job, lteue_job, nrue_job], 800)
|
||||
if not wait: logging.error('error during build of nr-cuup/lteUE/nrUE')
|
||||
status = status and wait
|
||||
# recover logs
|
||||
self.cmd.run(f'oc logs {nr_cuup_job} &> cmake_targets/log/oai-nr-cuup.log')
|
||||
self.cmd.run(f'oc logs {lteue_job} &> cmake_targets/log/oai-lte-ue.log')
|
||||
self.cmd.run(f'oc logs {nrue_job} &> cmake_targets/log/oai-nr-ue.log')
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
|
||||
log_files.append(self._retrieveOCLog(ctx, nr_cuup_job, lSourcePath, 'oai-nr-cuup'))
|
||||
log_files.append(self._retrieveOCLog(ctx, lteue_job, lSourcePath, 'oai-lte-ue'))
|
||||
log_files.append(self._retrieveOCLog(ctx, nrue_job, lSourcePath, 'oai-nr-ue'))
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
|
||||
|
||||
if status:
|
||||
self._recreate_is_tag('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-is.yaml')
|
||||
self._recreate_bc('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-bc.yaml')
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.fhi72.rhel9')
|
||||
ranbuildfhi72_job = self._start_build('ran-build-fhi72')
|
||||
attemptedImages += ['ran-build-fhi72']
|
||||
|
||||
wait = ranbuildfhi72_job is not None and self._wait_build_end([ranbuildfhi72_job], 1200)
|
||||
if not wait: logging.error('error during build of ranbuildfhi72_job')
|
||||
status = status and wait
|
||||
self.cmd.run(f'oc logs {ranbuildfhi72_job} &> cmake_targets/log/ran-build-fhi72.log')
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
|
||||
log_files.append(self._retrieveOCLog(ctx, ranbuildfhi72_job, lSourcePath, 'ran-build-fhi72'))
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
|
||||
|
||||
if status:
|
||||
self._recreate_is_tag('oai-gnb-fhi72', imageTag, 'openshift/oai-gnb-fhi72-is.yaml')
|
||||
@@ -405,19 +407,17 @@ class Cluster:
|
||||
self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.fhi72.rhel9')
|
||||
self._retag_image_statement('ran-build-fhi72', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build-fhi72', imageTag, 'docker/Dockerfile.gNB.fhi72.rhel9')
|
||||
gnb_fhi72_job = self._start_build('oai-gnb-fhi72')
|
||||
attemptedImages += ['oai-gnb-fhi72']
|
||||
|
||||
wait = gnb_fhi72_job is not None and self._wait_build_end([gnb_fhi72_job], 600)
|
||||
if not wait: logging.error('error during build of gNB-fhi72')
|
||||
status = status and wait
|
||||
# recover logs
|
||||
self.cmd.run(f'oc logs {gnb_fhi72_job} &> cmake_targets/log/oai-gnb-fhi72.log')
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log')
|
||||
log_files.append(self._retrieveOCLog(ctx, gnb_fhi72_job, lSourcePath, 'oai-gnb-fhi72'))
|
||||
self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}')
|
||||
|
||||
# split and analyze logs
|
||||
imageSize = {}
|
||||
for image in attemptedImages:
|
||||
self.cmd.run(f'mkdir -p cmake_targets/log/{image}')
|
||||
for image, _ in log_files:
|
||||
tag = imageTag if image != 'ran-base' else baseTag
|
||||
size = self._get_image_size(image, tag)
|
||||
if size <= 0:
|
||||
@@ -428,12 +428,14 @@ class Cluster:
|
||||
imageSize[image] = f'{sizeMb:.1f} Mbytes (uncompressed: ~{sizeMb*2.5:.1f} Mbytes)'
|
||||
logging.info(f'\u001B[1m{image} size is {imageSize[image]}\u001B[0m')
|
||||
|
||||
grep_exp = r"\|".join(attemptedImages)
|
||||
self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> cmake_targets/log/image_registry.log');
|
||||
self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> cmake_targets/log/build_pod_summary.log; done')
|
||||
|
||||
build_log_name = f'build_log_{self.testCase_id}'
|
||||
cls_containerize.CopyLogsToExecutor(self.cmd, lSourcePath, build_log_name)
|
||||
archiveArtifact(self.cmd, ctx, build_metrics)
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/image_registry.log'
|
||||
grep_exp = r"\|".join([i for i,f in log_files])
|
||||
self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> {logfile}');
|
||||
archiveArtifact(self.cmd, ctx, logfile)
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/build_pod_summary.log'
|
||||
self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> {logfile}; done')
|
||||
archiveArtifact(self.cmd, ctx, logfile)
|
||||
|
||||
self.cmd.run('for pod in $(oc get pods | tail -n +2 | awk \'{print $1}\'); do oc delete pod ${pod}; done')
|
||||
|
||||
@@ -442,7 +444,7 @@ class Cluster:
|
||||
self.cmd.close()
|
||||
|
||||
# Analyze the logs
|
||||
collectInfo = cls_containerize.AnalyzeBuildLogs(build_log_name, attemptedImages, status)
|
||||
collectInfo = cls_containerize.AnalyzeBuildLogs(log_files, status)
|
||||
for img in collectInfo:
|
||||
for f in collectInfo[img]:
|
||||
status = status and collectInfo[img][f]['status']
|
||||
@@ -458,9 +460,16 @@ class Cluster:
|
||||
|
||||
HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, imageSize)
|
||||
|
||||
# TODO fix groovy script, remove the following.
|
||||
# the groovy scripts expects all logs in
|
||||
# <jenkins-workspace>/<pipeline>/ci-scripts, so copy it there
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f'mkdir -p {os.getcwd()}/test_log_{ctx.test_id}/')
|
||||
c.run(f'cp -r {ctx.logPath} {os.getcwd()}/test_log_{ctx.test_id}/')
|
||||
|
||||
return status
|
||||
|
||||
def deploy_oc_physim(self, HTML, oc_release, svr_id):
|
||||
def deploy_oc_physim(self, ctx, HTML, oc_release, svr_id):
|
||||
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {self.ranBranch} ranCommitID {self.ranCommitID}')
|
||||
@@ -470,17 +479,13 @@ class Cluster:
|
||||
options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {self.eNBSourceCodePath}"
|
||||
ret = cls_cmd.runScript(svr_id, script, 600, options)
|
||||
logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}')
|
||||
log_dir = f'{os.getcwd()}/../cmake_targets/log'
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
result_junit = f'{oc_release}-run.xml'
|
||||
details_json = f'{oc_release}-tests.json'
|
||||
with cls_cmd.getConnection(svr_id) as ssh:
|
||||
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{details_json}', tgt=f'{log_dir}/{details_json}')
|
||||
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{result_junit}', tgt=f'{log_dir}/{result_junit}')
|
||||
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt', tgt=f'{log_dir}/physim_log.txt')
|
||||
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt', tgt=f'{log_dir}/physim_pods_summary.txt')
|
||||
ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log', tgt=f'{log_dir}/LastTestsFailed.log')
|
||||
test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(f'{log_dir}/{result_junit}', f'{log_dir}/{details_json}')
|
||||
details_json = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-tests.json')
|
||||
result_junit = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-run.xml')
|
||||
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt')
|
||||
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt')
|
||||
archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log')
|
||||
test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(result_junit, details_json, ctx.logPath)
|
||||
if test_summary:
|
||||
if test_status:
|
||||
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
+80
-129
@@ -46,6 +46,7 @@ import cls_cmd
|
||||
import helpreadme as HELP
|
||||
import constants as CONST
|
||||
import cls_oaicitest
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Helper functions used here and in other classes
|
||||
@@ -81,67 +82,26 @@ def CreateTag(ranCommitID, ranBranch, ranAllowMerge):
|
||||
tagToUse = f'develop-{shortCommit}'
|
||||
return tagToUse
|
||||
|
||||
def CopyLogsToExecutor(cmd, sourcePath, log_name):
|
||||
cmd.cd(f'{sourcePath}/cmake_targets')
|
||||
cmd.run(f'rm -f {log_name}.zip')
|
||||
cmd.run(f'mkdir -p {log_name}')
|
||||
cmd.run(f'mv log/* {log_name}')
|
||||
cmd.run(f'zip -r -qq {log_name}.zip {log_name}')
|
||||
|
||||
# copy zip to executor for analysis
|
||||
if (os.path.isfile(f'./{log_name}.zip')):
|
||||
os.remove(f'./{log_name}.zip')
|
||||
if (os.path.isdir(f'./{log_name}')):
|
||||
shutil.rmtree(f'./{log_name}')
|
||||
cmd.copyin(src=f'{sourcePath}/cmake_targets/{log_name}.zip', tgt=f'{os.getcwd()}/{log_name}.zip')
|
||||
cmd.run(f'rm -f {log_name}.zip')
|
||||
ZipFile(f'{log_name}.zip').extractall('.')
|
||||
|
||||
def AnalyzeBuildLogs(buildRoot, images, globalStatus):
|
||||
def AnalyzeBuildLogs(logfiles, globalStatus):
|
||||
collectInfo = {}
|
||||
for image in images:
|
||||
for image, lf in logfiles:
|
||||
files = {}
|
||||
file_list = [f for f in os.listdir(f'{buildRoot}/{image}') if os.path.isfile(os.path.join(f'{buildRoot}/{image}', f)) and f.endswith('.txt')]
|
||||
# Analyze the "sub-logs" of every target image
|
||||
for fil in file_list:
|
||||
errorandwarnings = {}
|
||||
warningsNo = 0
|
||||
errorsNo = 0
|
||||
with open(f'{buildRoot}/{image}/{fil}', mode='r') as inputfile:
|
||||
for line in inputfile:
|
||||
result = re.search(' ERROR ', str(line))
|
||||
if result is not None:
|
||||
errorsNo += 1
|
||||
result = re.search(' error:', str(line))
|
||||
if result is not None:
|
||||
errorsNo += 1
|
||||
result = re.search(' WARNING ', str(line))
|
||||
if result is not None:
|
||||
warningsNo += 1
|
||||
result = re.search(' warning:', str(line))
|
||||
if result is not None:
|
||||
warningsNo += 1
|
||||
errorandwarnings['errors'] = errorsNo
|
||||
errorandwarnings['warnings'] = warningsNo
|
||||
errorandwarnings['status'] = globalStatus
|
||||
files[fil] = errorandwarnings
|
||||
# Analyze the target image
|
||||
if os.path.isfile(f'{buildRoot}/{image}.log'):
|
||||
errorandwarnings = {}
|
||||
committed = False
|
||||
tagged = False
|
||||
with open(f'{buildRoot}/{image}.log', mode='r') as inputfile:
|
||||
for line in inputfile:
|
||||
lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None
|
||||
lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None
|
||||
tagged = tagged or lineHasTag or lineHasTag2
|
||||
# the OpenShift Cluster builder prepends image registry URL
|
||||
lineHasCommit = re.search(f'COMMIT [a-zA-Z0-9\.:/\-]*{image}', str(line)) is not None
|
||||
committed = committed or lineHasCommit
|
||||
errorandwarnings['errors'] = 0 if committed or tagged else 1
|
||||
errorandwarnings['warnings'] = 0
|
||||
errorandwarnings['status'] = committed or tagged
|
||||
files['Target Image Creation'] = errorandwarnings
|
||||
errorandwarnings = {}
|
||||
committed = False
|
||||
tagged = False
|
||||
with open(lf, mode='r') as inputfile:
|
||||
for line in inputfile:
|
||||
lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None
|
||||
lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None
|
||||
tagged = tagged or lineHasTag or lineHasTag2
|
||||
# the OpenShift Cluster builder prepends image registry URL
|
||||
lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None
|
||||
committed = committed or lineHasCommit
|
||||
errorandwarnings['errors'] = 0 if committed or tagged else 1
|
||||
errorandwarnings['warnings'] = 0
|
||||
errorandwarnings['status'] = committed or tagged
|
||||
logging.info(f"Analyzing {image}, file {lf}: {errorandwarnings}")
|
||||
files['Target Image Creation'] = errorandwarnings
|
||||
collectInfo[image] = files
|
||||
return collectInfo
|
||||
|
||||
@@ -202,12 +162,10 @@ def GetServices(ssh, requested, file):
|
||||
else:
|
||||
return requested
|
||||
|
||||
def CopyinServiceLog(ssh, lSourcePath, yaml, svcName, wd_yaml, filename):
|
||||
remote_filename = f"{lSourcePath}/cmake_targets/log/{filename}"
|
||||
def CopyinServiceLog(ssh, lSourcePath, svcName, wd_yaml, ctx):
|
||||
remote_filename = f"{lSourcePath}/cmake_targets/log/{svcName}.logs"
|
||||
ssh.run(f'docker compose -f {wd_yaml} logs {svcName} --no-log-prefix &> {remote_filename}')
|
||||
local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml}"
|
||||
local_filename = f"{local_dir}/{filename}"
|
||||
return ssh.copyin(remote_filename, local_filename)
|
||||
return archiveArtifact(ssh, ctx, remote_filename)
|
||||
|
||||
def GetRunningServices(ssh, file):
|
||||
ret = ssh.run(f'docker compose -f {file} config --services')
|
||||
@@ -229,14 +187,13 @@ def GetRunningServices(ssh, file):
|
||||
logging.info(f'stopping services: {running_services}')
|
||||
return running_services
|
||||
|
||||
def CheckLogs(self, yaml, service_name, HTML, RAN):
|
||||
logPath = f'{os.getcwd()}/../cmake_targets/log/{yaml}'
|
||||
filename = f'{logPath}/{service_name}-{HTML.testCase_id}.log'
|
||||
def CheckLogs(self, filename, HTML, RAN):
|
||||
success = True
|
||||
if (any(sub in service_name for sub in ['oai_ue','oai-nr-ue','lte_ue'])):
|
||||
name = os.path.basename(filename)
|
||||
if (any(sub in name for sub in ['oai_ue','oai-nr-ue','lte_ue'])):
|
||||
logging.debug(f'\u001B[1m Analyzing UE logfile {filename} \u001B[0m')
|
||||
logStatus = cls_oaicitest.OaiCiTest().AnalyzeLogFile_UE(filename, HTML, RAN)
|
||||
opt = f"UE log analysis for service {service_name}"
|
||||
opt = f"UE log analysis ({name})"
|
||||
# usage of htmlUEFailureMsg/htmleNBFailureMsg is because Analyze log files
|
||||
# abuse HTML to store their reports, and we here want to put custom options,
|
||||
# which is not possible with CreateHtmlTestRow
|
||||
@@ -247,13 +204,13 @@ def CheckLogs(self, yaml, service_name, HTML, RAN):
|
||||
else:
|
||||
HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmlUEFailureMsg])
|
||||
HTML.htmlUEFailureMsg = ""
|
||||
elif service_name == 'nv-cubb':
|
||||
elif 'nv-cubb' in name:
|
||||
msg = 'Undeploy PNF/Nvidia CUBB'
|
||||
HTML.CreateHtmlTestRow(msg, 'OK', CONST.ALL_PROCESSES_OK)
|
||||
elif (any(sub in service_name for sub in ['enb','rru','rcc','cu','du','gnb'])):
|
||||
elif (any(sub in name for sub in ['enb','rru','rcc','cu','du','gnb'])):
|
||||
logging.debug(f'\u001B[1m Analyzing XnB logfile {filename}\u001B[0m')
|
||||
logStatus = RAN.AnalyzeLogFile_eNB(filename, HTML, self.ran_checkers)
|
||||
opt = f"xNB log analysis for service {service_name}"
|
||||
opt = f"xNB log analysis ({name})"
|
||||
if (logStatus < 0):
|
||||
HTML.CreateHtmlTestRowQueue(opt, 'KO', [HTML.htmleNBFailureMsg])
|
||||
success = False
|
||||
@@ -261,9 +218,9 @@ def CheckLogs(self, yaml, service_name, HTML, RAN):
|
||||
HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmleNBFailureMsg])
|
||||
HTML.htmleNBFailureMsg = ""
|
||||
else:
|
||||
logging.info(f'Skipping to analyze log for service name {service_name}')
|
||||
HTML.CreateHtmlTestRowQueue(f"service {service_name}", 'OK', ["no analysis function"])
|
||||
logging.debug(f"log check: service {service_name} passed analysis {success}")
|
||||
logging.info(f"Skipping analysis of log '{filename}': no submatch for xNB/UE")
|
||||
HTML.CreateHtmlTestRowQueue(f"file {name}", 'OK', ["no analysis function"])
|
||||
logging.debug(f"log check: file {filename} passed analysis {success}")
|
||||
return success
|
||||
|
||||
#-----------------------------------------------------------
|
||||
@@ -299,8 +256,6 @@ class Containerize():
|
||||
self.services = ['', '', '']
|
||||
self.deploymentTag = ''
|
||||
|
||||
self.testCase_id = ''
|
||||
|
||||
self.cli = ''
|
||||
self.cliBuildOptions = ''
|
||||
self.dockerfileprefix = ''
|
||||
@@ -330,11 +285,12 @@ class Containerize():
|
||||
raise ValueError(f'Insufficient Parameter: IP/node {ip}, path {path}')
|
||||
return (ip, path)
|
||||
|
||||
def BuildImage(self, HTML):
|
||||
def BuildImage(self, ctx, HTML):
|
||||
svr = self.eNB_serverId[self.eNB_instance]
|
||||
lIpAddr, lSourcePath = self.GetCredentials(svr)
|
||||
logging.debug('Building on server: ' + lIpAddr)
|
||||
cmd = cls_cmd.getConnection(lIpAddr)
|
||||
log_files = []
|
||||
|
||||
# Checking the hostname to get adapted on cli and dockerfileprefixes
|
||||
cmd.run('hostnamectl')
|
||||
@@ -390,7 +346,6 @@ class Containerize():
|
||||
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
|
||||
imageNames.append(('oai-gnb-aerial', 'gNB.aerial', 'oai-gnb-aerial', ''))
|
||||
|
||||
self.testCase_id = HTML.testCase_id
|
||||
cmd.cd(lSourcePath)
|
||||
# if asterix, copy the entitlement and subscription manager configurations
|
||||
if self.host == 'Red Hat':
|
||||
@@ -420,13 +375,24 @@ class Containerize():
|
||||
# Let's remove any previous run artifacts if still there
|
||||
cmd.run(f"{self.cli} image prune --force")
|
||||
for image,pattern,name,option in imageNames:
|
||||
cmd.run(f"{self.cli} image rm {name}:{imageTag}")
|
||||
cmd.run(f"{self.cli} image rm {name}:{imageTag}", reportNonZero=False)
|
||||
|
||||
# Build the base image only on Push Events (not on Merge Requests)
|
||||
# On when the base image docker file is being modified.
|
||||
if forceBaseImageBuild:
|
||||
cmd.run(f"{self.cli} image rm {baseImage}:{baseTag}")
|
||||
cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> cmake_targets/log/ran-base.log", timeout=1600)
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/ran-base.docker.log'
|
||||
cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> {logfile}", timeout=1600)
|
||||
t = ("ran-base", archiveArtifact(cmd, ctx, logfile))
|
||||
log_files.append(t)
|
||||
|
||||
# Recover build logs, for the moment only possible when build is successful
|
||||
cmd.run(f"{self.cli} create --name test {baseImage}:{baseTag}")
|
||||
cmd.run("mkdir -p cmake_targets/log/ran-base")
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/ran-base.log'
|
||||
cmd.run(f"{self.cli} cp test:/oai-ran/cmake_targets/log/all.txt {logfile}")
|
||||
cmd.run(f"{self.cli} rm -f test")
|
||||
archiveArtifact(cmd, ctx, logfile)
|
||||
# First verify if the base image was properly created.
|
||||
ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}")
|
||||
allImagesSize = {}
|
||||
@@ -441,7 +407,7 @@ class Containerize():
|
||||
HTML.CreateHtmlTabFooter(False)
|
||||
return False
|
||||
else:
|
||||
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
|
||||
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
|
||||
if result is not None:
|
||||
size = float(result.group("size")) / 1000000
|
||||
imageSizeStr = f'{size:.1f}'
|
||||
@@ -450,17 +416,9 @@ class Containerize():
|
||||
else:
|
||||
logging.debug('ran-base size is unknown')
|
||||
|
||||
# Recover build logs, for the moment only possible when build is successful
|
||||
cmd.run(f"{self.cli} create --name test {baseImage}:{baseTag}")
|
||||
cmd.run("mkdir -p cmake_targets/log/ran-base")
|
||||
cmd.run(f"{self.cli} cp test:/oai-ran/cmake_targets/log/. cmake_targets/log/ran-base")
|
||||
cmd.run(f"{self.cli} rm -f test")
|
||||
|
||||
# Build the target image(s)
|
||||
status = True
|
||||
attemptedImages = ['ran-base']
|
||||
for image,pattern,name,option in imageNames:
|
||||
attemptedImages += [name]
|
||||
# the archived Dockerfiles have "ran-base:latest" as base image
|
||||
# we need to update them with proper tag
|
||||
cmd.run(f'git checkout -- docker/Dockerfile.{pattern}{self.dockerfileprefix}')
|
||||
@@ -474,15 +432,18 @@ class Containerize():
|
||||
cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}')
|
||||
if image == 'oai-gnb-aerial':
|
||||
cmd.run('cp -f /opt/nvidia-ipc/nvipc.src.2025.05.20.tar.gz .')
|
||||
ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > cmake_targets/log/{name}.log 2>&1', timeout=1200)
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/{name}.docker.log'
|
||||
ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > {logfile} 2>&1', timeout=1200)
|
||||
t = (name, archiveArtifact(cmd, ctx, logfile))
|
||||
log_files.append(t)
|
||||
if image == 'oai-gnb-aerial':
|
||||
cmd.run('rm -f nvipc.src.2025.05.20.tar.gz')
|
||||
if image == 'ran-build' and ret.returncode == 0:
|
||||
if image == 'ran-build' or image == 'ran-build-asan' or image == 'ran-build-fhi72':
|
||||
cmd.run(f"docker run --name test-log -d {name}:{imageTag} /bin/true")
|
||||
cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/ cmake_targets/log/{name}/")
|
||||
logfile = f'{lSourcePath}/{image}.ninja.log'
|
||||
cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/all.txt {logfile}")
|
||||
cmd.run(f"docker rm -f test-log")
|
||||
else:
|
||||
cmd.run(f"mkdir -p cmake_targets/log/{name}")
|
||||
archiveArtifact(cmd, ctx, logfile)
|
||||
# check the status of the build
|
||||
ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {name}:{imageTag}")
|
||||
if ret.returncode != 0:
|
||||
@@ -493,7 +454,7 @@ class Containerize():
|
||||
allImagesSize[name] = 'N/A -- Build Failed'
|
||||
break
|
||||
else:
|
||||
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
|
||||
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', cmd.getBefore())
|
||||
if result is not None:
|
||||
size = float(result.group("size")) / 1000000 # convert to MB
|
||||
imageSizeStr = f'{size:.1f}'
|
||||
@@ -516,13 +477,10 @@ class Containerize():
|
||||
logging.debug(cmd.run("df -h").stdout)
|
||||
logging.debug(cmd.run("docker system df").stdout)
|
||||
|
||||
# create a zip with all logs
|
||||
build_log_name = f'build_log_{self.testCase_id}'
|
||||
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
|
||||
cmd.close()
|
||||
|
||||
# Analyze the logs
|
||||
collectInfo = AnalyzeBuildLogs(build_log_name, attemptedImages, status)
|
||||
collectInfo = AnalyzeBuildLogs(log_files, status)
|
||||
|
||||
if status:
|
||||
logging.info('\u001B[1m Building OAI Image(s) Pass\u001B[0m')
|
||||
@@ -536,13 +494,12 @@ class Containerize():
|
||||
HTML.CreateHtmlTabFooter(False)
|
||||
return False
|
||||
|
||||
def BuildProxy(self, HTML):
|
||||
def BuildProxy(self, ctx, HTML):
|
||||
svr = self.eNB_serverId[self.eNB_instance]
|
||||
lIpAddr, lSourcePath = self.GetCredentials(svr)
|
||||
logging.debug('Building on server: ' + lIpAddr)
|
||||
ssh = cls_cmd.getConnection(lIpAddr)
|
||||
|
||||
self.testCase_id = HTML.testCase_id
|
||||
oldRanCommidID = self.ranCommitID
|
||||
oldRanRepository = self.ranRepository
|
||||
oldRanAllowMerge = self.ranAllowMerge
|
||||
@@ -568,15 +525,10 @@ class Containerize():
|
||||
if not success:
|
||||
raise Exception("could not clone proxy repository")
|
||||
|
||||
filename = f'build_log_{self.testCase_id}'
|
||||
fullpath = f'{lSourcePath}/{filename}'
|
||||
fullpath = f'{lSourcePath}/proxy_build.log'
|
||||
|
||||
ssh.run(f'docker build --target oai-lte-multi-ue-proxy --tag proxy:{tag} --file {lSourcePath}/docker/Dockerfile.ubuntu18.04 {lSourcePath} > {fullpath} 2>&1')
|
||||
ssh.run(f'zip -r -qq {fullpath}.zip {fullpath}')
|
||||
local_file = f"{os.getcwd()}/../cmake_targets/log/{filename}.zip"
|
||||
ssh.copyin(f'{fullpath}.zip', local_file)
|
||||
# don't delete such that we might recover the zips
|
||||
#ssh.run(f'rm -f {fullpath}.zip')
|
||||
archiveArtifact(ssh, ctx, fullpath)
|
||||
|
||||
ssh.run('docker image prune --force')
|
||||
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
|
||||
@@ -616,7 +568,7 @@ class Containerize():
|
||||
collectInfo = {}
|
||||
collectInfo['proxy'] = files
|
||||
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
|
||||
result = re.search('Size *= *(?P<size>[0-9\-]+) *bytes', ret.stdout)
|
||||
result = re.search(r'Size *= *(?P<size>[0-9\-]+) *bytes', ret.stdout)
|
||||
# Cleaning any created tmp volume
|
||||
ssh.run('docker volume prune --force')
|
||||
ssh.close()
|
||||
@@ -638,7 +590,7 @@ class Containerize():
|
||||
HTML.CreateHtmlTabFooter(False)
|
||||
return False
|
||||
|
||||
def BuildRunTests(self, HTML):
|
||||
def BuildRunTests(self, ctx, HTML):
|
||||
svr = self.eNB_serverId[self.eNB_instance]
|
||||
lIpAddr, lSourcePath = self.GetCredentials(svr)
|
||||
logging.debug('Building on server: ' + lIpAddr)
|
||||
@@ -655,7 +607,6 @@ class Containerize():
|
||||
|
||||
if self.forcedWorkspaceCleanup:
|
||||
cmd.run(f'sudo -S rm -Rf {lSourcePath}')
|
||||
self.testCase_id = HTML.testCase_id
|
||||
|
||||
# check that ran-base image exists as we expect it
|
||||
baseImage = 'ran-base'
|
||||
@@ -675,10 +626,10 @@ class Containerize():
|
||||
|
||||
# build ran-unittests image
|
||||
dockerfile = "ci-scripts/docker/Dockerfile.unittest.ubuntu22"
|
||||
ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {lSourcePath}/cmake_targets/log/unittest-build.log')
|
||||
logfile = f'{lSourcePath}/cmake_targets/log/unittest-build.log'
|
||||
ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {logfile}')
|
||||
archiveArtifact(cmd, ctx, logfile)
|
||||
if ret.returncode != 0:
|
||||
build_log_name = f'build_log_{self.testCase_id}'
|
||||
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
|
||||
logging.error(f'Cannot build unit tests')
|
||||
HTML.CreateHtmlTestRow("Unit test build failed", 'KO', [dockerfile])
|
||||
HTML.CreateHtmlTabFooter(False)
|
||||
@@ -687,10 +638,15 @@ class Containerize():
|
||||
HTML.CreateHtmlTestRowQueue("Build unit tests", 'OK', [dockerfile])
|
||||
|
||||
# it worked, build and execute tests, and close connection
|
||||
ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --rm ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)')
|
||||
cmd.run(f'docker rmi ran-unittests:{baseTag}')
|
||||
build_log_name = f'build_log_{self.testCase_id}'
|
||||
CopyLogsToExecutor(cmd, lSourcePath, build_log_name)
|
||||
# I would like to run it with --rm and mount the ctest result directory to avoid 'docker cp'
|
||||
# below, but then permissions are messed up and we can't remove the directory without sudo
|
||||
# making the next pipeline fail
|
||||
ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ran-unittests ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)')
|
||||
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTest.log .')
|
||||
archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTest.log')
|
||||
cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTestsFailed.log .')
|
||||
archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTestsFailed.log')
|
||||
cmd.run('docker rm ran-unittests')
|
||||
cmd.close()
|
||||
|
||||
if ret.returncode == 0:
|
||||
@@ -823,18 +779,14 @@ class Containerize():
|
||||
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["cannot create workspace"])
|
||||
return success
|
||||
|
||||
def DeployObject(self, HTML):
|
||||
def DeployObject(self, ctx, HTML):
|
||||
svr = self.eNB_serverId[self.eNB_instance]
|
||||
num_attempts = self.num_attempts
|
||||
lIpAddr, lSourcePath = self.GetCredentials(svr)
|
||||
logging.debug(f'Deploying OAI Object on server: {lIpAddr}')
|
||||
yaml = self.yamlPath[self.eNB_instance].strip('/')
|
||||
# creating the log folder by default
|
||||
local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml.split('/')[-1]}"
|
||||
os.system(f'mkdir -p {local_dir}')
|
||||
wd = f'{lSourcePath}/{yaml}'
|
||||
wd_yaml = f'{wd}/docker-compose.y*ml'
|
||||
yaml_dir = yaml.split('/')[-1]
|
||||
with cls_cmd.getConnection(lIpAddr) as ssh:
|
||||
services = GetServices(ssh, self.services[self.eNB_instance], wd_yaml)
|
||||
if services == [] or services == ' ' or services == None:
|
||||
@@ -861,7 +813,7 @@ class Containerize():
|
||||
logging.warning(warning_msg)
|
||||
HTML.CreateHtmlTestRowQueue('N/A', 'NOK', [warning_msg])
|
||||
for svc in services.split():
|
||||
CopyinServiceLog(ssh, lSourcePath, yaml_dir, svc, wd_yaml, f'{svc}-{HTML.testCase_id}-attempt{attempt}.log')
|
||||
CopyinServiceLog(ssh, lSourcePath, svc, wd_yaml, ctx)
|
||||
ssh.run(f'docker compose -f {wd_yaml} down -- {services}')
|
||||
imagesInfo = info.stdout.splitlines()[1:]
|
||||
logging.debug(f'{info.stdout.splitlines()[1:]}')
|
||||
@@ -871,13 +823,12 @@ class Containerize():
|
||||
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['\n'.join(imagesInfo)])
|
||||
return deployed
|
||||
|
||||
def UndeployObject(self, HTML, RAN):
|
||||
def UndeployObject(self, ctx, HTML, RAN):
|
||||
svr = self.eNB_serverId[self.eNB_instance]
|
||||
lIpAddr, lSourcePath = self.GetCredentials(svr)
|
||||
logging.debug(f'\u001B[1m Undeploying OAI Object from server: {lIpAddr}\u001B[0m')
|
||||
yaml = self.yamlPath[self.eNB_instance].strip('/')
|
||||
wd = f'{lSourcePath}/{yaml}'
|
||||
yaml_dir = yaml.split('/')[-1]
|
||||
with cls_cmd.getConnection(lIpAddr) as ssh:
|
||||
ExistEnvFilePrint(ssh, wd)
|
||||
services = GetRunningServices(ssh, f"{wd}/docker-compose.y*ml")
|
||||
@@ -885,7 +836,7 @@ class Containerize():
|
||||
if services is not None:
|
||||
all_serv = " ".join([s for s, _ in services])
|
||||
ssh.run(f'docker compose -f {wd}/docker-compose.y*ml stop -- {all_serv}')
|
||||
copyin_res = all(CopyinServiceLog(ssh, lSourcePath, yaml_dir, s, f"{wd}/docker-compose.y*ml", f'{s}-{HTML.testCase_id}.log') for s, c in services)
|
||||
copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, f"{wd}/docker-compose.y*ml", ctx) for s, _ in services]
|
||||
else:
|
||||
logging.warning('could not identify services to stop => no log file')
|
||||
ssh.run(f'docker compose -f {wd}/docker-compose.y*ml down -v')
|
||||
@@ -894,7 +845,7 @@ class Containerize():
|
||||
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['Could not copy logfile(s)'])
|
||||
return False
|
||||
else:
|
||||
log_results = [CheckLogs(self, yaml_dir, s, HTML, RAN) for s, _ in services]
|
||||
log_results = [CheckLogs(self, f, HTML, RAN) for f in copyin_res]
|
||||
success = all(log_results)
|
||||
if success:
|
||||
logging.info('\u001B[1m Undeploying OAI Object Pass\u001B[0m')
|
||||
|
||||
@@ -29,6 +29,7 @@ import yaml
|
||||
import re
|
||||
|
||||
import cls_cmd
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
def listify(s):
|
||||
if s is None:
|
||||
@@ -114,8 +115,7 @@ class CoreNetwork:
|
||||
logging.info(f'deployed core network {self}, pingable IP address {ip}')
|
||||
return True, output
|
||||
|
||||
def _collect_logs(self, log_dir):
|
||||
logging.info(f'collecting logs into (local) {log_dir}')
|
||||
def _collect_logs(self, ctx):
|
||||
remote_dir = "/tmp/cn-undeploy-logs"
|
||||
with cls_cmd.getConnection(self._host) as c:
|
||||
# create a directory for log collection
|
||||
@@ -134,18 +134,17 @@ class CoreNetwork:
|
||||
logging.error("cannot enumerate log files")
|
||||
return []
|
||||
log_files = []
|
||||
# copy them to the executor one by one, and store in log_dir
|
||||
# copy them to the executor one by one
|
||||
for f in ret.stdout.split("\n"):
|
||||
l = f.replace(remote_dir, log_dir)
|
||||
c.copyin(f, l)
|
||||
log_files.append(l)
|
||||
name = archiveArtifact(c, ctx, f)
|
||||
log_files.append(name)
|
||||
c.run(f'rm -rf {remote_dir}')
|
||||
return log_files
|
||||
|
||||
def undeploy(self, log_dir=None):
|
||||
def undeploy(self, ctx=None):
|
||||
log_files = []
|
||||
if log_dir is not None:
|
||||
log_files = self._collect_logs(log_dir)
|
||||
if ctx is not None:
|
||||
log_files = self._collect_logs(ctx)
|
||||
else:
|
||||
logging.warning("no directory for log collection specified, cannot retrieve core network logs")
|
||||
logging.info(f'undeploy core network {self}')
|
||||
|
||||
+54
-26
@@ -32,6 +32,7 @@ import re
|
||||
import yaml
|
||||
|
||||
import cls_cmd
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
class Module_UE:
|
||||
|
||||
@@ -59,26 +60,31 @@ class Module_UE:
|
||||
}
|
||||
self.interface = m.get('IF')
|
||||
self.MTU = m.get('MTU')
|
||||
self.trace = m.get('trace') == True
|
||||
self.logStore = m.get('LogStore')
|
||||
self.cmd_prefix = m.get('CmdPrefix')
|
||||
logging.info(f'initialized UE {self} from {filename}')
|
||||
|
||||
t = m.get('Tracing')
|
||||
self.trace = t is not None
|
||||
if self.trace:
|
||||
if t.get('Start') is None or t.get('Stop') is None or t.get('Collect')is None :
|
||||
raise ValueError("need to have Start/Stop/Collect for tracing")
|
||||
self.cmd_dict["traceStart"] = t.get('Start')
|
||||
self.cmd_dict["traceStop"] = t.get('Stop')
|
||||
self._logCollect = t.get('Collect')
|
||||
if "%%log_dir%%" not in self._logCollect:
|
||||
raise ValueError(f"(At least one) LogCollect expression for {module_name} must contain \"%%log_dir%%\"")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.module_name}@{self.host} [IP: {self.getIP()}]"
|
||||
return f"{self.module_name}@{self.host}"
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def _command(self, cmd, silent = False):
|
||||
def _command(self, cmd, silent=False, reportNonZero=True):
|
||||
if cmd is None:
|
||||
raise Exception("no command provided")
|
||||
if self.host == "" or self.host == "localhost":
|
||||
c = cls_cmd.LocalCmd()
|
||||
else:
|
||||
c = cls_cmd.RemoteCmd(self.host)
|
||||
response = c.run(cmd, silent=silent)
|
||||
c.close()
|
||||
with cls_cmd.getConnection(self.host) as c:
|
||||
response = c.run(cmd, silent=silent, reportNonZero=reportNonZero)
|
||||
return response
|
||||
|
||||
#-----------------$
|
||||
@@ -86,25 +92,23 @@ class Module_UE:
|
||||
#-----------------$
|
||||
|
||||
def initialize(self):
|
||||
if self.trace:
|
||||
raise Exception("UE tracing not implemented yet")
|
||||
self._enableTrace()
|
||||
# we first terminate to make sure the UE has been stopped
|
||||
if self.cmd_dict["detach"]:
|
||||
self._command(self.cmd_dict["detach"], silent=True)
|
||||
self._command(self.cmd_dict["terminate"], silent=True)
|
||||
ret = self._command(self.cmd_dict["initialize"])
|
||||
logging.info(f'For command: {ret.args} | return output: {ret.stdout} | Code: {ret.returncode}')
|
||||
if self.trace:
|
||||
self._enableTrace()
|
||||
# Here each UE returns differently for the successful initialization, requires check based on UE
|
||||
return ret.returncode == 0
|
||||
|
||||
|
||||
def terminate(self):
|
||||
def terminate(self, ctx=None):
|
||||
self._command(self.cmd_dict["terminate"])
|
||||
if self.trace:
|
||||
raise Exception("UE tracing not implemented yet")
|
||||
if self.trace and ctx is not None:
|
||||
self._disableTrace()
|
||||
return self._logCollect()
|
||||
return self._collectTrace(ctx)
|
||||
return None
|
||||
|
||||
def attach(self, attach_tries = 4, attach_timeout = 60):
|
||||
@@ -113,10 +117,11 @@ class Module_UE:
|
||||
self._command(self.cmd_dict["attach"])
|
||||
timeout = attach_timeout
|
||||
logging.debug("Waiting for IP address to be assigned")
|
||||
ip = self.getIP(silent=False, reportNonZero=True)
|
||||
while timeout > 0 and not ip:
|
||||
time.sleep(5)
|
||||
timeout -= 5
|
||||
ip = self.getIP()
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
ip = self.getIP(silent=True, reportNonZero=False)
|
||||
if ip:
|
||||
break
|
||||
logging.warning(f"UE did not receive IP address after {attach_timeout} s, detaching")
|
||||
@@ -160,8 +165,8 @@ class Module_UE:
|
||||
logging.error(message)
|
||||
return False
|
||||
|
||||
def getIP(self):
|
||||
output = self._command(self.cmd_dict["getNetwork"], silent=True)
|
||||
def getIP(self, silent=True, reportNonZero=True):
|
||||
output = self._command(self.cmd_dict["getNetwork"], silent=silent, reportNonZero=reportNonZero)
|
||||
result = re.search(r'inet (?P<ip>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', output.stdout)
|
||||
if result and result.group('ip'):
|
||||
ip = result.group('ip')
|
||||
@@ -191,10 +196,33 @@ class Module_UE:
|
||||
return self.cmd_prefix if self.cmd_prefix else ""
|
||||
|
||||
def _enableTrace(self):
|
||||
raise Exception("not implemented")
|
||||
logging.info(f'UE {self}: start UE tracing')
|
||||
self._command(self.cmd_dict["traceStart"])
|
||||
|
||||
def _disableTrace(self):
|
||||
raise Exception("not implemented")
|
||||
logging.info(f'UE {self}: stop UE tracing')
|
||||
self._command(self.cmd_dict["traceStop"])
|
||||
|
||||
def _logCollect(self):
|
||||
raise Exception("not implemented")
|
||||
def _collectTrace(self, ctx):
|
||||
remote_dir = "/tmp/ue-trace-logs"
|
||||
with cls_cmd.getConnection(self.host) as c:
|
||||
# create a directory for log collection
|
||||
c.run(f'rm -rf {remote_dir}')
|
||||
ret = c.run(f'mkdir {remote_dir}')
|
||||
if ret.returncode != 0:
|
||||
logging.error("cannot create directory for log collection")
|
||||
return []
|
||||
log_cmd = self._logCollect.replace('%%log_dir%%', remote_dir)
|
||||
self._command(log_cmd)
|
||||
# enumerate collected files
|
||||
ret = c.run(f'ls {remote_dir}/*')
|
||||
if ret.returncode != 0:
|
||||
logging.error("cannot enumerate log files")
|
||||
return []
|
||||
log_files = []
|
||||
# copy them to the executor one by one, and store in log_dir
|
||||
for f in ret.stdout.split("\n"):
|
||||
name = archiveArtifact(c, ctx, f)
|
||||
log_files.append(name)
|
||||
c.run(f'rm -rf {remote_dir}')
|
||||
return log_files
|
||||
|
||||
+10
-14
@@ -28,27 +28,24 @@ import cls_cmd
|
||||
import cls_oai_html
|
||||
import cls_analysis
|
||||
import constants as CONST
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
LOG_PATH_PHYSIM = 'phy_sim_logs'
|
||||
DPDK_PATH = '/opt/dpdk-t2-22.11.0'
|
||||
|
||||
class Native():
|
||||
|
||||
def Build(test_case, HTML, host, directory, options):
|
||||
def Build(ctx, test_case, HTML, host, directory, options):
|
||||
logging.debug(f'Building on server: {host}')
|
||||
HTML.testCase_id = test_case
|
||||
|
||||
with cls_cmd.getConnection(host) as ssh:
|
||||
base = f"{directory}/cmake_targets"
|
||||
ret = ssh.run(f"C_INCLUDE_PATH={DPDK_PATH}/include/ PKG_CONFIG_PATH={DPDK_PATH}/lib64/pkgconfig/ {base}/build_oai {options} > {base}/log/build_oai.log", timeout=900)
|
||||
ret = ssh.run(f"C_INCLUDE_PATH={DPDK_PATH}/include/ PKG_CONFIG_PATH={DPDK_PATH}/lib64/pkgconfig/ {base}/build_oai {options} > {base}/build_oai.log", timeout=900)
|
||||
success = ret.returncode == 0
|
||||
logs = ssh.run(f"cat {base}/log/build_oai.log", silent=True)
|
||||
logs = ssh.run(f"cat {base}/build_oai.log", silent=True)
|
||||
logging.debug(f"build finished with code {ret.returncode}, output:\n{logs.stdout}")
|
||||
|
||||
# create log directory, and copy build logs
|
||||
target = f"{base}/build_log_{test_case}/"
|
||||
ssh.run(f"mkdir -p {target}")
|
||||
ssh.run(f"mv {base}/log/* {target}")
|
||||
archiveArtifact(ssh, ctx, f'{base}/build_oai.log')
|
||||
|
||||
# check if build artifacts are there
|
||||
# NOTE: build_oai should fail with exit code if it could not build, but it does not
|
||||
@@ -68,15 +65,14 @@ class Native():
|
||||
HTML.CreateHtmlTestRow(options, 'KO', CONST.ALL_PROCESSES_OK)
|
||||
return success
|
||||
|
||||
def Run_Physim(HTML, host, directory, options, physim_test, threshold):
|
||||
def Run_Physim(ctx, HTML, host, directory, options, physim_test, threshold):
|
||||
logging.debug(f'Runnin {physim_test} on server: {host}')
|
||||
workSpacePath = f'{directory}/cmake_targets'
|
||||
os.system(f'mkdir -p ./{LOG_PATH_PHYSIM}')
|
||||
runLogFile=f'physim_{HTML.testCase_id}.log'
|
||||
runLogFile=f'{workSpacePath}/physim.log'
|
||||
with cls_cmd.getConnection(host) as cmd:
|
||||
cmd.run(f'sudo LD_LIBRARY_PATH=.:{DPDK_PATH}/lib64/ {workSpacePath}/ran_build/build/{physim_test} {options} >> {workSpacePath}/{runLogFile}')
|
||||
cmd.copyin(src=f'{workSpacePath}/{runLogFile}', tgt=f'{LOG_PATH_PHYSIM}/{runLogFile}')
|
||||
success, msg = cls_analysis.Analysis.analyze_physim(f'{LOG_PATH_PHYSIM}/{runLogFile}', physim_test, options, threshold)
|
||||
cmd.run(f'sudo LD_LIBRARY_PATH=.:{DPDK_PATH}/lib64/ {workSpacePath}/ran_build/build/{physim_test} {options} >> {runLogFile}')
|
||||
physim_file = archiveArtifact(cmd, ctx, runLogFile)
|
||||
success, msg = cls_analysis.Analysis.analyze_physim(physim_file, physim_test, options, threshold)
|
||||
if success:
|
||||
HTML.CreateHtmlTestRowQueue(options, 'OK', [msg])
|
||||
else:
|
||||
|
||||
+78
-127
@@ -47,6 +47,7 @@ import constants as CONST
|
||||
import cls_module
|
||||
import cls_corenetwork
|
||||
import cls_cmd
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Helper functions used here and in other classes
|
||||
@@ -83,6 +84,29 @@ def Iperf_ComputeTime(args):
|
||||
raise Exception('Iperf time not found!')
|
||||
return int(result.group('iperf_time'))
|
||||
|
||||
def convert_to_mbps(value, magnitude):
|
||||
value = float(value)
|
||||
if magnitude == 'K' or magnitude == 'k':
|
||||
return value / 1000
|
||||
elif magnitude == 'M':
|
||||
return value
|
||||
elif magnitude == 'G':
|
||||
return value * 1000
|
||||
else:
|
||||
return value
|
||||
|
||||
def extract_iperf_data(res):
|
||||
if not res:
|
||||
return None
|
||||
bitrate_val = res.group('bitrate')
|
||||
magnitude = res.group('magnitude')
|
||||
return {
|
||||
'bitrate_mbps': convert_to_mbps(bitrate_val, magnitude),
|
||||
'jitter': res.group('jitter'),
|
||||
'packetloss': res.group('packetloss'),
|
||||
'bitrate_disp': f'{float(bitrate_val):.2f} {magnitude}bps'
|
||||
}
|
||||
|
||||
def Iperf_analyzeV3TCPJson(filename, iperf_tcp_rate_target):
|
||||
try:
|
||||
with open(filename) as f:
|
||||
@@ -130,54 +154,37 @@ def Iperf_analyzeV3BIDIRJson(filename):
|
||||
return (True, msg)
|
||||
|
||||
def Iperf_analyzeV3UDP(filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate):
|
||||
if (not os.path.isfile(filename)):
|
||||
if not os.path.isfile(filename):
|
||||
return (False, 'Iperf3 UDP: Log file not present')
|
||||
if (os.path.getsize(filename)==0):
|
||||
if os.path.getsize(filename) == 0:
|
||||
return (False, 'Iperf3 UDP: Log file is empty')
|
||||
sender_bitrate = None
|
||||
receiver_bitrate = None
|
||||
with open(filename, 'r') as server_file:
|
||||
for line in server_file.readlines():
|
||||
res_sender = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<unit>[KMG]?bits\/sec)\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<sentPack>-?\d+) \((?P<lost>[0-9\.]+).*?\s+(sender)', line)
|
||||
res_receiver = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<unit>[KMG]?bits\/sec)\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<receivedPack>-?\d+)\s+\((?P<lost>[0-9\.]+)%\).*?(receiver)', line)
|
||||
if res_sender is not None:
|
||||
sender_bitrate = res_sender.group('bitrate')
|
||||
sender_unit = res_sender.group('unit')
|
||||
sender_jitter = res_sender.group('jitter')
|
||||
sender_lostPack = res_sender.group('lostPack')
|
||||
sender_sentPack = res_sender.group('sentPack')
|
||||
sender_packetloss = res_sender.group('lost')
|
||||
if res_receiver is not None:
|
||||
receiver_bitrate = res_receiver.group('bitrate')
|
||||
receiver_unit = res_receiver.group('unit')
|
||||
receiver_jitter = res_receiver.group('jitter')
|
||||
receiver_lostPack = res_receiver.group('lostPack')
|
||||
receiver_receivedPack = res_receiver.group('receivedPack')
|
||||
receiver_packetloss = res_receiver.group('lost')
|
||||
|
||||
if receiver_bitrate is not None and sender_bitrate is not None:
|
||||
if sender_unit == 'Kbits/sec':
|
||||
sender_bitrate = float(sender_bitrate) / 1000
|
||||
if receiver_unit == 'Kbits/sec':
|
||||
receiver_bitrate = float(receiver_bitrate) / 1000
|
||||
br_perf = 100 * float(receiver_bitrate) / float(target_bitrate)
|
||||
br_perf = '%.2f ' % br_perf
|
||||
sender_bitrate = '%.2f ' % float(sender_bitrate)
|
||||
receiver_bitrate = '%.2f ' % float(receiver_bitrate)
|
||||
req_msg = f'Sender Bitrate : {sender_bitrate} Mbps'
|
||||
bir_msg = f'Receiver Bitrate: {receiver_bitrate} Mbps'
|
||||
brl_msg = f'{br_perf}%'
|
||||
jit_msg = f'Jitter : {receiver_jitter}'
|
||||
pal_msg = f'Packet Loss : {receiver_packetloss} %'
|
||||
if float(br_perf) < float(iperf_bitrate_threshold):
|
||||
brl_msg = f'too low! < {iperf_bitrate_threshold}%'
|
||||
if float(receiver_packetloss) > float(iperf_packetloss_threshold):
|
||||
pal_msg += f' (too high! > {iperf_packetloss_threshold}%)'
|
||||
result = float(br_perf) >= float(iperf_bitrate_threshold) and float(receiver_packetloss) <= float(iperf_packetloss_threshold)
|
||||
return (result, f'{req_msg}\n{bir_msg} ({brl_msg})\n{jit_msg}\n{pal_msg}')
|
||||
else:
|
||||
sender_data = None
|
||||
receiver_data = None
|
||||
with open(filename, 'r') as server_file:
|
||||
for line in server_file:
|
||||
res_sender = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<magnitude>[kKMG]?)bits\/sec\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<sentPack>-?\d+)\s+\((?P<packetloss>[0-9\.eE\-\+]+).*?\s+(sender)', line)
|
||||
res_receiver = re.search(r'(?P<bitrate>[0-9\.]+)\s+(?P<magnitude>[kKMG]?)bits\/sec\s+(?P<jitter>[0-9\.]+\s+ms)\s+(?P<lostPack>-?\d+)/(?P<receivedPack>-?\d+)\s+\((?P<packetloss>[0-9\.eE\-\+]+)%\).*?(receiver)', line)
|
||||
if res_sender:
|
||||
sender_data = extract_iperf_data(res_sender)
|
||||
if res_receiver:
|
||||
receiver_data = extract_iperf_data(res_receiver)
|
||||
if not sender_data or not receiver_data:
|
||||
return (False, 'Could not analyze iperf report')
|
||||
|
||||
br_perf = 100 * receiver_data['bitrate_mbps'] / float(target_bitrate)
|
||||
br_perf_str = f'{br_perf:.2f}%'
|
||||
req_msg = f"Sender Bitrate : {sender_data['bitrate_disp']}"
|
||||
bir_msg = f"Receiver Bitrate: {receiver_data['bitrate_disp']} ({br_perf_str})"
|
||||
jit_msg = f"Jitter : {receiver_data['jitter']}"
|
||||
pal_msg = f"Packet Loss : {receiver_data['packetloss']}%"
|
||||
if br_perf < float(iperf_bitrate_threshold):
|
||||
bir_msg += f' (too low! < {iperf_bitrate_threshold}%)'
|
||||
if float(receiver_data['packetloss']) > float(iperf_packetloss_threshold):
|
||||
pal_msg += f' (too high! > {iperf_packetloss_threshold}%)'
|
||||
result = br_perf >= float(iperf_bitrate_threshold) and float(receiver_data['packetloss']) <= float(iperf_packetloss_threshold)
|
||||
return (result, f'{req_msg}\n{bir_msg}\n{jit_msg}\n{pal_msg}')
|
||||
|
||||
def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate):
|
||||
result = None
|
||||
if (not os.path.isfile(server_filename)):
|
||||
@@ -190,12 +197,9 @@ def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetlos
|
||||
result = re.search(statusTemplate, str(line)) or result
|
||||
if result is None:
|
||||
return (False, 'Could not parse server report!')
|
||||
bitrate = float(result.group('bitrate'))
|
||||
magn = result.group('magnitude')
|
||||
if magn == "k" or magn == "K":
|
||||
bitrate /= 1000
|
||||
elif magn == "G": # we assume bitrate in Mbps, therefore it must be G now
|
||||
bitrate *= 1000
|
||||
bitrate_val = float(result.group('bitrate'))
|
||||
magnitude = result.group('magnitude')
|
||||
bitrate = convert_to_mbps(bitrate_val, magnitude)
|
||||
jitter = float(result.group('jitter'))
|
||||
packetloss = float(result.group('packetloss'))
|
||||
br_perf = float(bitrate)/float(target_bitrate) * 100
|
||||
@@ -256,9 +260,7 @@ class OaiCiTest():
|
||||
self.ranAllowMerge = False
|
||||
self.ranTargetBranch = ''
|
||||
|
||||
self.testCase_id = ''
|
||||
self.testXMLfiles = []
|
||||
self.desc = ''
|
||||
self.ping_args = ''
|
||||
self.ping_packetloss_threshold = ''
|
||||
self.ping_rttavg_threshold =''
|
||||
@@ -352,7 +354,7 @@ class OaiCiTest():
|
||||
HTML.CreateHtmlTestRowQueue('NA', 'OK', messages)
|
||||
return True
|
||||
|
||||
def Ping_common(self, cn, ue, logPath):
|
||||
def Ping_common(self, ctx, cn, ue):
|
||||
ping_status = 0
|
||||
ueIP = ue.getIP()
|
||||
if not ueIP:
|
||||
@@ -360,13 +362,12 @@ class OaiCiTest():
|
||||
svrIP = cn.getIP()
|
||||
if not svrIP:
|
||||
return (False, f"CN {cn.getName()} has no IP address")
|
||||
ping_log_file = f'ping_{self.testCase_id}_{ue.getName()}.log'
|
||||
ping_log_file = f'/tmp/ping_{ue.getName()}.log'
|
||||
ping_time = re.findall(r"-c *(\d+)",str(self.ping_args))
|
||||
local_ping_log_file = f'{logPath}/{ping_log_file}'
|
||||
if re.search('%cn_ip%', self.ping_args) or re.search(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', self.ping_args):
|
||||
raise Exception(f"ping_args should not have IP address: {self.ping_args}")
|
||||
interface = f'-I {ue.getIFName()}' if ue.getIFName() else ''
|
||||
ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee /tmp/{ping_log_file}'
|
||||
ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee {ping_log_file}'
|
||||
cmd = cls_cmd.getConnection(ue.getHost())
|
||||
response = cmd.run(ping_cmd, timeout=int(ping_time[0])*1.5)
|
||||
ue_header = f'UE {ue.getName()} ({ueIP})'
|
||||
@@ -374,8 +375,7 @@ class OaiCiTest():
|
||||
message = ue_header + ': ping crashed: TIMEOUT?'
|
||||
return (False, message)
|
||||
|
||||
#copy the ping log file to have it locally for analysis (ping stats)
|
||||
cmd.copyin(src=f'/tmp/{ping_log_file}', tgt=local_ping_log_file)
|
||||
local_ping_log_file = archiveArtifact(cmd, ctx, ping_log_file)
|
||||
cmd.close()
|
||||
|
||||
with open(local_ping_log_file, 'r') as f:
|
||||
@@ -415,18 +415,13 @@ class OaiCiTest():
|
||||
|
||||
return (True, message)
|
||||
|
||||
def Ping(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
|
||||
def Ping(self, ctx, HTML, infra_file="ci_infra.yaml"):
|
||||
if self.ue_ids == [] or self.svr_id == None:
|
||||
raise Exception("no module names in self.ue_ids or/and self.svr_id provided")
|
||||
# Creating destination log folder if needed on the python executor workspace
|
||||
with cls_cmd.getConnection('localhost') as local:
|
||||
ymlPath = CONTAINERS.yamlPath[0].split('/')
|
||||
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
|
||||
local.run(f'mkdir -p {logPath}', silent=True)
|
||||
ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)]
|
||||
cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
|
||||
futures = [executor.submit(self.Ping_common, cn, ue, logPath) for ue in ues]
|
||||
futures = [executor.submit(self.Ping_common, ctx, cn, ue) for ue in ues]
|
||||
results = [f.result() for f in futures]
|
||||
# each result in results is a tuple, first member goes to successes, second to messages
|
||||
successes, messages = map(list, zip(*results))
|
||||
@@ -447,7 +442,7 @@ class OaiCiTest():
|
||||
HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', messages)
|
||||
return success
|
||||
|
||||
def Iperf_Module(self, cn, ue, idx, ue_num, logPath):
|
||||
def Iperf_Module(self, ctx, cn, ue, idx, ue_num):
|
||||
ueIP = ue.getIP()
|
||||
if not ueIP:
|
||||
return (False, f"UE {ue.getName()} has no IP address")
|
||||
@@ -460,7 +455,7 @@ class OaiCiTest():
|
||||
serverReport = ""
|
||||
udpIperf = re.search('-u', iperf_opt) is not None
|
||||
bidirIperf = re.search('--bidir', iperf_opt) is not None
|
||||
client_filename = f'iperf_client_{self.testCase_id}_{ue.getName()}.log'
|
||||
client_filename = f'/tmp/iperf_client_{ue.getName()}.log'
|
||||
if udpIperf:
|
||||
target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(idx, ue_num, self.iperf_profile, self.iperf_args)
|
||||
# note: for UDP testing we don't want to use json report - reports 0 Mbps received bitrate
|
||||
@@ -475,14 +470,11 @@ class OaiCiTest():
|
||||
port = 5002 + idx
|
||||
# note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False`
|
||||
t = iperf_time * 2.5
|
||||
cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True)
|
||||
cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True)
|
||||
if cn.runIperf3Server():
|
||||
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t)
|
||||
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> /tmp/{client_filename}', timeout=t)
|
||||
# note: copy iperf3 log to the current directory for log analysis and log collection
|
||||
dest_filename = f'{logPath}/{client_filename}'
|
||||
cmd_ue.copyin(f'/tmp/{client_filename}', dest_filename)
|
||||
cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True)
|
||||
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t)
|
||||
dest_filename = archiveArtifact(cmd_ue, ctx, client_filename)
|
||||
if udpIperf:
|
||||
status, msg = Iperf_analyzeV3UDP(dest_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
|
||||
elif bidirIperf:
|
||||
@@ -492,20 +484,15 @@ class OaiCiTest():
|
||||
|
||||
return (status, f'{ue_header}\n{msg}')
|
||||
|
||||
def Iperf(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
|
||||
def Iperf(self, ctx, HTML, infra_file="ci_infra.yaml"):
|
||||
logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"')
|
||||
|
||||
if self.ue_ids == [] or self.svr_id == None:
|
||||
raise Exception("no module names in self.ue_ids or/and self.svr_id provided")
|
||||
# create log directory on executor node
|
||||
with cls_cmd.getConnection('localhost') as local:
|
||||
ymlPath = CONTAINERS.yamlPath[0].split('/')
|
||||
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
|
||||
local.run(f'mkdir -p {logPath}', silent=True)
|
||||
ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)]
|
||||
cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
|
||||
futures = [executor.submit(self.Iperf_Module, cn, ue, i, len(ues), logPath) for i, ue in enumerate(ues)]
|
||||
futures = [executor.submit(self.Iperf_Module, ctx, cn, ue, i, len(ues)) for i, ue in enumerate(ues)]
|
||||
results = [f.result() for f in futures]
|
||||
# each result in results is a tuple, first member goes to successes, second to messages
|
||||
successes, messages = map(list, zip(*results))
|
||||
@@ -526,7 +513,7 @@ class OaiCiTest():
|
||||
HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages)
|
||||
return success
|
||||
|
||||
def Iperf2_Unidir(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"):
|
||||
def Iperf2_Unidir(self, ctx, HTML, infra_file="ci_infra.yaml"):
|
||||
if self.ue_ids == [] or self.svr_id == None or len(self.ue_ids) != 1:
|
||||
raise Exception("no module names in self.ue_ids or/and self.svr_id provided, multi UE scenario not supported")
|
||||
ue = cls_module.Module_UE(self.ue_ids[0].strip(),self.nodes[0].strip(), infra_file)
|
||||
@@ -537,23 +524,17 @@ class OaiCiTest():
|
||||
svrIP = cn.getIP()
|
||||
if not svrIP:
|
||||
return False
|
||||
server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log'
|
||||
ymlPath = CONTAINERS.yamlPath[0].split('/')
|
||||
logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}'
|
||||
server_filename = f'/tmp/iperf_server_{ue.getName()}.log'
|
||||
iperf_time = Iperf_ComputeTime(self.iperf_args)
|
||||
target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(0, 1, self.iperf_profile, self.iperf_args)
|
||||
t = iperf_time*2.5
|
||||
with cls_cmd.getConnection('localhost') as local:
|
||||
local.run(f'mkdir -p {logPath}')
|
||||
with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(cn.getHost()) as cmd_svr:
|
||||
cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False)
|
||||
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> /tmp/{server_filename} &', timeout=t)
|
||||
cmd_ue.run(f'rm {server_filename}', reportNonZero=False)
|
||||
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> {server_filename} &', timeout=t)
|
||||
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 >> /dev/null', timeout=t)
|
||||
localPath = f'{os.getcwd()}'
|
||||
# note: copy iperf2 log to the directory for log collection
|
||||
cmd_ue.copyin(f'/tmp/{server_filename}', f'{logPath}/{server_filename}')
|
||||
cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False)
|
||||
success, msg = Iperf_analyzeV2UDP(f'{logPath}/{server_filename}', self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
|
||||
local = archiveArtifact(cmd_ue, ctx, server_filename)
|
||||
success, msg = Iperf_analyzeV2UDP(local, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)
|
||||
ue_header = f'UE {ue.getName()} ({ueIP})'
|
||||
logging.info(f'\u001B[1;37;45m iperf result for {ue_header}\u001B[0m')
|
||||
for l in msg.split('\n'):
|
||||
@@ -835,17 +816,17 @@ class OaiCiTest():
|
||||
global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC
|
||||
return global_status
|
||||
|
||||
def TerminateUE(self, HTML):
|
||||
def TerminateUE(self, ctx, HTML):
|
||||
ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids]
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor:
|
||||
futures = [executor.submit(ue.terminate) for ue in ues]
|
||||
futures = [executor.submit(ue.terminate, ctx) for ue in ues]
|
||||
archives = [f.result() for f in futures]
|
||||
archive_info = [f'Log at: {a}' if a else 'No log available' for a in archives]
|
||||
messages = [f"UE {ue.getName()}: {log}" for (ue, log) in zip(ues, archive_info)]
|
||||
HTML.CreateHtmlTestRowQueue(f'N/A', 'OK', messages)
|
||||
return True
|
||||
|
||||
def DeployCoreNetwork(cn_id, HTML):
|
||||
def DeployCoreNetwork(cn_id, ctx, HTML):
|
||||
core_name = cn_id.strip()
|
||||
cn = cls_corenetwork.CoreNetwork(core_name)
|
||||
success, output = cn.deploy()
|
||||
@@ -859,41 +840,11 @@ class OaiCiTest():
|
||||
HTML.CreateHtmlTestRowQueue(core_name, 'KO', [msg])
|
||||
return success
|
||||
|
||||
def UndeployCoreNetwork(cn_id, HTML):
|
||||
# Ping, Iperf, DeployObject put logs into a path based on YAML. We
|
||||
# can't do this here (because there is no yaml), so hardcode a path for
|
||||
# "cn_logs" for the moment
|
||||
logPath = f'{os.getcwd()}/../cmake_targets/log/cn_logs'
|
||||
with cls_cmd.getConnection('localhost') as local:
|
||||
local.run(f'mkdir -p {logPath}', silent=True)
|
||||
def UndeployCoreNetwork(cn_id, ctx, HTML):
|
||||
core_name = cn_id.strip()
|
||||
cn = cls_corenetwork.CoreNetwork(core_name)
|
||||
logs, output = cn.undeploy(log_dir=logPath)
|
||||
logs, output = cn.undeploy(ctx=ctx)
|
||||
logging.info(f"undeployed core network {core_name}, logs {logs}, output:\n{output}")
|
||||
message = "Log files: " + ", ".join([os.path.basename(l) for l in logs])
|
||||
message = "Log files:\n" + "\n".join([os.path.basename(l) for l in logs])
|
||||
HTML.CreateHtmlTestRowQueue(core_name, 'OK', [message])
|
||||
return True
|
||||
|
||||
def LogCollectBuild(self,RAN):
|
||||
# Some pipelines are using "none" IP / Credentials
|
||||
# In that case, just forget about it
|
||||
if RAN.eNBIPAddress == 'none':
|
||||
sys.exit(0)
|
||||
|
||||
if (RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != ''):
|
||||
IPAddress = RAN.eNBIPAddress
|
||||
UserName = RAN.eNBUserName
|
||||
Password = RAN.eNBPassword
|
||||
SourceCodePath = RAN.eNBSourceCodePath
|
||||
else:
|
||||
sys.exit('Insufficient Parameter')
|
||||
with cls_cmd.getConnection(IPAddress) as cmd:
|
||||
d = f'{SourceCodePath}/cmake_targets'
|
||||
cmd.run(f'rm -f {d}/build.log.zip')
|
||||
cmd.run(f'cd {d} && zip -r build.log.zip build_log_*/*')
|
||||
|
||||
def ShowTestID(self):
|
||||
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
|
||||
logging.info(f'\u001B[1m Test ID: {self.testCase_id} \u001B[0m')
|
||||
logging.info(f'\u001B[1m {self.desc} \u001B[0m')
|
||||
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
|
||||
|
||||
@@ -43,6 +43,7 @@ from pathlib import Path
|
||||
import helpreadme as HELP
|
||||
import constants as CONST
|
||||
import cls_cmd
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Class Declaration
|
||||
@@ -80,7 +81,7 @@ class StaticCodeAnalysis():
|
||||
self.eNBPassword = ''
|
||||
self.eNBSourceCodePath = ''
|
||||
|
||||
def CppCheckAnalysis(self, HTML):
|
||||
def CppCheckAnalysis(self, ctx, HTML):
|
||||
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
sys.exit('Insufficient Parameter')
|
||||
@@ -103,7 +104,7 @@ class StaticCodeAnalysis():
|
||||
full_ran_repo_name = self.ranRepository + '.git'
|
||||
|
||||
cmd.cd(lSourcePath)
|
||||
logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}'
|
||||
logDir = f'{lSourcePath}/cmake_targets/log'
|
||||
cmd.run(f'mkdir -p {logDir}')
|
||||
cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
|
||||
cmd.run(f'sed -e "s@xenial@bionic@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic')
|
||||
@@ -112,31 +113,21 @@ class StaticCodeAnalysis():
|
||||
cmd.run(f'docker build --tag oai-cppcheck:focal --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal . > {logDir}/cppcheck-focal.txt 2>&1')
|
||||
cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
|
||||
|
||||
# Analyzing the logs
|
||||
cmd.copyin(f'{logDir}/cppcheck-bionic.txt', 'cppcheck-bionic.txt')
|
||||
cmd.copyin(f'{logDir}/cppcheck-focal.txt', 'cppcheck-focal.txt')
|
||||
bionic = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-bionic.txt')
|
||||
focal = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-focal.txt')
|
||||
cmd.close()
|
||||
|
||||
CCR = CppCheckResults()
|
||||
CCR_ref = CppCheckResults()
|
||||
vId = 0
|
||||
for variant in CCR.variants:
|
||||
refAvailable = False
|
||||
if self.ranAllowMerge:
|
||||
refFolder = str(Path.home()) + '/cppcheck-references'
|
||||
if (os.path.isfile(refFolder + '/cppcheck-'+ variant + '.txt')):
|
||||
refAvailable = True
|
||||
with open(refFolder + '/cppcheck-'+ variant + '.txt', 'r') as refFile:
|
||||
for line in refFile:
|
||||
ret = re.search(' (?P<nb_errors>[0-9\.]+) errors', str(line))
|
||||
if ret is not None:
|
||||
CCR_ref.nbErrors[vId] = int(ret.group('nb_errors'))
|
||||
ret = re.search(' (?P<nb_warnings>[0-9\.]+) warnings', str(line))
|
||||
if ret is not None:
|
||||
CCR_ref.nbWarnings[vId] = int(ret.group('nb_warnings'))
|
||||
if (os.path.isfile('./cppcheck-'+ variant + '.txt')):
|
||||
filename = ctx.baseFilename() + '-cppcheck-'+ variant + '.txt'
|
||||
logging.info(f"will check file '{filename}'")
|
||||
if not os.path.isfile(filename):
|
||||
raise FileNotFoundError(f"{filename} is not a file")
|
||||
else:
|
||||
xmlStart = False
|
||||
with open('./cppcheck-'+ variant + '.txt', 'r') as logfile:
|
||||
with open(filename, 'r') as logfile:
|
||||
for line in logfile:
|
||||
ret = re.search('cppcheck version="(?P<version>[0-9\.]+)"', str(line))
|
||||
if ret is not None:
|
||||
@@ -185,26 +176,6 @@ class StaticCodeAnalysis():
|
||||
vMsg += ' Wrong Scanf Nb Args: ' + str(CCR.nbWrongScanfArg[vId]) + '\n'
|
||||
for vLine in vMsg.split('\n'):
|
||||
logging.debug(vLine)
|
||||
if self.ranAllowMerge and refAvailable:
|
||||
if CCR_ref.nbErrors[vId] == CCR.nbErrors[vId]:
|
||||
logging.debug(' No change in number of errors')
|
||||
elif CCR_ref.nbErrors[vId] > CCR.nbErrors[vId]:
|
||||
logging.debug(' Good! Decrease in number of errors')
|
||||
else:
|
||||
logging.debug(' Bad! increase in number of errors')
|
||||
if CCR_ref.nbWarnings[vId] == CCR.nbWarnings[vId]:
|
||||
logging.debug(' No change in number of warnings')
|
||||
elif CCR_ref.nbWarnings[vId] > CCR.nbWarnings[vId]:
|
||||
logging.debug(' Good! Decrease in number of warnings')
|
||||
else:
|
||||
logging.debug(' Bad! increase in number of warnings')
|
||||
# Create new reference file
|
||||
if not self.ranAllowMerge:
|
||||
refFolder = str(Path.home()) + '/cppcheck-references'
|
||||
if not os.path.isdir(refFolder):
|
||||
os.mkdir(refFolder)
|
||||
with open(refFolder + '/cppcheck-'+ variant + '.txt', 'w') as refFile:
|
||||
refFile.write(vMsg)
|
||||
vId += 1
|
||||
|
||||
HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||
@@ -213,7 +184,7 @@ class StaticCodeAnalysis():
|
||||
|
||||
return True
|
||||
|
||||
def LicenceAndFormattingCheck(self, HTML):
|
||||
def LicenceAndFormattingCheck(self, ctx, HTML):
|
||||
# Workspace is no longer recreated from scratch.
|
||||
# It implies that this method shall be called last within a build pipeline
|
||||
# where workspace is already created
|
||||
@@ -238,7 +209,7 @@ class StaticCodeAnalysis():
|
||||
else:
|
||||
check_options += f' --build-arg TARGET_BRANCH={self.ranTargetBranch}'
|
||||
|
||||
logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}'
|
||||
logDir = f'{lSourcePath}/cmake_targets/log/'
|
||||
cmd.run(f'mkdir -p {logDir}')
|
||||
cmd.run('docker image rm oai-formatting-check:latest')
|
||||
cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {lSourcePath}/ci-scripts/docker/Dockerfile.formatting.bionic {lSourcePath} > {logDir}/oai-formatting-check.txt 2>&1')
|
||||
@@ -247,12 +218,11 @@ class StaticCodeAnalysis():
|
||||
cmd.run('docker image prune --force')
|
||||
cmd.run('docker volume prune --force')
|
||||
|
||||
# Analyzing the logs
|
||||
cmd.copyin(f'{logDir}/oai-formatting-check.txt', 'oai-formatting-check.txt')
|
||||
file = archiveArtifact(cmd, ctx, f'{logDir}/oai-formatting-check.txt')
|
||||
cmd.close()
|
||||
|
||||
finalStatus = 0
|
||||
if (os.path.isfile('./oai-formatting-check.txt')):
|
||||
if (os.path.isfile(file)):
|
||||
analyzed = False
|
||||
nbFilesNotFormatted = 0
|
||||
listFiles = False
|
||||
@@ -263,7 +233,7 @@ class StaticCodeAnalysis():
|
||||
gnuGplLicenceFiles = []
|
||||
suspectLicence = False
|
||||
suspectLicenceFiles = []
|
||||
with open('./oai-formatting-check.txt', 'r') as logfile:
|
||||
with open(file, 'r') as logfile:
|
||||
for line in logfile:
|
||||
ret = re.search('./ci-scripts/checkCodingFormattingRules.sh', str(line))
|
||||
if ret is not None:
|
||||
|
||||
@@ -23,9 +23,7 @@ gNBs =
|
||||
|
||||
local_s_address = "192.168.71.150";
|
||||
remote_s_address = "192.168.71.171";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -23,9 +23,7 @@ gNBs =
|
||||
|
||||
local_s_address = "127.0.0.4";
|
||||
remote_s_address = "127.0.0.5";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -23,9 +23,7 @@ gNBs =
|
||||
|
||||
local_s_address = "192.168.71.150";
|
||||
remote_s_address = "0.0.0.0"; # multiple DUs
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2152;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2152;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -23,9 +23,7 @@ gNBs =
|
||||
|
||||
local_s_address = "127.0.0.4";
|
||||
remote_s_address = "127.0.0.5";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -22,9 +22,7 @@ gNBs =
|
||||
|
||||
local_s_address = "192.168.72.161";
|
||||
remote_s_address = "192.168.72.171";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -22,9 +22,7 @@ gNBs =
|
||||
|
||||
local_s_address = "127.0.0.6";
|
||||
remote_s_address = "127.0.0.5";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -163,9 +163,7 @@ MACRLCs = (
|
||||
tr_n_preference = "f1";
|
||||
local_n_address = "127.0.0.5";
|
||||
remote_n_address = "127.0.0.4";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
|
||||
@@ -168,9 +168,7 @@ MACRLCs = (
|
||||
tr_n_preference = "f1";
|
||||
local_n_address = "192.168.71.171";
|
||||
remote_n_address = "192.168.71.150";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
|
||||
@@ -204,9 +204,7 @@ MACRLCs = (
|
||||
tr_n_preference = "f1";
|
||||
local_n_address = "127.0.0.5";
|
||||
remote_n_address = "127.0.0.4";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
|
||||
@@ -16,7 +16,6 @@ gNBs =
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
sib1_tda = 5;
|
||||
min_rxtxtime = 6;
|
||||
num_dlharq = 32;
|
||||
num_ulharq = 32;
|
||||
@@ -150,6 +149,8 @@ gNBs =
|
||||
|
||||
#ext2
|
||||
#ntn_Config_r17
|
||||
# This can be of values {s5, s10, s15, s20, s25, s30, s35, s40, s45, s50, s55, s60, s120, s180, s240, s900}
|
||||
ntn-UlSyncValidityDuration-r17 = 5;
|
||||
cellSpecificKoffset_r17 = 40;
|
||||
ta-Common-r17 = 4627000; # 18.84 ms
|
||||
ta-CommonDrift-r17 = -230000; # -46 µs/s
|
||||
|
||||
@@ -16,7 +16,6 @@ gNBs =
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
sib1_tda = 5;
|
||||
min_rxtxtime = 6;
|
||||
disable_harq = 1;
|
||||
cu_sibs = [2];
|
||||
|
||||
+44
-41
@@ -43,8 +43,10 @@ import cls_containerize #class Containerize for all container-based operations
|
||||
import cls_static_code_analysis #class for static code analysis
|
||||
import cls_cluster # class for building/deploying on cluster
|
||||
import cls_native # class for all native/source-based operations
|
||||
from cls_ci_helper import TestCaseCtx
|
||||
|
||||
import ran
|
||||
import cls_cmd
|
||||
import cls_oai_html
|
||||
|
||||
|
||||
@@ -91,7 +93,7 @@ def AssignParams(params_dict):
|
||||
setattr(RAN, key, value)
|
||||
setattr(HTML, key, value)
|
||||
|
||||
def ExecuteActionWithParam(action):
|
||||
def ExecuteActionWithParam(action, ctx):
|
||||
global RAN
|
||||
global HTML
|
||||
global CONTAINERS
|
||||
@@ -134,15 +136,15 @@ def ExecuteActionWithParam(action):
|
||||
if proxy_commit is not None:
|
||||
CONTAINERS.proxyCommit = proxy_commit
|
||||
if action == 'Build_eNB':
|
||||
success = cls_native.Native.Build(HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args)
|
||||
success = cls_native.Native.Build(ctx, HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args)
|
||||
elif action == 'Build_Image':
|
||||
success = CONTAINERS.BuildImage(HTML)
|
||||
success = CONTAINERS.BuildImage(ctx, HTML)
|
||||
elif action == 'Build_Proxy':
|
||||
success = CONTAINERS.BuildProxy(HTML)
|
||||
success = CONTAINERS.BuildProxy(ctx, HTML)
|
||||
elif action == 'Build_Cluster_Image':
|
||||
success = CLUSTER.BuildClusterImage(HTML)
|
||||
success = CLUSTER.BuildClusterImage(ctx, HTML)
|
||||
elif action == 'Build_Run_Tests':
|
||||
success = CONTAINERS.BuildRunTests(HTML)
|
||||
success = CONTAINERS.BuildRunTests(ctx, HTML)
|
||||
|
||||
elif action == 'Initialize_eNB':
|
||||
datalog_rt_stats_file=test.findtext('rt_stats_cfg')
|
||||
@@ -204,7 +206,7 @@ def ExecuteActionWithParam(action):
|
||||
RAN.air_interface[RAN.eNB_instance] = 'lte-softmodem'
|
||||
else:
|
||||
RAN.air_interface[RAN.eNB_instance] = air_interface.lower() +'-softmodem'
|
||||
success = RAN.TerminateeNB(HTML)
|
||||
success = RAN.TerminateeNB(ctx, HTML)
|
||||
|
||||
elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE':
|
||||
CiTestObj.ue_ids = test.findtext('id').split(' ')
|
||||
@@ -226,7 +228,7 @@ def ExecuteActionWithParam(action):
|
||||
elif action == 'Detach_UE':
|
||||
success = CiTestObj.DetachUE(HTML)
|
||||
elif action == 'Terminate_UE':
|
||||
success = CiTestObj.TerminateUE(HTML)
|
||||
success = CiTestObj.TerminateUE(ctx, HTML)
|
||||
elif action == 'CheckStatusUE':
|
||||
success = CiTestObj.CheckStatusUE(HTML)
|
||||
elif action == 'DataEnable_UE':
|
||||
@@ -253,7 +255,7 @@ def ExecuteActionWithParam(action):
|
||||
else:
|
||||
CiTestObj.nodes = [None] * len(CiTestObj.ue_ids)
|
||||
ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or ''
|
||||
success = CiTestObj.Ping(HTML, CONTAINERS)
|
||||
success = CiTestObj.Ping(ctx, HTML)
|
||||
|
||||
elif action == 'Iperf' or action == 'Iperf2_Unidir':
|
||||
CiTestObj.iperf_args = test.findtext('iperf_args')
|
||||
@@ -284,9 +286,9 @@ def ExecuteActionWithParam(action):
|
||||
logging.error('test-case has wrong option ' + CiTestObj.iperf_options)
|
||||
CiTestObj.iperf_options = 'check'
|
||||
if action == 'Iperf':
|
||||
success = CiTestObj.Iperf(HTML, CONTAINERS)
|
||||
success = CiTestObj.Iperf(ctx, HTML)
|
||||
elif action == 'Iperf2_Unidir':
|
||||
success = CiTestObj.Iperf2_Unidir(HTML, CONTAINERS)
|
||||
success = CiTestObj.Iperf2_Unidir(ctx, HTML)
|
||||
|
||||
elif action == 'IdleSleep':
|
||||
st = test.findtext('idle_sleep_time_in_sec') or "5"
|
||||
@@ -295,12 +297,12 @@ def ExecuteActionWithParam(action):
|
||||
elif action == 'Deploy_Run_PhySim':
|
||||
oc_release = test.findtext('oc_release')
|
||||
svr_id = test.findtext('svr_id') or None
|
||||
success = CLUSTER.deploy_oc_physim(HTML, oc_release, svr_id)
|
||||
success = CLUSTER.deploy_oc_physim(ctx, HTML, oc_release, svr_id)
|
||||
|
||||
elif action == 'DeployCoreNetwork' or action == 'UndeployCoreNetwork':
|
||||
cn_id = test.findtext('cn_id')
|
||||
core_op = getattr(cls_oaicitest.OaiCiTest, action)
|
||||
success = core_op(cn_id, HTML)
|
||||
success = core_op(cn_id, ctx, HTML)
|
||||
|
||||
elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace":
|
||||
eNB_instance=test.findtext('eNB_instance')
|
||||
@@ -328,9 +330,9 @@ def ExecuteActionWithParam(action):
|
||||
CONTAINERS.num_attempts = int(test.findtext('num_attempts') or 1)
|
||||
CONTAINERS.deploymentTag = cls_containerize.CreateTag(CONTAINERS.ranCommitID, CONTAINERS.ranBranch, CONTAINERS.ranAllowMerge)
|
||||
if action == 'Deploy_Object':
|
||||
success = CONTAINERS.DeployObject(HTML)
|
||||
success = CONTAINERS.DeployObject(ctx, HTML)
|
||||
elif action == 'Undeploy_Object':
|
||||
success = CONTAINERS.UndeployObject(HTML, RAN)
|
||||
success = CONTAINERS.UndeployObject(ctx, HTML, RAN)
|
||||
elif action == 'Create_Workspace':
|
||||
if force_local:
|
||||
# Do not create a working directory when running locally. Current repo directory will be used
|
||||
@@ -341,13 +343,13 @@ def ExecuteActionWithParam(action):
|
||||
physim_options = test.findtext('physim_run_args')
|
||||
physim_test = test.findtext('physim_test')
|
||||
physim_threshold = test.findtext('physim_time_threshold') or 'inf'
|
||||
success = cls_native.Native.Run_Physim(HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold)
|
||||
success = cls_native.Native.Run_Physim(ctx, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold)
|
||||
|
||||
elif action == 'LicenceAndFormattingCheck':
|
||||
success = SCA.LicenceAndFormattingCheck(HTML)
|
||||
success = SCA.LicenceAndFormattingCheck(ctx, HTML)
|
||||
|
||||
elif action == 'Cppcheck_Analysis':
|
||||
success = SCA.CppCheckAnalysis(HTML)
|
||||
success = SCA.CppCheckAnalysis(ctx, HTML)
|
||||
|
||||
elif action == 'Push_Local_Registry':
|
||||
svr_id = test.findtext('svr_id')
|
||||
@@ -407,10 +409,11 @@ def test_in_list(test, list):
|
||||
def receive_signal(signum, frame):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def ShowTestID(ctx, desc):
|
||||
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
|
||||
logging.info(f'\u001B[1m Test ID: {ctx.test_id} (#{ctx.count}) \u001B[0m')
|
||||
logging.info(f'\u001B[1m {desc} \u001B[0m')
|
||||
logging.info(f'\u001B[1m----------------------------------------\u001B[0m')
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# MAIN PART
|
||||
@@ -466,15 +469,7 @@ if py_param_file_present == True:
|
||||
cwd = os.getcwd()
|
||||
|
||||
if re.match('^TerminateeNB$', mode, re.IGNORECASE):
|
||||
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
sys.exit('Insufficient Parameter')
|
||||
if RAN.eNBIPAddress == 'none':
|
||||
sys.exit(0)
|
||||
RAN.eNB_instance=0
|
||||
RAN.eNB_serverId[0]='0'
|
||||
RAN.eNBSourceCodePath='/tmp/'
|
||||
RAN.TerminateeNB(HTML)
|
||||
logging.warning("Option TerminateeNB ignored")
|
||||
elif re.match('^TerminateHSS$', mode, re.IGNORECASE):
|
||||
logging.warning("Option TerminateHSS ignored")
|
||||
elif re.match('^TerminateMME$', mode, re.IGNORECASE):
|
||||
@@ -482,12 +477,7 @@ elif re.match('^TerminateMME$', mode, re.IGNORECASE):
|
||||
elif re.match('^TerminateSPGW$', mode, re.IGNORECASE):
|
||||
logging.warning("Option TerminateSPGW ignored")
|
||||
elif re.match('^LogCollectBuild$', mode, re.IGNORECASE):
|
||||
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
|
||||
logging.warning("nothing to collect (eNBIPAddress/eNBUserName/eNBPassword/eNBSourceCodePath is '')")
|
||||
sys.exit(0)
|
||||
if RAN.eNBIPAddress == 'none':
|
||||
sys.exit(0)
|
||||
CiTestObj.LogCollectBuild(RAN)
|
||||
logging.warning("Option LogCollectBuild ignored")
|
||||
elif re.match('^LogCollecteNB$', mode, re.IGNORECASE):
|
||||
if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
@@ -565,6 +555,16 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
|
||||
else:
|
||||
xml_test_file = cwd + "/" + CiTestObj.testXMLfiles[0]
|
||||
|
||||
# directory where all log artifacts will be placed
|
||||
logPath = f"{cwd}/../cmake_targets/log/{CiTestObj.testXMLfiles[0].split('/')[-1]}.d"
|
||||
# we run from within ci-scripts, but the logPath is absolute, so replace
|
||||
# the ci-scripts/..; if it does not exist, nothing will happen
|
||||
logPath = logPath.replace(r'/ci-scripts/..', '')
|
||||
logging.info(f"placing all artifacts for this run in {logPath}/")
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f"rm -rf {logPath}")
|
||||
c.run(f"mkdir -p {logPath}")
|
||||
|
||||
xmlTree = ET.parse(xml_test_file)
|
||||
xmlRoot = xmlTree.getroot()
|
||||
|
||||
@@ -615,29 +615,32 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
|
||||
task_set_succeeded = True
|
||||
HTML.startTime=int(round(time.time() * 1000))
|
||||
|
||||
i = 0
|
||||
for test_case_id in todo_tests:
|
||||
for test in all_tests:
|
||||
id = test.get('id')
|
||||
if test_case_id != id:
|
||||
continue
|
||||
i += 1
|
||||
CiTestObj.testCase_id = id
|
||||
ctx = TestCaseCtx(i, int(id), logPath)
|
||||
HTML.testCase_id=CiTestObj.testCase_id
|
||||
CiTestObj.desc = test.findtext('desc')
|
||||
desc = test.findtext('desc')
|
||||
always_exec = test.findtext('always_exec') in ['True', 'true', 'Yes', 'yes']
|
||||
may_fail = test.findtext('may_fail') in ['True', 'true', 'Yes', 'yes']
|
||||
HTML.desc=CiTestObj.desc
|
||||
HTML.desc = desc
|
||||
action = test.findtext('class')
|
||||
if (CheckClassValidity(xml_class_list, action, id) == False):
|
||||
task_set_succeeded = False
|
||||
continue
|
||||
CiTestObj.ShowTestID()
|
||||
ShowTestID(ctx, desc)
|
||||
if not task_set_succeeded and not always_exec:
|
||||
msg = f"skipping test due to prior error"
|
||||
logging.warning(msg)
|
||||
HTML.CreateHtmlTestRowQueue(msg, "SKIP", [])
|
||||
break
|
||||
try:
|
||||
test_succeeded = ExecuteActionWithParam(action)
|
||||
test_succeeded = ExecuteActionWithParam(action, ctx)
|
||||
if not test_succeeded and may_fail:
|
||||
logging.warning(f"test ID {test_case_id} action {action} may or may not fail, proceeding despite error")
|
||||
elif not test_succeeded:
|
||||
|
||||
+18
-50
@@ -46,6 +46,7 @@ import cls_cmd
|
||||
import sshconnection as SSH
|
||||
import helpreadme as HELP
|
||||
import constants as CONST
|
||||
from cls_ci_helper import archiveArtifact
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Class Declaration
|
||||
@@ -124,7 +125,6 @@ class RANManagement():
|
||||
|
||||
self.testCase_id = HTML.testCase_id
|
||||
mySSH = SSH.SSHConnection()
|
||||
cwd = os.getcwd()
|
||||
|
||||
mySSH.open(lIpAddr, lUserName, lPassWord)
|
||||
mySSH.command('cd ' + lSourcePath, '\$', 5)
|
||||
@@ -249,42 +249,7 @@ class RANManagement():
|
||||
logging.debug('\u001B[1m Initialize eNB/gNB Completed\u001B[0m')
|
||||
return enbDidSync
|
||||
|
||||
def CheckeNBProcess(self, status_queue):
|
||||
# At least the instance 0 SHALL be on!
|
||||
if self.eNBstatuses[0] == 0:
|
||||
lIpAddr = self.eNBIPAddress
|
||||
lUserName = self.eNBUserName
|
||||
lPassWord = self.eNBPassword
|
||||
elif self.eNBstatuses[0] == 1:
|
||||
lIpAddr = self.eNB1IPAddress
|
||||
lUserName = self.eNB1UserName
|
||||
lPassWord = self.eNB1Password
|
||||
elif self.eNBstatuses[0] == 2:
|
||||
lIpAddr = self.eNB2IPAddress
|
||||
lUserName = self.eNB2UserName
|
||||
lPassWord = self.eNB2Password
|
||||
else:
|
||||
lIpAddr = self.eNBIPAddress
|
||||
lUserName = self.eNBUserName
|
||||
lPassWord = self.eNBPassword
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(lIpAddr, lUserName, lPassWord)
|
||||
if self.air_interface[self.eNB_instance] == '':
|
||||
pattern = 'softmodem'
|
||||
else:
|
||||
pattern = self.air_interface[self.eNB_instance]
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never ' + pattern + ' | grep -v grep', '\$', 5)
|
||||
result = re.search(pattern, mySSH.getBefore())
|
||||
success = result is not None
|
||||
if not success:
|
||||
logging.debug('\u001B[1;37;41m eNB Process Not Found! \u001B[0m')
|
||||
status_queue.put(CONST.ENB_PROCESS_FAILED)
|
||||
else:
|
||||
status_queue.put(CONST.ENB_PROCESS_OK)
|
||||
mySSH.close()
|
||||
return success
|
||||
|
||||
def TerminateeNB(self, HTML):
|
||||
def TerminateeNB(self, ctx, HTML):
|
||||
if self.eNB_serverId[self.eNB_instance] == '0':
|
||||
lIpAddr = self.eNBIPAddress
|
||||
lUserName = self.eNBUserName
|
||||
@@ -356,13 +321,11 @@ class RANManagement():
|
||||
fileToAnalyze = self.eNBLogFiles[int(self.eNB_instance)]
|
||||
self.eNBLogFiles[int(self.eNB_instance)] = ''
|
||||
if analyzeFile:
|
||||
#*stats.log files + pickle + png
|
||||
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*stats.log', '.')
|
||||
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.pickle', '.')
|
||||
mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.png', '.')
|
||||
#
|
||||
copyin_res = mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/' + fileToAnalyze, '.')
|
||||
if (copyin_res == -1):
|
||||
with cls_cmd.getConnection(lIpAddr) as cmd:
|
||||
file = archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/' + fileToAnalyze)
|
||||
archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/nrL1_stats.log')
|
||||
archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/nrMAC_stats.log')
|
||||
if file is None:
|
||||
logging.debug('\u001B[1;37;41m Could not copy ' + nodeB_prefix + 'NB logfile to analyze it! \u001B[0m')
|
||||
HTML.htmleNBFailureMsg='Could not copy ' + nodeB_prefix + 'NB logfile to analyze it!'
|
||||
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE)
|
||||
@@ -375,8 +338,8 @@ class RANManagement():
|
||||
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrL1_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
|
||||
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrMAC_stats.log', self.eNBSourceCodePath + '/cmake_targets/')
|
||||
mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './' + fileToAnalyze, self.eNBSourceCodePath + '/cmake_targets/')
|
||||
logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + fileToAnalyze)
|
||||
logStatus = self.AnalyzeLogFile_eNB(fileToAnalyze, HTML, self.ran_checkers)
|
||||
logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + file)
|
||||
logStatus = self.AnalyzeLogFile_eNB(file, HTML, self.ran_checkers)
|
||||
if (logStatus < 0):
|
||||
HTML.CreateHtmlTestRow('N/A', 'KO', logStatus)
|
||||
#display rt stats for gNB only
|
||||
@@ -734,11 +697,16 @@ class RANManagement():
|
||||
datalog_rt_stats = yaml.load(f,Loader=yaml.FullLoader)
|
||||
rt_keys = datalog_rt_stats['Ref'] #we use the keys from the Ref field
|
||||
|
||||
if os.path.isfile('./nrL1_stats.log') and os.path.isfile('./nrMAC_stats.log'):
|
||||
# nrL1_stats.log/nrMAC_stats.log should be in the same directory as main log file
|
||||
# currently the link is only implicit as below based on pattern matching
|
||||
# I will rework this to give the file explicitly
|
||||
l1_stats_fn = re.sub(r'enb_[0-9]+', 'nrL1_stats', eNBlogFile)
|
||||
mac_stats_fn = re.sub(r'enb_[0-9]+', 'nrMAC_stats', eNBlogFile)
|
||||
if os.path.isfile(l1_stats_fn) and os.path.isfile(mac_stats_fn):
|
||||
# don't use CI-nrL1_stats.log, as this will increase the processing time for
|
||||
# no reason, we just need the last occurence
|
||||
nrL1_stats = open('./nrL1_stats.log', 'r')
|
||||
nrMAC_stats = open('./nrMAC_stats.log', 'r')
|
||||
nrL1_stats = open(l1_stats_fn, 'r')
|
||||
nrMAC_stats = open(mac_stats_fn, 'r')
|
||||
for line in nrL1_stats.readlines():
|
||||
for k in rt_keys:
|
||||
result = re.search(k, line)
|
||||
@@ -758,7 +726,7 @@ class RANManagement():
|
||||
nrL1_stats.close()
|
||||
nrMAC_stats.close()
|
||||
else:
|
||||
logging.debug("NR Stats files for RT analysis not found")
|
||||
logging.debug(f"NR Stats files for RT analysis not found: {l1_stats_fn}, {mac_stats_fn}")
|
||||
|
||||
#stdout log file and stat log files analysis completed
|
||||
logging.debug(' File analysis (stdout, stats) completed')
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
test:
|
||||
Host: localhost
|
||||
InitScript: ""
|
||||
TermScript: ""
|
||||
AttachScript: "true"
|
||||
DetachScript: "true"
|
||||
NetworkScript: ip a show dev lo
|
||||
IF: lo
|
||||
MTU: 65536
|
||||
|
||||
test-fail:
|
||||
Host: localhost
|
||||
InitScript: ""
|
||||
TermScript: ""
|
||||
AttachScript: "false"
|
||||
DetachScript: "true"
|
||||
NetworkScript: ip a show dev TESTDEVICEDOESNOTEXIST
|
||||
IF: TESTDEVICEDOESNOTEXIST
|
||||
MTU: 123
|
||||
|
||||
test-trace:
|
||||
Host: localhost
|
||||
InitScript: ""
|
||||
TermScript: ""
|
||||
AttachScript: "true"
|
||||
DetachScript: "true"
|
||||
Tracing:
|
||||
# tttf: temp-trace-test-file
|
||||
Start: "rm -f /tmp/tttf; for i in {1..8}; do echo $i >> /tmp/tttf; sleep 1; done &"
|
||||
Stop: "while [[ $(wc -l /tmp/tttf) < 4 ]]; do sleep 1; done"
|
||||
Collect: "cp /tmp/tttf %%log_dir%%"
|
||||
NetworkScript: ip a show dev lo
|
||||
IF: lo
|
||||
MTU: 65536
|
||||
@@ -6,14 +6,22 @@ logging.basicConfig(
|
||||
format="[%(asctime)s] %(levelname)8s: %(message)s"
|
||||
)
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import unittest
|
||||
|
||||
sys.path.append('./') # to find OAI imports below
|
||||
import cls_corenetwork
|
||||
from cls_ci_helper import TestCaseCtx
|
||||
import cls_cmd
|
||||
|
||||
class TestCoreNetwork(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
|
||||
def tearDown(self):
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f'rm -rf {self.ctx.logPath}')
|
||||
|
||||
def test_simple_core(self):
|
||||
c = cls_corenetwork.CoreNetwork("test", filename="tests/config/test_core_infra.yaml")
|
||||
success, output = c.deploy()
|
||||
@@ -22,14 +30,14 @@ class TestCoreNetwork(unittest.TestCase):
|
||||
# should not have it set in config
|
||||
self.assertTrue(c.runIperf3Server())
|
||||
self.assertEqual(c.getIP(), "127.0.0.1")
|
||||
log_files, output = c.undeploy(log_dir="/tmp")
|
||||
log_files, output = c.undeploy(self.ctx)
|
||||
self.assertEqual(output, "undeploy")
|
||||
with cls_cmd.LocalCmd() as cmd:
|
||||
# there must be one log file for this (test) core
|
||||
self.assertEqual(len(log_files), 1)
|
||||
# test core writes to %%log_dir%%/logs, which expands to /tmp/logs
|
||||
# undeploy uses archiveArtifact(), which writes to {prefix}-logs
|
||||
l = log_files[0]
|
||||
self.assertEqual(l, "/tmp/logs")
|
||||
self.assertEqual(l, f"{self.ctx.baseFilename()}-logs")
|
||||
ret = cmd.run(f"cat {l}")
|
||||
self.assertEqual(ret.returncode, 0) # command must succeed
|
||||
self.assertEqual(ret.stdout, "logs") # output should be "logs"
|
||||
@@ -39,14 +47,14 @@ class TestCoreNetwork(unittest.TestCase):
|
||||
success, _ = c.deploy()
|
||||
self.assertTrue(success)
|
||||
self.assertFalse(c.runIperf3Server())
|
||||
c.undeploy(log_dir="/tmp")
|
||||
c.undeploy(None)
|
||||
|
||||
def test_core_fail(self):
|
||||
c = cls_corenetwork.CoreNetwork("test_fail", filename="tests/config/test_core_infra.yaml")
|
||||
success, _ = c.deploy()
|
||||
self.assertFalse(success)
|
||||
# undeployment should still work
|
||||
c.undeploy(log_dir="/tmp")
|
||||
c.undeploy(None)
|
||||
|
||||
def test_core_script(self):
|
||||
c = cls_corenetwork.CoreNetwork("test_script", filename="tests/config/test_core_infra.yaml")
|
||||
|
||||
@@ -6,6 +6,7 @@ logging.basicConfig(
|
||||
format="[%(asctime)s] %(levelname)8s: %(message)s"
|
||||
)
|
||||
import os
|
||||
import tempfile
|
||||
os.system(f'rm -rf cmake_targets')
|
||||
os.system(f'mkdir -p cmake_targets/log')
|
||||
import unittest
|
||||
@@ -14,6 +15,7 @@ sys.path.append('./') # to find OAI imports below
|
||||
import cls_oai_html
|
||||
import cls_oaicitest
|
||||
import cls_containerize
|
||||
from cls_ci_helper import TestCaseCtx
|
||||
import ran
|
||||
import cls_cmd
|
||||
|
||||
@@ -48,12 +50,16 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
self.cont.eNBPassword = None
|
||||
self.cont.eNBSourceCodePath = os.getcwd()
|
||||
self.cont.num_attempts = 3
|
||||
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
|
||||
def tearDown(self):
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f'rm -rf {self.ctx.logPath}')
|
||||
|
||||
def test_deploy(self):
|
||||
self.cont.yamlPath[0] = 'tests/simple-dep/'
|
||||
self.cont.deploymentTag = "noble"
|
||||
deploy = self.cont.DeployObject(self.html)
|
||||
undeploy = self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy = self.cont.DeployObject(self.ctx, self.html)
|
||||
undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertTrue(deploy)
|
||||
self.assertTrue(undeploy)
|
||||
|
||||
@@ -61,8 +67,8 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
# fails reliably
|
||||
old = self.cont.yamlPath
|
||||
self.cont.yamlPath[0] = 'tests/simple-fail/'
|
||||
deploy = self.cont.DeployObject(self.html)
|
||||
self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy = self.cont.DeployObject(self.ctx, self.html)
|
||||
self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertFalse(deploy)
|
||||
self.cont.yamlPath = old
|
||||
|
||||
@@ -70,8 +76,8 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
# fails reliably
|
||||
old = self.cont.yamlPath
|
||||
self.cont.yamlPath[0] = 'tests/simple-fail-2svc/'
|
||||
deploy = self.cont.DeployObject(self.html)
|
||||
self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy = self.cont.DeployObject(self.ctx, self.html)
|
||||
self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertFalse(deploy)
|
||||
self.cont.yamlPath = old
|
||||
|
||||
@@ -79,8 +85,8 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
|
||||
self.cont.services[0] = "oai-gnb"
|
||||
self.cont.deploymentTag = 'develop-12345678'
|
||||
deploy = self.cont.DeployObject(self.html)
|
||||
undeploy = self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy = self.cont.DeployObject(self.ctx, self.html)
|
||||
undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertTrue(deploy)
|
||||
self.assertTrue(undeploy)
|
||||
|
||||
@@ -88,8 +94,8 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
|
||||
self.cont.services[0] = "oai-gnb oai-nr-ue"
|
||||
self.cont.deploymentTag = 'develop-12345678'
|
||||
deploy = self.cont.DeployObject(self.html)
|
||||
undeploy = self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy = self.cont.DeployObject(self.ctx, self.html)
|
||||
undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertTrue(deploy)
|
||||
self.assertTrue(undeploy)
|
||||
|
||||
@@ -97,10 +103,10 @@ class TestDeploymentMethods(unittest.TestCase):
|
||||
self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora'
|
||||
self.cont.services[0] = "oai-gnb"
|
||||
self.cont.deploymentTag = 'develop-12345678'
|
||||
deploy1 = self.cont.DeployObject(self.html)
|
||||
deploy1 = self.cont.DeployObject(self.ctx, self.html)
|
||||
self.cont.services[0] = "oai-nr-ue"
|
||||
deploy2 = self.cont.DeployObject(self.html)
|
||||
undeploy = self.cont.UndeployObject(self.html, self.ran)
|
||||
deploy2 = self.cont.DeployObject(self.ctx, self.html)
|
||||
undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran)
|
||||
self.assertTrue(deploy1)
|
||||
self.assertTrue(deploy2)
|
||||
self.assertTrue(undeploy)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Sender Bitrate : 0.52 Mbps
|
||||
Receiver Bitrate: 0.52 Mbps (too low! < 99%)
|
||||
Sender Bitrate : 524.00 Kbps
|
||||
Receiver Bitrate: 524.00 Kbps (52.40%) (too low! < 99%)
|
||||
Jitter : 0.026 ms
|
||||
Packet Loss : 10 % (too high! > 0%)
|
||||
Packet Loss : 10% (too high! > 0%)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Sender Bitrate : 0.00 Mbps
|
||||
Receiver Bitrate: 3.98 Mbps (too low! < 99%)
|
||||
Sender Bitrate : 0.00 bps
|
||||
Receiver Bitrate: 3.98 Mbps (4.97%) (too low! < 99%)
|
||||
Jitter : 3.847 ms
|
||||
Packet Loss : 0.038 % (too high! > 0%)
|
||||
Packet Loss : 0.038% (too high! > 0%)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Sender Bitrate : 1.03 Mbps
|
||||
Receiver Bitrate: 0.00 Mbps (too low! < 99%)
|
||||
Sender Bitrate : 1.03 Mbps
|
||||
Receiver Bitrate: 0.00 bps (0.00%) (too low! < 99%)
|
||||
Jitter : 0.000 ms
|
||||
Packet Loss : 0 %
|
||||
Packet Loss : 0%
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Sender Bitrate : 1.00 Mbps
|
||||
Receiver Bitrate: 1.00 Mbps (99.60 %)
|
||||
Sender Bitrate : 996.00 Kbps
|
||||
Receiver Bitrate: 996.00 Kbps (99.60%)
|
||||
Jitter : 0.029 ms
|
||||
Packet Loss : 0 %
|
||||
Packet Loss : 0%
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Sender Bitrate : 4.67 Mbps
|
||||
Receiver Bitrate: 4.67 Mbps (100.00 %)
|
||||
Sender Bitrate : 4.67 Mbps
|
||||
Receiver Bitrate: 4.67 Mbps (100.00%)
|
||||
Jitter : 3.413 ms
|
||||
Packet Loss : 0 %
|
||||
Packet Loss : 0%
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import sys
|
||||
import logging
|
||||
import tempfile
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
stream=sys.stdout,
|
||||
format="[%(asctime)s] %(levelname)8s: %(message)s"
|
||||
)
|
||||
import os
|
||||
|
||||
import unittest
|
||||
|
||||
sys.path.append('./') # to find OAI imports below
|
||||
import cls_module
|
||||
from cls_ci_helper import TestCaseCtx
|
||||
import cls_cmd
|
||||
|
||||
class TestModule(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
|
||||
def tearDown(self):
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f'rm -rf {self.ctx.logPath}')
|
||||
|
||||
def test_simple_module(self):
|
||||
c = cls_module.Module_UE("test", filename="tests/config/test_module_infra.yaml")
|
||||
self.assertFalse(c.trace)
|
||||
success = c.initialize()
|
||||
self.assertTrue(success)
|
||||
ip = c.attach()
|
||||
self.assertEqual(ip, "127.0.0.1")
|
||||
self.assertTrue(c.checkMTU())
|
||||
c.detach()
|
||||
logs = c.terminate()
|
||||
self.assertEqual(logs, None) # no tracing
|
||||
|
||||
@unittest.skip("this test takes long: it verifies the UE cannot attach")
|
||||
def test_simple_fail(self):
|
||||
c = cls_module.Module_UE("test-fail", filename="tests/config/test_module_infra.yaml")
|
||||
success = c.initialize()
|
||||
self.assertTrue(success)
|
||||
ip = c.attach()
|
||||
self.assertEqual(ip, None)
|
||||
self.assertFalse(c.checkMTU())
|
||||
c.detach()
|
||||
logs = c.terminate()
|
||||
self.assertEqual(logs, None) # no tracing
|
||||
|
||||
def test_simple_trace(self):
|
||||
c = cls_module.Module_UE("test-trace", filename="tests/config/test_module_infra.yaml")
|
||||
self.assertTrue(c.trace)
|
||||
success = c.initialize()
|
||||
self.assertTrue(success)
|
||||
ip = c.attach()
|
||||
self.assertEqual(ip, "127.0.0.1")
|
||||
self.assertTrue(c.checkMTU())
|
||||
c.detach()
|
||||
tmp = tempfile.mkdtemp()
|
||||
log_file = c.terminate(self.ctx)
|
||||
# undeploy uses archiveArtifact(), which writes to {prefix}-logs
|
||||
self.assertEqual(log_file, [f"{self.ctx.baseFilename()}-tttf"]) # matches test-trace UE collection file
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -9,11 +9,13 @@ import os
|
||||
os.system(f'rm -rf cmake_targets')
|
||||
os.system(f'mkdir -p cmake_targets/log')
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
sys.path.append('./') # to find OAI imports below
|
||||
import cls_oai_html
|
||||
from cls_ci_helper import TestCaseCtx
|
||||
import cls_oaicitest
|
||||
import cls_containerize
|
||||
import cls_cmd
|
||||
|
||||
class TestPingIperf(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -22,7 +24,10 @@ class TestPingIperf(unittest.TestCase):
|
||||
self.ci = cls_oaicitest.OaiCiTest()
|
||||
self.ci.ue_ids = ["test"]
|
||||
self.ci.nodes = ["localhost"]
|
||||
self.cont = cls_containerize.Containerize()
|
||||
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
|
||||
def tearDown(self):
|
||||
with cls_cmd.LocalCmd() as c:
|
||||
c.run(f'rm -rf {self.ctx.logPath}')
|
||||
|
||||
def test_ping(self):
|
||||
self.ci.ping_args = "-c3"
|
||||
@@ -30,7 +35,7 @@ class TestPingIperf(unittest.TestCase):
|
||||
self.ci.svr_id = "test"
|
||||
infra_file = "tests/config/infra_ping_iperf.yaml"
|
||||
# TODO Should need nothing but options and UE(s) to use
|
||||
success = self.ci.Ping(self.html, self.cont, infra_file=infra_file)
|
||||
success = self.ci.Ping(self.ctx, self.html, infra_file=infra_file)
|
||||
self.assertTrue(success)
|
||||
|
||||
def test_iperf(self):
|
||||
@@ -45,7 +50,7 @@ class TestPingIperf(unittest.TestCase):
|
||||
self.ci.iperf_profile = "balanced"
|
||||
infra_file = "tests/config/infra_ping_iperf.yaml"
|
||||
# TODO Should need nothing but options and UE(s) to use
|
||||
success = self.ci.Iperf(self.html, self.cont, infra_file=infra_file)
|
||||
success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file)
|
||||
self.assertTrue(success)
|
||||
|
||||
def test_iperf2_unidir(self):
|
||||
@@ -57,7 +62,22 @@ class TestPingIperf(unittest.TestCase):
|
||||
self.ci.iperf_profile = "balanced"
|
||||
infra_file = "tests/config/infra_ping_iperf.yaml"
|
||||
# TODO Should need nothing but options and UE(s) to use
|
||||
success = self.ci.Iperf2_Unidir(self.html, self.cont, infra_file=infra_file)
|
||||
success = self.ci.Iperf2_Unidir(self.ctx, self.html, infra_file=infra_file)
|
||||
self.assertTrue(success)
|
||||
|
||||
def test_iperf_highrate(self):
|
||||
# note: needs to be five seconds because Iperf() adds -O 3, so if it is
|
||||
# too short, the server is terminated before the client loaded
|
||||
# everything
|
||||
self.ci.iperf_args = "-u -t 5 -b 1000M -R -O 0"
|
||||
self.ci.svr_id = "test"
|
||||
self.ci.svr_node = "localhost"
|
||||
self.ci.iperf_packetloss_threshold = "0"
|
||||
self.ci.iperf_bitrate_threshold = "0"
|
||||
self.ci.iperf_profile = "balanced"
|
||||
infra_file = "tests/config/infra_ping_iperf.yaml"
|
||||
# TODO Should need nothing but options and UE(s) to use
|
||||
success = self.ci.Iperf(self.html, self.cont, infra_file=infra_file)
|
||||
self.assertTrue(success)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<htmlTabIcon>wrench</htmlTabIcon>
|
||||
<TestCaseRequestedList>
|
||||
111111
|
||||
100001
|
||||
800813
|
||||
000001
|
||||
000002
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<htmlTabIcon>wrench</htmlTabIcon>
|
||||
<TestCaseRequestedList>
|
||||
111111
|
||||
100001
|
||||
800813
|
||||
000001
|
||||
000002
|
||||
|
||||
@@ -40,13 +40,14 @@
|
||||
<mode>TesteNB</mode>
|
||||
<class>Build_eNB</class>
|
||||
<desc>Build gNB (USRP)</desc>
|
||||
<Build_eNB_args>--gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_t2 --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c</Build_eNB_args>
|
||||
<Build_eNB_args>--gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_aal --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c</Build_eNB_args>
|
||||
<forced_workspace_cleanup>True</forced_workspace_cleanup>
|
||||
</testCase>
|
||||
|
||||
<testCase id="100000">
|
||||
<class>Custom_Command</class>
|
||||
<desc>Reboot USRP</desc>
|
||||
<may_fail>true</may_fail>
|
||||
<node>caracal</node>
|
||||
<command>ssh root@192.168.20.2 reboot</command>
|
||||
</testCase>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>100</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010121">
|
||||
@@ -80,7 +80,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>150</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010131">
|
||||
@@ -98,7 +98,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>250</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010211">
|
||||
@@ -116,7 +116,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>150</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010221">
|
||||
@@ -134,7 +134,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>350</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010231">
|
||||
@@ -152,7 +152,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>550</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010311">
|
||||
@@ -170,7 +170,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>250</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010321">
|
||||
@@ -188,7 +188,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>650</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="010331">
|
||||
@@ -206,7 +206,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_ulsim</physim_test>
|
||||
<physim_time_threshold>1100</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
</testCaseList>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>100</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000121">
|
||||
@@ -81,7 +81,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>100</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000131">
|
||||
@@ -99,7 +99,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>200</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000211">
|
||||
@@ -117,7 +117,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>150</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000221">
|
||||
@@ -135,7 +135,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>250</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000231">
|
||||
@@ -153,7 +153,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>400</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000311">
|
||||
@@ -171,7 +171,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>200</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000321">
|
||||
@@ -189,7 +189,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>500</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000331">
|
||||
@@ -207,7 +207,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>500</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000411">
|
||||
@@ -225,7 +225,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>200</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000421">
|
||||
@@ -243,7 +243,7 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>300</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000431">
|
||||
@@ -261,6 +261,6 @@
|
||||
<always_exec>true</always_exec>
|
||||
<physim_test>nr_dlsim</physim_test>
|
||||
<physim_time_threshold>450</physim_time_threshold>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12</physim_run_args>
|
||||
<physim_run_args>-n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1</physim_run_args>
|
||||
</testCase>
|
||||
</testCaseList>
|
||||
|
||||
@@ -45,7 +45,7 @@ BUILD_DOXYGEN=0
|
||||
DISABLE_HARDWARE_DEPENDENCY="False"
|
||||
CMAKE_BUILD_TYPE="RelWithDebInfo"
|
||||
CMAKE_CMD="$CMAKE"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 ldpc_xdma websrv oai_iqplayer imscope imscope_record"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
|
||||
TARGET_LIST=""
|
||||
BUILD_TOOL_OPT="-j$(nproc)"
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# - Try to find SCTP library and headers
|
||||
# Once done, this will define
|
||||
#
|
||||
# SCTP_FOUND - system has SCTP
|
||||
# SCTP_INCLUDE_DIR - the SCTP include directories
|
||||
# SCTP_LIBRARIES - link these to use SCTP
|
||||
|
||||
if (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES)
|
||||
set(SCTP_FIND_QUIETLY TRUE)
|
||||
endif (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES)
|
||||
|
||||
# Include dir
|
||||
find_path(SCTP_INCLUDE_DIR
|
||||
NAMES netinet/sctp.h
|
||||
)
|
||||
|
||||
# Library
|
||||
find_library(SCTP_LIBRARY
|
||||
NAMES sctp
|
||||
)
|
||||
|
||||
# Set the include dir variables and the libraries and let libfind_process do the rest.
|
||||
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
|
||||
#set(SCTP_PROCESS_INCLUDES SCTP_INCLUDE_DIR)
|
||||
#set(SCTP_PROCESS_LIBS SCTP_LIBRARY)
|
||||
#libfind_process(SCTP)
|
||||
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set SCTP_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARY SCTP_INCLUDE_DIR)
|
||||
|
||||
# If we successfully found the sctp library then add the library to the
|
||||
# SCTP_LIBRARIES cmake variable otherwise set SCTP_LIBRARIES to nothing.
|
||||
IF(SCTP_FOUND)
|
||||
SET( SCTP_LIBRARIES ${SCTP_LIBRARY} )
|
||||
ELSE(SCTP_FOUND)
|
||||
SET( SCTP_LIBRARIES )
|
||||
ENDIF(SCTP_FOUND)
|
||||
|
||||
|
||||
# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables
|
||||
# only show up under the advanced options in the gui cmake applications.
|
||||
MARK_AS_ADVANCED( SCTP_LIBRARY SCTP_INCLUDE_DIR )
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# - Try to find SCTP library and headers
|
||||
#
|
||||
# Imported Targets
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# This module provides the following imported targets, if found:
|
||||
#
|
||||
# ``sctp::sctp``
|
||||
# The SCTP library
|
||||
#
|
||||
# Result Variables
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# this will define
|
||||
#
|
||||
# sctp_FOUND - system has SCTP
|
||||
# sctp_INCLUDE_DIRS - the SCTP include directories
|
||||
# sctp_LIBRARIES - link these to use SCTP
|
||||
|
||||
# Include dir
|
||||
find_path(sctp_INCLUDE_DIR
|
||||
NAMES netinet/sctp.h
|
||||
)
|
||||
|
||||
# Library
|
||||
find_library(sctp_LIBRARY
|
||||
NAMES sctp
|
||||
)
|
||||
|
||||
# Set the include dir variables and the libraries and let libfind_process do the rest.
|
||||
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
|
||||
#set(sctp_PROCESS_INCLUDES sctp_INCLUDE_DIR)
|
||||
#set(sctp_PROCESS_LIBS sctp_LIBRARY)
|
||||
#libfind_process(sctp)
|
||||
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set sctp_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(sctp
|
||||
FOUND_VAR sctp_FOUND
|
||||
REQUIRED_VARS
|
||||
sctp_LIBRARY
|
||||
sctp_INCLUDE_DIR
|
||||
)
|
||||
|
||||
# If we successfully found the sctp library then add the library to the
|
||||
# sctp_LIBRARIES cmake variable otherwise set sctp_LIBRARIES to nothing.
|
||||
if(sctp_FOUND)
|
||||
set(sctp_LIBRARIES ${sctp_LIBRARY})
|
||||
set(sctp_INCLUDE_DIRS ${sctp_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(sctp_FOUND AND NOT TARGET sctp::sctp)
|
||||
add_library(sctp::sctp UNKNOWN IMPORTED)
|
||||
set_target_properties(sctp::sctp PROPERTIES
|
||||
IMPORTED_LOCATION "${sctp_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${sctp_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables
|
||||
# only show up under the advanced options in the gui cmake applications.
|
||||
mark_as_advanced(SCTP_LIBRARY SCTP_INCLUDE_DIR)
|
||||
@@ -46,7 +46,6 @@ struct gNB_RRC_INST_s;
|
||||
struct eNB_MAC_INST_s;
|
||||
struct eNB_MAC_INST_NB_IoT_s;
|
||||
struct gNB_MAC_INST_s;
|
||||
struct gtpv1u_data_s;
|
||||
struct RU_t_s;
|
||||
|
||||
typedef struct {
|
||||
@@ -100,8 +99,6 @@ typedef struct {
|
||||
struct eNB_MAC_INST_NB_IoT_s **nb_iot_mac;
|
||||
/// NR MAC context variables
|
||||
struct gNB_MAC_INST_s **nrmac;
|
||||
/// GTPu descriptor
|
||||
struct gtpv1u_data_s *gtpv1u_data_g;
|
||||
/// RU descriptors. These describe what each radio unit is supposed to do and contain the necessary functions for fronthaul interfaces
|
||||
struct RU_t_s **ru;
|
||||
/// Mask to indicate fronthaul setup status of RU (hard-limit to 64 RUs)
|
||||
|
||||
@@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I. -I..
|
||||
|
||||
LIBS=-lX11 -lm -lpng -lXft
|
||||
|
||||
all: dump_nack_signal time_meas timeplot multi-rru-clean
|
||||
all: dump_nack_signal time_meas timeplot multi-rru-clean ant0 plot-ofdm
|
||||
|
||||
dump_nack_signal: ../utils.o ../database.o ../configuration.o ../event.o \
|
||||
dump_nack_signal.o
|
||||
@@ -19,10 +19,25 @@ timplot: timeplot.o
|
||||
multi-rru-clean: ../utils.o ../database.o ../event.o ../configuration.o multi-rru-clean.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
ant0: ant0.o ../utils.o ../database.o ../configuration.o ../event.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
#ant0 needs good compilation, conversion to float is heavy
|
||||
ant0.o: ant0.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3
|
||||
|
||||
plot-ofdm: plot-ofdm.o ../utils.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lfftw3f
|
||||
|
||||
#plot-ofdm needs good compilation
|
||||
plot-ofdm.o: plot-ofdm.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3
|
||||
|
||||
.PHONY: all
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean
|
||||
rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean \
|
||||
ant0 plot-ofdm
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "database.h"
|
||||
//#include "utils.h"
|
||||
#include "configuration.h"
|
||||
#include "event.h"
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"options:\n"
|
||||
" -d <database file> this option is mandatory\n"
|
||||
" -o <output file> this option is mandatory (use - to dump to stdout)\n"
|
||||
" -ip <host> connect to given IP address (default %s)\n"
|
||||
" -p <port> connect to given port (default %d)\n"
|
||||
" -n <max> save maximum 'max' IQ points (may be a bit more)\n"
|
||||
" -nosig don't intercept ctrl+c, ctrl+z (it may mess up when piping to another tool)\n"
|
||||
" -e <event name> trace given event (default 'USRP_TX_ANT0')\n"
|
||||
" -b <buffer name> trace given buffer (default 'data')\n"
|
||||
" -no-timestamp don't use 'timestamp' to fill missing transmission\n"
|
||||
" -scale <scale value> divide 16bits input by given value for float output (default 32768.)\n"
|
||||
" -s16 output short instead of float\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
volatile int run = 1;
|
||||
|
||||
static int socket = -1;
|
||||
|
||||
void force_stop(int x)
|
||||
{
|
||||
fprintf(stderr, "\ngently quit...\n");
|
||||
close(socket);
|
||||
socket = -1;
|
||||
run = 0;
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
char *database_filename = NULL;
|
||||
char *output_filename = NULL;
|
||||
FILE *out;
|
||||
void *database;
|
||||
char *ip = DEFAULT_REMOTE_IP;
|
||||
int port = DEFAULT_REMOTE_PORT;
|
||||
int *is_on;
|
||||
int number_of_events;
|
||||
int i;
|
||||
char mt;
|
||||
long max = -1;
|
||||
long dumped = 0;
|
||||
int nosig = 0;
|
||||
char *event_name = "USRP_TX_ANT0";
|
||||
char *buffer_name = "data";
|
||||
int no_timestamp = 0;
|
||||
float scale = 32768;
|
||||
int do_float = 1;
|
||||
|
||||
/* write on a socket fails if the other end is closed and we get SIGPIPE */
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
|
||||
if (!strcmp(v[i], "-d")) { if (i > n-2) usage(); database_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-o")) { if (i > n-2) usage(); output_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-p")) { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-n")) { if (i > n-2) usage(); max = atol(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-nosig")) { nosig = 1; continue; }
|
||||
if (!strcmp(v[i], "-e")) { if (i > n-2) usage(); event_name = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-b")) { if (i > n-2) usage(); buffer_name = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-no-timestamp")) { no_timestamp = 1; continue; }
|
||||
if (!strcmp(v[i], "-scale")) { if (i > n-2) usage(); scale = atof(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-s16")) { do_float = 0; continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
if (database_filename == NULL) {
|
||||
fprintf(stderr, "ERROR: provide a database file (-d)\n");
|
||||
exit(1);
|
||||
}
|
||||
if (output_filename == NULL) {
|
||||
fprintf(stderr, "ERROR: provide an output file (-o)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
database = parse_database(database_filename);
|
||||
load_config_file(database_filename);
|
||||
|
||||
/* get the event and fields from database */
|
||||
int tx_ant0_id = event_id_from_name(database, event_name);
|
||||
database_event_format f = get_format(database, tx_ant0_id);
|
||||
|
||||
#define G(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) abort(); \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
int timestamp_id = -1;
|
||||
int data_id = -1;
|
||||
for (i = 0; i < f.count; i++) {
|
||||
/* there is a typo in T_messages.txt, "timestap" instead of "timestamp" */
|
||||
if (!no_timestamp)
|
||||
G("timestap", "int", timestamp_id);
|
||||
G(buffer_name, "buffer", data_id);
|
||||
}
|
||||
if ((!no_timestamp && timestamp_id == -1) || data_id == -1) {
|
||||
if (data_id == -1)
|
||||
fprintf(stderr, "fatal: field '%s' of event '%s' does not exist\n",
|
||||
buffer_name, event_name);
|
||||
if (!no_timestamp && timestamp_id == -1)
|
||||
fprintf(stderr, "fatal: field 'timestamp' of event '%s' does not exist\n",
|
||||
event_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
number_of_events = number_of_ids(database);
|
||||
is_on = calloc(number_of_events, sizeof(int));
|
||||
if (is_on == NULL) abort();
|
||||
|
||||
on_off(database, event_name, is_on, 1);
|
||||
|
||||
/* connect to tracee */
|
||||
socket = connect_to(ip, port);
|
||||
|
||||
/* activate trace */
|
||||
mt = 1;
|
||||
if (socket_send(socket, &mt, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
|
||||
abort();
|
||||
|
||||
/* - means to output to stdout */
|
||||
if (!strcmp(output_filename, "-")) {
|
||||
out = stdout;
|
||||
} else {
|
||||
out = fopen(output_filename, "w");
|
||||
if (out == NULL) {
|
||||
perror(output_filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nosig) {
|
||||
/* exit on ctrl+c and ctrl+z */
|
||||
if (signal(SIGQUIT, force_stop) == SIG_ERR) abort();
|
||||
if (signal(SIGINT, force_stop) == SIG_ERR) abort();
|
||||
if (signal(SIGTSTP, force_stop) == SIG_ERR) abort();
|
||||
}
|
||||
|
||||
OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL};
|
||||
|
||||
/* read messages */
|
||||
unsigned int last_timestamp = 0;
|
||||
int last_count = 0;
|
||||
int first = 1;
|
||||
|
||||
/* stop if max reached */
|
||||
while ((max == -1 || dumped < max) && run) {
|
||||
event e;
|
||||
e = get_event(socket, &ebuf, database);
|
||||
|
||||
if (e.type == -1) break;
|
||||
if (e.type != tx_ant0_id) continue;
|
||||
|
||||
unsigned int timestamp = 0;
|
||||
if (!no_timestamp)
|
||||
timestamp = e.e[timestamp_id].i;
|
||||
int count = e.e[data_id].bsize / 4;
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "timestamp %u buffer %p count %d not transmitted %d delta %d\n",
|
||||
timestamp,
|
||||
e.e[data_id].b,
|
||||
count,
|
||||
timestamp - (last_timestamp + last_count),
|
||||
timestamp - last_timestamp);
|
||||
#endif
|
||||
|
||||
/* rfsimulator may send 1 sample with old time when a new client connects
|
||||
* let's reject this packet
|
||||
*/
|
||||
if (!first && timestamp < last_timestamp + last_count) {
|
||||
/* more than 1 sample is not accepted */
|
||||
if (count != 1) {
|
||||
fprintf(stderr, "fatal: past transmission detected (%d samples)\n", count);
|
||||
abort();
|
||||
}
|
||||
fprintf(stderr, "past transmission detected (%d samples), ignore\n", count);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* insert missing samples */
|
||||
if (!no_timestamp && !first) {
|
||||
int missing = timestamp - (last_timestamp + last_count);
|
||||
if (missing < 0) abort();
|
||||
while (missing) {
|
||||
/* limit size of array miss[], it's allocated on the stack */
|
||||
int count = missing > 65536 ? 65536 : missing;
|
||||
float miss[count * 2];
|
||||
memset(miss, 0, count * 2 * sizeof(float));
|
||||
|
||||
if (fwrite(miss, count * 2 * sizeof(float), 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
close(socket);
|
||||
exit(1);
|
||||
}
|
||||
dumped += count;
|
||||
missing -= count;
|
||||
}
|
||||
}
|
||||
|
||||
first = 0;
|
||||
|
||||
if (do_float) {
|
||||
/* transform to float */
|
||||
float iq[count * 2];
|
||||
short *iq_short = (short *)e.e[data_id].b;
|
||||
for (int i = 0; i < count * 2; i++)
|
||||
iq[i] = iq_short[i] / scale;
|
||||
|
||||
/* save */
|
||||
if (fwrite(iq, count * 2 * sizeof(float), 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
/* save directly as short */
|
||||
if (fwrite(e.e[data_id].b, count * 2 * 2, 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
dumped += count;
|
||||
|
||||
last_timestamp = timestamp;
|
||||
last_count = count;
|
||||
}
|
||||
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
close(socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <complex.h>
|
||||
#include <fftw3.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
const int width = 14 * 20 * 4;
|
||||
int height;
|
||||
|
||||
/* when vzoom = 1 each pixel represents 1 RE
|
||||
* when 2, it is max of 2 vertical REs
|
||||
* useful if height of plot is to big
|
||||
* (with 106 RB we already reach 106*12 = 1272 pixels, so with even bigger
|
||||
* bandwidths...)
|
||||
*/
|
||||
int vzoom = 1;
|
||||
/* when hzoom = 1 each pixel represents 1 RE
|
||||
* when 2, it is max of 2 horizontal REs
|
||||
* useful to slow down display
|
||||
*/
|
||||
int hzoom = 1;
|
||||
/* intensity is to scale the input
|
||||
* normally, as float, cabs(input) is in [0..1]
|
||||
* to get pixel color we *255
|
||||
* but maybe it's too intense, or not enough,
|
||||
* so let's have a control the user can set at will
|
||||
*/
|
||||
float intensity = 255;
|
||||
|
||||
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&m);
|
||||
}
|
||||
|
||||
void unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&m);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Display *d;
|
||||
Window w;
|
||||
XImage *img;
|
||||
unsigned char *img_data;
|
||||
int img_pos;
|
||||
} gui_t;
|
||||
|
||||
/* delay management begin */
|
||||
|
||||
long double frame_time;
|
||||
|
||||
void time_start(void)
|
||||
{
|
||||
struct timespec l;
|
||||
if (clock_gettime(CLOCK_REALTIME, &l)) abort();
|
||||
frame_time = l.tv_sec * 1000000000. + l.tv_nsec;
|
||||
}
|
||||
|
||||
void delay(int fps)
|
||||
{
|
||||
struct timespec t;
|
||||
struct timespec delay;
|
||||
long double cur_time;
|
||||
|
||||
frame_time += 1000000000. / fps;
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &t)) abort();
|
||||
|
||||
cur_time = t.tv_sec * 1000000000. + t.tv_nsec;
|
||||
|
||||
if (frame_time < cur_time) { fprintf(stderr, "ERROR: we late\n"); return; }
|
||||
|
||||
delay.tv_nsec = ((uint64_t)(frame_time-cur_time)) % 1000000000;
|
||||
delay.tv_sec = ((uint64_t)(frame_time-cur_time)) / 1000000000;
|
||||
|
||||
if (pselect(0, NULL, NULL, NULL, &delay, NULL)) { fprintf(stderr, "ERROR: pselect fails\n"); exit(1); }
|
||||
}
|
||||
|
||||
/* delay management end */
|
||||
|
||||
static void *gui_thread(void *_g)
|
||||
{
|
||||
time_start();
|
||||
|
||||
gui_t *g = _g;
|
||||
while (1) {
|
||||
XEvent e; while (XPending(g->d)) XNextEvent(g->d, &e);
|
||||
lock();
|
||||
XPutImage(g->d, g->w, DefaultGC(g->d, DefaultScreen(g->d)), g->img, 0, 0, 0, 0,
|
||||
width, height/vzoom);
|
||||
unlock();
|
||||
delay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char re_to_pixel(float re)
|
||||
{
|
||||
float v = fminf(255, re * intensity);
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned char mix(unsigned char a, unsigned char b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("options:\n");
|
||||
printf(" -f input is float, not s16\n");
|
||||
printf(" -srate <sampling rate> set sampling rate (default 61440000)\n");
|
||||
printf(" -prb <prb> set number of PRBs (default 106)\n");
|
||||
printf(" -mu <mu> set mu (default 1, 0 is for 15KHz, 1 for 30KHz, etc.)\n");
|
||||
printf(" -no-fft don't do fft, input is in frequency domain\n");
|
||||
printf(" -vzoom <zoom> add some sort of vertical zoom, default 1, passing 2 divides height by 2, etc.\n");
|
||||
printf(" -hzoom <zoom> add some sort of speed control, default 1, passing 2 means 2 REs per pixel, etc.\n");
|
||||
printf(" -delay <delay> delay 'delay' microseconds between processing of each symbol\n");
|
||||
printf(" -intensity <intensity> set intensity of data pixel (default 255.)\n");
|
||||
printf(" -skip <count> skip 'count' samples at beginning\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
int input_is_float = 0;
|
||||
int no_fft = 0;
|
||||
int delay = 0;
|
||||
int skip = 0;
|
||||
int symbols_per_subframe;
|
||||
long sampling_rate = 61440000;
|
||||
int mu = 1;
|
||||
int prb = 106;
|
||||
|
||||
/* parse command line arguments */
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-f")) { input_is_float = 1; continue; }
|
||||
if (!strcmp(v[i], "-no-fft")) { no_fft = 1; continue; }
|
||||
if (!strcmp(v[i], "-vzoom")) { if (i>n-2) usage(); vzoom = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-hzoom")) { if (i>n-2) usage(); hzoom = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-delay")) { if (i>n-2) usage(); delay = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-intensity")) { if (i>n-2) usage(); intensity = atof(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-srate")) { if (i>n-2) usage(); sampling_rate = atol(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-prb")) { if (i>n-2) usage(); prb = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-mu")) { if (i>n-2) usage(); mu = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-skip")) { if (i>n-2) usage(); skip = atoi(v[++i]); continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
symbols_per_subframe = 14 * (1 << mu);
|
||||
|
||||
/* compute durations from sampling rate and mu (scs) */
|
||||
/* this is 38.211 5.3.1, a bit simplified/reordered */
|
||||
int ofdm_duration = sampling_rate / 15000 / (1 << mu);
|
||||
int l0_cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu) + 16*64) / 4096;
|
||||
int cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu)) / 4096;
|
||||
|
||||
/* small sanity check */
|
||||
if (prb * 12 > ofdm_duration) {
|
||||
printf("fatal: too many PRBs for this sampling rate / mu\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
height = prb * 12;
|
||||
|
||||
if (height % vzoom) { fprintf(stderr, "bad vzoom for this height, try 1, 2, 4, ...\n"); return 1; }
|
||||
|
||||
gui_t g = { 0 };
|
||||
|
||||
/* open X11 display */
|
||||
g.d = XOpenDisplay(0);
|
||||
if (!g.d) {
|
||||
fprintf(stderr, "could not open display\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create window */
|
||||
g.w = XCreateSimpleWindow(g.d, DefaultRootWindow(g.d), 0, 0,
|
||||
width, height / vzoom, 0, WhitePixel(g.d, DefaultScreen(g.d)),
|
||||
WhitePixel(g.d, DefaultScreen(g.d)));
|
||||
XStoreName(g.d, g.w, "IQ");
|
||||
/* enable backing store */
|
||||
{
|
||||
XSetWindowAttributes att;
|
||||
att.backing_store = Always;
|
||||
XChangeWindowAttributes(g.d, g.w, CWBackingStore, &att);
|
||||
}
|
||||
XSelectInput(g.d, g.w,
|
||||
KeyPressMask |
|
||||
ButtonPressMask |
|
||||
ButtonReleaseMask |
|
||||
PointerMotionMask |
|
||||
ExposureMask |
|
||||
StructureNotifyMask);
|
||||
XMapWindow(g.d, g.w);
|
||||
|
||||
g.img_data = calloc(1, width * height/vzoom * 4); if (!g.img_data) abort();
|
||||
g.img = XCreateImage(g.d, DefaultVisual(g.d, DefaultScreen(g.d)), 24 /*depth*/,
|
||||
ZPixmap, 0 /*offset*/, (char*)g.img_data, width, height/vzoom, 8, 0);
|
||||
|
||||
new_thread(gui_thread, &g);
|
||||
|
||||
/* fft data */
|
||||
int fft_size = ofdm_duration;
|
||||
float complex *in = fftwf_malloc(sizeof(float complex) * fft_size);
|
||||
float complex *out = fftwf_malloc(sizeof(float complex) * fft_size);
|
||||
fftwf_plan p_fft = fftwf_plan_dft_1d(fft_size, in, out,
|
||||
FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
memset(in, 0, sizeof(float complex) * fft_size);
|
||||
fftwf_execute(p_fft);
|
||||
|
||||
int symbol = 0;
|
||||
int back_color = 0;
|
||||
int hpixel = 0;
|
||||
|
||||
if (skip) {
|
||||
if (input_is_float) {
|
||||
float intime[skip * 2];
|
||||
if (fread(intime, skip * 8, 1, stdin) != 1) abort();
|
||||
} else {
|
||||
short intime[skip * 2];
|
||||
if (fread(intime, skip * 4, 1, stdin) != 1) abort();
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int cur_cycle_prefix;
|
||||
if (symbol == 0 || symbol == 7 * (1 << mu))
|
||||
cur_cycle_prefix = l0_cycle_prefix;
|
||||
else
|
||||
cur_cycle_prefix = cycle_prefix;
|
||||
|
||||
if (no_fft) goto without_fft;
|
||||
|
||||
/* input signal is in time domain, timing aligned, do FFT */
|
||||
|
||||
/* read time domain data */
|
||||
if (input_is_float) {
|
||||
float intime[(ofdm_duration + cur_cycle_prefix) * 2];
|
||||
if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 8, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
/* skip cycle prefix */
|
||||
in[i] = intime[(cur_cycle_prefix + i) * 2] + I * intime[(cur_cycle_prefix + i) * 2 + 1];
|
||||
} else {
|
||||
short intime[(ofdm_duration + cur_cycle_prefix) * 2];
|
||||
if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 4, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
/* skip cycle prefix */
|
||||
in[i] = intime[(cur_cycle_prefix + i) * 2] / 2048. + I * intime[(cur_cycle_prefix + i) * 2 + 1] / 2048.;
|
||||
}
|
||||
|
||||
/* go to freq domain */
|
||||
fftwf_execute(p_fft);
|
||||
|
||||
goto after_fft;
|
||||
|
||||
without_fft:
|
||||
/* input data is symbols, each containing full FFT data */
|
||||
if (input_is_float) {
|
||||
float infreq[fft_size * 2];
|
||||
if (fread(infreq, fft_size * 2 * 4, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
out[i] = infreq[i * 2] + I * infreq[i * 2 + 1];
|
||||
} else {
|
||||
short infreq[ofdm_duration * 2];
|
||||
if (fread(infreq, fft_size * 2 * 2, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
out[i] = infreq[i * 2] / 2048. + I * infreq[i * 2 + 1] / 2048.;
|
||||
}
|
||||
|
||||
after_fft:
|
||||
lock();
|
||||
|
||||
/* extract data of this ofdm symbol */
|
||||
unsigned char *p = g.img_data + g.img_pos * 4;
|
||||
int front_pos = (g.img_pos + 8) % width;
|
||||
unsigned char *pfront = g.img_data + front_pos * 4;
|
||||
for (int i = prb * 12 - 1; i >= 0; i -= vzoom) {
|
||||
/* take the max */
|
||||
float out_val = 0;
|
||||
for (int j = 0; j < vzoom; j++) {
|
||||
int start = - prb * 12 / 2;
|
||||
int pos = start + i + j;
|
||||
/* neg freq are at the end of fft data array */
|
||||
int pos_in_fft = pos >= 0
|
||||
? pos
|
||||
: pos + fft_size;
|
||||
float v = cabs(out[pos_in_fft]);
|
||||
if (v > out_val) out_val = v;
|
||||
}
|
||||
int color = re_to_pixel(out_val);
|
||||
/* grey level image mixed with some background changing every slot */
|
||||
unsigned char back_col[2][3] = {
|
||||
{ 100, 90, 90 },
|
||||
{ 100, 60, 60, }
|
||||
};
|
||||
if (hpixel == 0) {
|
||||
p[0] = p[1] = p[2] = color;
|
||||
} else {
|
||||
p[0] = mix(p[0], color);
|
||||
p[1] = mix(p[1], color);
|
||||
p[2] = mix(p[2], color);
|
||||
}
|
||||
if (hpixel == hzoom - 1) {
|
||||
p[0] = mix(p[0], back_col[back_color][0]);
|
||||
p[1] = mix(p[1], back_col[back_color][1]);
|
||||
p[2] = mix(p[2], back_col[back_color][2]);
|
||||
}
|
||||
p[3] = 255;
|
||||
p += width * 4;
|
||||
pfront[0] = 255;
|
||||
pfront[1] = 128;
|
||||
pfront[2] = 128;
|
||||
pfront[3] = 255;
|
||||
pfront += width * 4;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
hpixel++;
|
||||
if (hpixel == hzoom) {
|
||||
hpixel = 0;
|
||||
g.img_pos++;
|
||||
if (g.img_pos == width) g.img_pos = 0;
|
||||
}
|
||||
|
||||
symbol++;
|
||||
if (symbol == symbols_per_subframe) symbol = 0;
|
||||
|
||||
/* switch back color every slot */
|
||||
if (symbol % 14 == 0)
|
||||
back_color = 1 - back_color;
|
||||
|
||||
if (delay) usleep(delay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ int socket_send(int socket, void *buffer, int size) {
|
||||
|
||||
int get_connection(char *addr, int port) {
|
||||
int s, t;
|
||||
printf("waiting for connection on %s:%d\n", addr, port);
|
||||
fprintf(stderr, "waiting for connection on %s:%d\n", addr, port);
|
||||
s = create_listen_socket(addr, port);
|
||||
t = socket_accept(s);
|
||||
|
||||
@@ -169,7 +169,7 @@ int get_connection(char *addr, int port) {
|
||||
}
|
||||
|
||||
close(s);
|
||||
printf("connected\n");
|
||||
fprintf(stderr, "connected\n");
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -216,12 +216,12 @@ int try_connect_to(char *addr, int port) {
|
||||
|
||||
int connect_to(char *addr, int port) {
|
||||
int s;
|
||||
printf("connecting to %s:%d\n", addr, port);
|
||||
fprintf(stderr, "connecting to %s:%d\n", addr, port);
|
||||
again:
|
||||
s = try_connect_to(addr, port);
|
||||
|
||||
if (s == -1) {
|
||||
printf("trying again in 1s\n");
|
||||
fprintf(stderr, "trying again in 1s\n");
|
||||
sleep(1);
|
||||
goto again;
|
||||
}
|
||||
|
||||
@@ -1160,6 +1160,7 @@ uint32_t get_ssb_offset_to_pointA(uint32_t absoluteFrequencySSB,
|
||||
const int ssb_offset_point_a = ((scaled_abs_diff / 12) - 10) * scaling;
|
||||
// Offset to point A needs to be divisible by scaling
|
||||
AssertFatal(ssb_offset_point_a % scaling == 0, "PRB offset %d not valid for scs %d\n", ssb_offset_point_a, ssbSubcarrierSpacing);
|
||||
AssertFatal(ssb_offset_point_a >= 0, "ssb offset is negative %d for scs %d\n", ssb_offset_point_a, ssbSubcarrierSpacing);
|
||||
return ssb_offset_point_a;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ static int client_server(char *program_name, bool iq_samples_time_source)
|
||||
/* wait a little bit so that server is ready to accept clients
|
||||
* (this is hackish)
|
||||
*/
|
||||
usleep(100);
|
||||
usleep(1000);
|
||||
|
||||
/* start client */
|
||||
client = fork();
|
||||
@@ -188,7 +188,7 @@ static int run_sub_client_server(bool iq_samples_time_source)
|
||||
* wait a bit for client to be there otherwise server
|
||||
* may finish too early
|
||||
*/
|
||||
usleep(100);
|
||||
usleep(100*1000);
|
||||
|
||||
/* let's pretend we have 2000 samples per second */
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ Some libraries have further dependencies and might not build on every system:
|
||||
- `enbscope`, `uescope`, `nrscope`: libforms/X
|
||||
- `ldpc_cuda`: CUDA
|
||||
- `websrv`: npm and others
|
||||
- `ldpc_t2`: DPDK with patch
|
||||
- `ldpc_aal`: DPDK with patch
|
||||
|
||||
# Running `cmake` directly
|
||||
|
||||
|
||||
@@ -221,9 +221,7 @@ In the CU file:
|
||||
ignored, but we recommend to put `0.0.0.0` ("any")
|
||||
- Ports should match the ones in the DU config, but for simplicity and
|
||||
standards-conformity, simply set all to 2152:
|
||||
- `local_s_portc` in CU should match `remote_n_portc` in DU
|
||||
- `local_s_portd` in CU should match `remote_n_portd` in DU
|
||||
- `remote_s_portc` in CU should match `local_n_portc` in DU
|
||||
- `remote_s_portd` in CU should match `local_n_portd` in DU
|
||||
|
||||
In the DU file:
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="./images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI LDPC offload (O-RAN AAL/DPDK BBDEV)</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
This documentation describes the integration of LDPC coding for lookaside acceleration using O-RAN AAL/DPDK BBDEV in OAI, along with its usage.
|
||||
For details on the implementation, please consult the [developer notes](../openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md).
|
||||
|
||||
# Requirements
|
||||
|
||||
In principle, any lookaside LDPC accelerator supporting the O-RAN AAL/DPDK BBDEV should work.
|
||||
However, the current implementation has only been validated for the Xilinx T2, Intel ACC100, and Intel ACC200 (VRB1).
|
||||
Therefore, your mileage may vary when using other BBDEV devices as there may be some hardware-specific changes required -- contributions are welcome!
|
||||
|
||||
## DPDK Version Requirements
|
||||
|
||||
The following DPDK versions are supported:
|
||||
- For the Xilinx T2 card, DPDK20.11+ is supported.
|
||||
- As for the Intel ACC100/ACC200, only DPDK22.11+ is supported.
|
||||
|
||||
## Tested Devices/ DPDK versions
|
||||
|
||||
### Xilinx T2
|
||||
|
||||
- DPDK20.11.9*.
|
||||
- DPDK22.11.7*.
|
||||
> Note: FPGA bitstream image and the corresponding patch file (e.g., `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` for DPDK20.11) from Accelercomm required.
|
||||
|
||||
### Intel ACC100
|
||||
|
||||
- DPDK22.11.7*.
|
||||
- DPDK23.11.3*.
|
||||
- DPDK24.11.2.
|
||||
> Note: [Patch]((https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd)) required for pre-DPDK24.11 versions when using the Intel ACC100.
|
||||
|
||||
### Intel ACC200 (also known as VRB1)
|
||||
- DPDK22.11.7.
|
||||
- DPDK23.11.3.
|
||||
- DPDK24.11.2.
|
||||
|
||||
# System Setup
|
||||
## DPDK installation
|
||||
|
||||
> Important:
|
||||
> - If you are using the Xilinx T2 card, you will need to apply the vendor-supplied patches before compiling DPDK.
|
||||
> - If you are using the Intel ACC100, you will need to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver if you are using DPDK22.11 or DPDK23.11.
|
||||
|
||||
|
||||
Refer to the guide [here](./ORAN_FHI7.2_Tutorial.md?ref_type=heads#dpdk-data-plane-development-kit) to install, and then validate your DPDK installation.
|
||||
|
||||
<details open>
|
||||
<summary> Notes on DPDK patching/installation for Xilinx T2. </summary>
|
||||
|
||||
*Note: The following instructions apply to `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch`, compatible with DPDK 20.11.9. For older patches (e.g., `ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch`), refer to the T2 documentation in `2023.w48`.*
|
||||
|
||||
```bash
|
||||
# Get DPDK source code
|
||||
git clone https://github.com/DPDK/dpdk-stable.git ~/dpdk-stable
|
||||
cd ~/dpdk-stable
|
||||
git checkout v20.11.9
|
||||
git apply ~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch
|
||||
```
|
||||
Replace `~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` by patch file provided by
|
||||
Accelercomm.
|
||||
|
||||
If you would like to install DPDK to a custom directory, here is an example.
|
||||
```bash
|
||||
cd ~/dpdk-stable
|
||||
# meson setup build
|
||||
meson setup --prefix=/opt/dpdk-t2 build # for installation with non-default installation prefix
|
||||
cd build
|
||||
ninja
|
||||
sudo ninja install
|
||||
sudo ldconfig
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
## System configuration
|
||||
|
||||
### Setting up Hugepages
|
||||
|
||||
First, we must setup hugepages on the system.
|
||||
In our setup, we setup 16 of the 1G hugepages.
|
||||
Apart from 1G, 2MB hugepages works too, but make sure to allocate a sufficient number of them.
|
||||
|
||||
```
|
||||
# sudo dpdk-hugepages.py -p 1G --setup 16G
|
||||
```
|
||||
|
||||
### Locating the Accelerator
|
||||
|
||||
Next, we check whether our system can detect our accelerator using `dpdk-devbind.py`.
|
||||
You should see Baseband devices detected by DPDK, as follows:
|
||||
```
|
||||
# sudo dpdk-devbind.py -s
|
||||
...
|
||||
Baseband devices using DPDK-compatible driver
|
||||
=============================================
|
||||
0000:f7:00.0 'Device 57c0' unused=vfio-pci
|
||||
...
|
||||
```
|
||||
|
||||
As you can see here, our Intel ACC200 has the address of `0000:f7:00.0`.
|
||||
Depending on the accelerator you are using, the address may vary.
|
||||
|
||||
### Loading VFIO-PCI and enabling SR-IOV
|
||||
Following, make sure to load the `vfio-pci` kernel modules and ensure that SR-IOV is enabled.
|
||||
|
||||
```
|
||||
# sudo modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
|
||||
```
|
||||
|
||||
### Binding the Accelerator with `vfio-pci`
|
||||
|
||||
Lastly, we bind our accelerator with the `vfio-pci` driver.
|
||||
```
|
||||
# sudo dpdk-devbind.py --bind=vfio-pci 0000:f7:00.0
|
||||
```
|
||||
|
||||
> Note: For the Xilinx T2, we can use this device directly.
|
||||
If you use an Intel vRAN accelerator, read on.
|
||||
|
||||
### Additional Steps for Intel vRAN Accelerators
|
||||
|
||||
> IMPORTANT NOTE:
|
||||
> - Currently, we only support using the Virtual Functions (VFs) of the Intel vRAN accelerators, but not the Physical Function (PF).
|
||||
> - One key advantage of using VFs is that this allows us to share the accelerator with other DU instances on the same machine, which is common in practice.
|
||||
|
||||
If you are using an Intel vRAN accelerator, you will need to use the [pf_bb_config](https://github.com/intel/pf-bb-config) tool to configure the accelerator beforehand.
|
||||
|
||||
#### pf_bb_config
|
||||
For more details, please consult the `pf_bb_config` README.
|
||||
|
||||
```
|
||||
# git clone https://github.com/intel/pf-bb-config
|
||||
# cd ~/pf-bb-config
|
||||
# ./build.sh
|
||||
```
|
||||
This clones and builds the `pf_bb_config` binary.
|
||||
|
||||
Next, we show an example for the Intel ACC200.
|
||||
We use an existing configuration located at `./vrb1/vrb1_config_16vf.cfg`.
|
||||
|
||||
Here, it is necessary to specify a VFIO token (in this case, we use the UUID `00112233-4455-6677-8899-aabbccddeeff`).
|
||||
Note that in practice, a random UUID should be used.
|
||||
```
|
||||
# sudo ./pf_bb_config VRB1 -v 00112233-4455-6677-8899-aabbccddeeff -c vrb1/vrb1_config_16vf.cfg
|
||||
== pf_bb_config Version v25.01-0-g812e032 ==
|
||||
VRB1 PF [0000:f7:00.0] configuration complete!
|
||||
Log file = /var/log/pf_bb_cfg_0000:f7:00.0.log
|
||||
```
|
||||
|
||||
#### Creating VFs
|
||||
|
||||
Finally, we create the VF(s) for our accelerator.
|
||||
In this example, we only create one SR-IOV VF.
|
||||
```
|
||||
# echo 1 | sudo tee /sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs
|
||||
```
|
||||
|
||||
If you encounter any error when creating the VF(s), e.g., `tee: '/sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs': No such file or directory`, then try enabling SR-IOV again.
|
||||
```
|
||||
# echo 1 | sudo tee /sys/module/vfio_pci/parameters/enable_sriov
|
||||
```
|
||||
|
||||
After you have successfully created the VF, you should see an additional baseband device, in our case, it is `0000:f7:00.1`.
|
||||
We will use this device with OAI later.
|
||||
The newly created VF should also be using the same `vfio-pci` driver as the PF, if it is not, you will need to do a `dpdk-devbind.py` to bind it with `vfio-pci`.
|
||||
```
|
||||
# sudo dpdk-devbind.py -s
|
||||
...
|
||||
Baseband devices using DPDK-compatible driver
|
||||
=============================================
|
||||
0000:f7:00.0 'Device 57c0' drv=vfio-pci unused=
|
||||
0000:f7:00.1 'Device 57c0' drv=vfio-pci unused=
|
||||
...
|
||||
```
|
||||
|
||||
# Building OAI with ORAN-AAL
|
||||
OTA deployment is precisely described in the following tutorial:
|
||||
- [NR_SA_Tutorial_COTS_UE](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_COTS_UE.md)
|
||||
Instead of section *3.2 Build OAI gNB* from the tutorial, run the following commands:
|
||||
|
||||
```bash
|
||||
# Get openairinterface5g source code
|
||||
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
|
||||
cd ~/openairinterface5g
|
||||
git checkout develop
|
||||
|
||||
# Install OAI dependencies
|
||||
cd ~/openairinterface5g/cmake_targets
|
||||
./build_oai -I
|
||||
|
||||
# Build OAI gNB
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets
|
||||
./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_aal" -C
|
||||
```
|
||||
|
||||
A shared object file `libldpc_aal.so` will be created during the compilation.
|
||||
This object is conditionally compiled.
|
||||
The selection of the library to compile is done using `--build-lib ldpc_aal`.
|
||||
|
||||
> Note: The required DPDK poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to building OAI.
|
||||
|
||||
# O-RAN AAL DPDK EAL parameters
|
||||
To configure O-RAN AAL/DPDK BBDEV, you can set the following parameters via the command line of PHY simulators or nr-softmodem:
|
||||
|
||||
> Note: the group parameter name has been renamed from `nrLDPC_coding_t2` to
|
||||
> `nrLDPC_coding_aal` to better reflect that it is a generic AAL accelerator
|
||||
> card.
|
||||
|
||||
- `nrLDPC_coding_aal.dpdk_dev` - **mandatory** parameter, specifies the PCI address of our accelerator. It must follow the format `WWWW:XX:YY.Z`.
|
||||
|
||||
- `nrLDPC_coding_aal.dpdk_core_list` - **mandatory** parameter, specifies the CPU cores assigned to DPDK .
|
||||
Ensure that the CPU cores specified in `nrLDPC_coding_aal.dpdk_core_list` are available and not used by other processes to avoid conflicts.
|
||||
|
||||
- `nrLDPC_coding_aal.dpdk_prefix` - optional parameter, DPDK shared data file prefix, by default set to *b6*.
|
||||
|
||||
- `nrLDPC_coding_aal.vfio_vf_token` - optional parameter, VFIO token set for the VF, if applicable.
|
||||
|
||||
- `nrLDPC_coding_aal.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased).
|
||||
|
||||
- `nrLDPC_coding_aal.is_t2` - optional parameter, set this to 1 when using the Xilinx T2 card.
|
||||
|
||||
**Note:** These parameters can also be provided in a configuration file.
|
||||
Example for the ACC200:
|
||||
```
|
||||
nrLDPC_coding_aal : {
|
||||
dpdk_dev : "0000:f7:00.1";
|
||||
dpdk_core_list : "14-15";
|
||||
vfio_vf_token: "00112233-4455-6677-8899-aabbccddeeff";
|
||||
};
|
||||
|
||||
loader : {
|
||||
ldpc : {
|
||||
shlibversion : "_aal";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
# Running OAI with O-RAN AAL
|
||||
|
||||
In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _aal` option.
|
||||
Reminder, if you are using the Xilinx T2 card, make sure to set `--nrLDPC_coding_aal.is_t2 1`.
|
||||
|
||||
## 5G PHY simulators
|
||||
|
||||
### nr_ulsim
|
||||
|
||||
Example command:
|
||||
```bash
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff
|
||||
```
|
||||
### nr_dlsim
|
||||
|
||||
Example command:
|
||||
```bash
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff
|
||||
```
|
||||
|
||||
## OTA test
|
||||
|
||||
### Running OAI gNB with USRP B210/FHI72
|
||||
|
||||
Example command:
|
||||
```bash
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 14-15 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff
|
||||
```
|
||||
|
||||
# Known Issue(s)
|
||||
|
||||
## BBDEV CPU Usage
|
||||
|
||||
When running the E2E setup (this applies to both USRP and FHI72), BBDEV may not be using the list of CPU cores as specified by `nrLDPC_coding_aal.dpdk_core_list` accordingly.
|
||||
This is an issue under investigation, and subject for future fixes.
|
||||
In the meantime, we recommend allocating idle, and isolated CPU cores in the configuration for BBDEV.
|
||||
|
||||
## Potential Low Throughput
|
||||
|
||||
The current implementation has been tested to work in an end-to-end setup and is functional.
|
||||
However, there are still opportunities for optimization, particularly in LDPC decoding performance, which is an area of ongoing improvement.
|
||||
As such, downlink/uplink throughput may be suboptimal with the default configurations, but enhancements are actively being explored.
|
||||
|
||||
To achieve better E2E performance with the current implementation, we recommend the following adjustments:
|
||||
1. Increasing the number of LDPC decoding iterations of the L1, e.g., `max_ldpc_iterations` to 200.
|
||||
2. Increasing the BLER targets of the MAC scheduler.
|
||||
|
||||
Example configuration snippet:
|
||||
```
|
||||
...
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 180;
|
||||
pucch_TargetSNRx10 = 220;
|
||||
dl_bler_target_upper = .35;
|
||||
dl_bler_target_lower = .15;
|
||||
ul_bler_target_upper = .35;
|
||||
ul_bler_target_lower = .15;
|
||||
pusch_FailureThres = 1000;
|
||||
ul_max_mcs = 28;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 120;
|
||||
pucch0_dtx_threshold = 100;
|
||||
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
|
||||
max_ldpc_iterations = 200;
|
||||
}
|
||||
);
|
||||
...
|
||||
```
|
||||
@@ -1,158 +0,0 @@
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="./images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI T1/T2 LDPC offload</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
This documentation aims to provide a tutorial for AMD Xilinx T2 Telco card integration into OAI and its usage.
|
||||
|
||||
# Requirements
|
||||
|
||||
- bitstream image and PMD driver for the T2 card provided by AccelerComm
|
||||
- DPDK 20.11.9 with patch from Accelercomm: version ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch
|
||||
- tested on RHEL7.9, RHEL9.2, Ubuntu 22.04
|
||||
|
||||
# DPDK setup
|
||||
## DPDK installation
|
||||
*Note: Following instructions are valid for ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch version, which is compatible with DPDK 20.11.9. Installation steps, which should be followed for older versions of the patch file (for example ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch) are present in older version of this documentation, under the tag 2023.w48.*
|
||||
|
||||
```
|
||||
# Get DPDK source code
|
||||
git clone https://github.com/DPDK/dpdk-stable.git ~/dpdk-stable
|
||||
cd ~/dpdk-stable
|
||||
git checkout v20.11.9
|
||||
git apply ~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch
|
||||
```
|
||||
Replace `~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` by patch file provided by
|
||||
Accelercomm.
|
||||
```
|
||||
cd ~/dpdk-stable
|
||||
meson setup build
|
||||
# meson setup --prefix=/opt/dpdk-t2 build for installation with non-default installation prefix
|
||||
cd build
|
||||
ninja
|
||||
sudo ninja install
|
||||
sudo ldconfig
|
||||
```
|
||||
## DPDK configuration
|
||||
- load required kernel module
|
||||
```
|
||||
sudo modprobe vfio-pci
|
||||
```
|
||||
- check presence of the card and its PCI addres on the host machine
|
||||
```
|
||||
lspci | grep "Xilinx"
|
||||
```
|
||||
- bind the card with vfio-pci driver
|
||||
```
|
||||
sudo python3 ~/dpdk-stable/usertools/dpdk-devbind.py --bind=vfio-pci 41:00.0
|
||||
```
|
||||
Replace PCI address of the card *41:00.0* by address detected by *lspci | grep "Xilinx"* command
|
||||
- hugepages setup (10 x 1GB hugepages)
|
||||
```
|
||||
sudo python3 ~/dpdk-stable/usertools/dpdk-hugepages.py -p 1G --setup 10G
|
||||
```
|
||||
|
||||
*Note: device binding and hugepages setup has to be done after every reboot of
|
||||
the host machine*
|
||||
|
||||
# Modifications in the OAI code
|
||||
## DPDK lib and PMD path specification
|
||||
If DPDK library was installed into custom path, you have to point to the right directory with `PKG_CONFIG_PATH`, prior to the OAI build. Sample command to set the DPDK path to `/opt/dpdk-t2/lib64/pkgconfig/`:
|
||||
```
|
||||
export PKG_CONFIG_PATH=/opt/dpdk-t2/lib64/pkgconfig/:$PKG_CONFIG_PATH
|
||||
```
|
||||
|
||||
# OAI Build
|
||||
OTA deployment is precisely described in the following tutorial:
|
||||
- [NR_SA_Tutorial_COTS_UE](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_COTS_UE.md)
|
||||
Instead of section *3.2 Build OAI gNB* from the tutorial, run following commands:
|
||||
|
||||
```
|
||||
# Get openairinterface5g source code
|
||||
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
|
||||
cd ~/openairinterface5g
|
||||
git checkout develop
|
||||
|
||||
# Install OAI dependencies
|
||||
cd ~/openairinterface5g/cmake_targets
|
||||
./build_oai -I
|
||||
|
||||
# Build OAI gNB
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets
|
||||
./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_t2" -C
|
||||
```
|
||||
|
||||
Shared object file *libldpc_t2.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using *--build-lib ldpc_t2*.
|
||||
|
||||
*Required poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to the build of OAI*
|
||||
|
||||
# Setup of T2-related DPDK EAL parameters
|
||||
To configure T2-related DPDK Environment Abstraction Layer (EAL) parameters, you can set the following parameters via the command line of PHY simulators or softmodem:
|
||||
- `nrLDPC_coding_t2.dpdk_dev` - **mandatory** parameter, specifies PCI address of the T2 card. PCI address of the T2 card can be detected by `lspci | grep "Xilinx"` command.
|
||||
- `nrLDPC_coding_t2.dpdk_core_list` - **mandatory** parameter, specifies CPU cores assigned to DPDK for T2 processing. Ensure that the CPU cores specified in *nrLDPC_coding_t2.dpdk_core_list* are available and not used by other processes to avoid conflicts.
|
||||
- `nrLDPC_coding_t2.dpdk_prefix` - DPDK shared data file prefix, by default set to *b6*.
|
||||
|
||||
**Note:** These parameters can also be provided in a configuration file:
|
||||
```
|
||||
nrLDPC_coding_t2 : {
|
||||
dpdk_dev : "41:00.0";
|
||||
dpdk_core_list : "14-15";
|
||||
};
|
||||
|
||||
loader : {
|
||||
ldpc : {
|
||||
shlibversion : "_t2";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
# 5G PHY simulators
|
||||
|
||||
## nr_ulsim test
|
||||
Offload of the channel decoding to the T2 card is in nr_ulsim specified by *--loader.ldpc.shlibversion _t2* option. Example command for running nr_ulsim with LDPC decoding offload to the T2 card:
|
||||
```
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1
|
||||
```
|
||||
## nr_dlsim test
|
||||
Offload of the channel encoding to the AMD Xilinx T2 card is in nr_dlsim specified by *-c* option. Example command for running nr_dlsim with LDPC encoding offload to the T2 card:
|
||||
```
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1
|
||||
```
|
||||
|
||||
# OTA test
|
||||
Offload of the channel encoding and decoding to the AMD Xilinx T2 card is enabled by *--loader.ldpc.shlibversion _t2* option.
|
||||
|
||||
## Run OAI gNB with USRP B210
|
||||
```
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets/ran_build/build
|
||||
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1
|
||||
```
|
||||
|
||||
# Limitations
|
||||
## AMD Xilinx T2 card
|
||||
- functionality of the LDPC encoding and decoding offload verified in OTA SISO setup with USRP N310 and Quectel RM500Q, blocking of the card reported for MIMO setup (2 layers)
|
||||
|
||||
*Note: AMD Xilinx T1 Telco card is not supported anymore.*
|
||||
+3
-1
@@ -64,7 +64,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https://
|
||||
- [How to run with E2 agent](../openair2/E2AP/README.md)
|
||||
- [How to run the physical simulators](./physical-simulators.md)
|
||||
- [How to setup OAI with Nvidia Aerial and Foxconn](./Aerial_FAPI_Split_Tutorial.md)
|
||||
- [How to setup OAI with AMD T2 Telco card](./LDPC_T2_OFFLOAD_SETUP.md)
|
||||
- [How to setup OAI with LDPC accelerators (Xilinx T2/Intel ACCs)](./LDPC_OFFLOAD_SETUP.md)
|
||||
- [How to do a handover](./handover-tutorial.md)
|
||||
- [How to setup gNB frequency](./gNB_frequency_setup.md)
|
||||
|
||||
@@ -130,6 +130,8 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
|
||||
# Testing
|
||||
|
||||
- [UnitTests.md](./UnitTests.md) explains the unit testing setup
|
||||
- Component tests are under `tests/`. Currently, there is a simple CU-UP
|
||||
tester, see the corresponding [README.md](../tests/nr-cuup/README.md).
|
||||
- [TESTBenches.md](./TESTBenches.md) lists the CI setup and links to pipelines
|
||||
|
||||
# Developer tools
|
||||
|
||||
@@ -78,7 +78,7 @@ RUN /bin/sh oaienv && \
|
||||
cd cmake_targets && \
|
||||
mkdir -p log && \
|
||||
./build_oai \
|
||||
--gNB \
|
||||
--ninja --gNB \
|
||||
--build-lib "telnetsrv enbscope uescope nrscope" \
|
||||
-t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \
|
||||
--build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
|
||||
|
||||
@@ -58,7 +58,7 @@ RUN /bin/sh oaienv && \
|
||||
mkdir -p log && \
|
||||
scl enable gcc-toolset-13 "\
|
||||
./build_oai \
|
||||
--gNB \
|
||||
--ninja --gNB \
|
||||
--build-lib 'telnetsrv enbscope uescope nrscope' \
|
||||
-t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \
|
||||
--build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION" &&\
|
||||
|
||||
@@ -68,7 +68,7 @@ RUN /bin/sh oaienv && \
|
||||
cd cmake_targets && \
|
||||
mkdir -p log && \
|
||||
./build_oai \
|
||||
--gNB \
|
||||
--ninja --gNB \
|
||||
--build-lib "telnetsrv enbscope uescope nrscope" \
|
||||
-t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \
|
||||
--build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
|
||||
|
||||
@@ -28,35 +28,35 @@ shlib_path libldpc.so
|
||||
`libldpc.so` has its decoder implemented in [nrLDPC_coding_segment_decoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c).\
|
||||
Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c).
|
||||
|
||||
loading `libldpc_t2.so` instead of `libldpc.so`:
|
||||
loading `libldpc_aal.so` instead of `libldpc.so`:
|
||||
|
||||
`make ldpc_t2`
|
||||
`make ldpc_aal`
|
||||
|
||||
This command creates the `libldpc_t2.so` shared library.
|
||||
This command creates the `libldpc_aal.so` shared library.
|
||||
|
||||
```
|
||||
Building C object CMakeFiles/ldpc_t2.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c.o
|
||||
Linking C shared module libldpc_t2.so
|
||||
Building C object CMakeFiles/ldpc_aal.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c.o
|
||||
Linking C shared module libldpc_aal.so
|
||||
```
|
||||
|
||||
At runtime, to successfully use the T2 board, you need to install vendor specific drivers and tools.\
|
||||
Please refer to the dedicated documentation at [LDPC_T2_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_T2_OFFLOAD_SETUP.md).
|
||||
At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools.
|
||||
Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_OFFLOAD_SETUP.md).
|
||||
|
||||
`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1`
|
||||
`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 01:00.0 --nrLDPC_coding_aal.dpdk_core_list 0-1`
|
||||
|
||||
```
|
||||
|
||||
.......................
|
||||
[CONFIG] loader.ldpc.shlibversion set to default value ""
|
||||
[LIBCONFIG] loader.ldpc: 2/2 parameters successfully set, (1 to default value)
|
||||
[CONFIG] shlibversion set to _t2 from command line
|
||||
[CONFIG] shlibversion set to _aal from command line
|
||||
[CONFIG] loader.ldpc 1 options set from command line
|
||||
shlib_path libldpc_t2.so
|
||||
[LOADER] library libldpc_t2.so successfully loaded
|
||||
shlib_path libldpc_aal.so
|
||||
[LOADER] library libldpc_aal.so successfully loaded
|
||||
........................
|
||||
```
|
||||
|
||||
`libldpc_t2.so` has its decoder and its encoder implemented in [nrLDPC_coding_t2.c](file://../nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c).
|
||||
`libldpc_aal.so` has its decoder and its encoder implemented in [nrLDPC_coding_aal.c](file://../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c).
|
||||
|
||||
loading `libldpc_xdma.so` instead of `libldpc.so`:
|
||||
|
||||
@@ -103,7 +103,7 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version>
|
||||
|
||||
`libldpc.so` is completed.
|
||||
|
||||
`libldpc_t2.so` is completed.
|
||||
`libldpc_aal.so` is completed.
|
||||
|
||||
`libldpc_xdma.so` is completed.
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
add_subdirectory(nrLDPC_coding_segment)
|
||||
add_subdirectory(nrLDPC_coding_xdma)
|
||||
add_subdirectory(nrLDPC_coding_t2)
|
||||
add_subdirectory(nrLDPC_coding_aal)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
##########################################################
|
||||
# LDPC offload library - O-RAN AAL/DPDK BBDEV
|
||||
##########################################################
|
||||
|
||||
add_boolean_option(ENABLE_LDPC_AAL OFF "Build support for LDPC Offload using O-RAN AAL library" OFF)
|
||||
|
||||
if (ENABLE_LDPC_AAL)
|
||||
pkg_check_modules(LIBDPDK_AAL REQUIRED libdpdk>=20.11.9)
|
||||
|
||||
find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_AAL_LIBRARY_DIRS})
|
||||
if (PMD_T2)
|
||||
message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)")
|
||||
else()
|
||||
if (LIBDPDK_AAL_VERSION VERSION_LESS "22.11")
|
||||
message(FATAL_ERROR "for non-T2 BBDEV PMDs, we support the Intel ACC100 and ACC200 (VRB1), using DPDK 22.11 or later.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(ldpc_aal MODULE nrLDPC_coding_aal.c)
|
||||
|
||||
set_target_properties(ldpc_aal PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API")
|
||||
set_target_properties(ldpc_aal PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
target_include_directories(ldpc_aal PRIVATE ${LIBDPDK_AAL_INCLUDE_DIRS})
|
||||
target_link_libraries(ldpc_aal PRIVATE ldpc_gen_HEADERS ${LIBDPDK_AAL_LDFLAGS})
|
||||
if (PMD_T2)
|
||||
target_link_libraries(ldpc_aal PRIVATE ${PMD_T2})
|
||||
endif()
|
||||
|
||||
add_dependencies(nr-softmodem ldpc_aal)
|
||||
add_dependencies(nr-uesoftmodem ldpc_aal)
|
||||
add_dependencies(nr_ulsim ldpc_aal)
|
||||
add_dependencies(nr_ulschsim ldpc_aal)
|
||||
add_dependencies(nr_dlsim ldpc_aal)
|
||||
add_dependencies(nr_dlschsim ldpc_aal)
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,143 @@
|
||||
This document highlights some of the key aspects of the implementation for O-RAN AAL/DPDK BBDEV; for user documentation, please consult the [setup guide](../../../../../doc/LDPC_OFFLOAD_SETUP.md) instead.
|
||||
|
||||
## Overview
|
||||
|
||||
The current implementation supports the following BBDEV devices and respective DPDK versions:
|
||||
- Xilinx T2 on DPDK20.11+
|
||||
- Intel ACC100* on DPDK22.11+
|
||||
- Intel ACC200 (VRB1) on DPDK22.11+
|
||||
|
||||
> Important notes:
|
||||
> - For the Intel ACC100, for DPDK22.11 and DPDK23.11, users are required to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver.
|
||||
> - If you intend to use this implementation in an FHI7.2 setup, only the F-release and beyond is supported.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Below, we highlight some of the important details in this implementation.
|
||||
|
||||
### HARQ
|
||||
|
||||
To maintain the HARQ buffers in the LDPC decoding process, the BBDEV either uses its internal memory (not visible to DPDK), or using external memory (i.e., DDR) managed by OAI.
|
||||
For the Xilinx T2 and Intel ACC100, they use the internal memory while the Intel ACC200 uses the DDR memory.
|
||||
|
||||
If internal memory is supported by the device, we will set `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE` and `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE`.
|
||||
|
||||
The following two fields are important for HARQ: `harq_combined_input` and `harq_combined_output`.
|
||||
|
||||
For the first transmission, there is no HARQ input.
|
||||
Thus, the flag `RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE` is not set.
|
||||
However, there will be a HARQ output, so we set the flag `RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE`.
|
||||
We also set the corresponding fields for `harq_combined_output`.
|
||||
|
||||
For the second transmission (or subsequent retransmissions), we need to provide a HARQ input at the same time, there will also be a HARQ output.
|
||||
Therefore, both fields, `harq_combined_input` and `harq_combined_output` must be set accordingly.
|
||||
|
||||
As HARQ requires the output of the previous transmission/ HARQ round as its input, we need to maintain a separate variable to hold the previous round's information.
|
||||
To do so, we define `harq_buffers` (which is a pointer to an array of size `num_harq_codeblock` allocated in the heap later) in the `active_device` struct (see the following) in our implementation.
|
||||
```C
|
||||
struct active_device {
|
||||
...
|
||||
uint32_t num_harq_codeblock;
|
||||
/* Persistent data structure to keep track of HARQ-related information */
|
||||
// Note: This is used to store/keep track of the combined output information across iterations
|
||||
struct rte_bbdev_op_data *harq_buffers;
|
||||
...
|
||||
} active_dev;
|
||||
```
|
||||
|
||||
|
||||
In the following, we elaborate on how the HARQ-related fields are set.
|
||||
|
||||
#### Internal Memory
|
||||
|
||||
When using the internal memory, we need to specify the *offset* of the hardware's memory where we want to store the HARQ combined output, and read from it accordingly later.
|
||||
|
||||
In the current implementation, we use a fixed offset of 32K, i.e., this means that each block is 32K.
|
||||
|
||||
An index is derived from a unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`).
|
||||
Using that index, we then derived the corresponding offset in the memory (i.e., `harq_combined_offset`).
|
||||
|
||||
```C
|
||||
...
|
||||
|
||||
// Calculate offset in the HARQ combined buffers
|
||||
// Unique segment offset
|
||||
uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i;
|
||||
// Prune to avoid shooting above maximum id
|
||||
uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock;
|
||||
// Segment offset to byte offset
|
||||
uint32_t harq_combined_offset = pruned_segment_offset * LDPC_MAX_CB_SIZE;
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
This `harq_combined_offset` is then set for `harq_combined_input.offset` and `harq_combined_output.offset` to tell BBDEV where to read/write the HARQ buffers.
|
||||
|
||||
Especially for the Intel ACCs, when providing the HARQ input, the `harq_combined_input.length` must be provided.
|
||||
To do so, we maintain the length of previous round's output using `harq_buffers` by copying it in `retrieve_ldpc_dec_op`.
|
||||
|
||||
#### External Memory
|
||||
|
||||
Different from the Xilinx T2 and Intel ACC100, the Intel ACC200 uses DDR memory to maintain the HARQ buffers which is managed by OAI.
|
||||
|
||||
In short, we use `harq_buffers` from the `active_device` struct, which have been initialzed with corresponding DPDK mempools for BBDEV to read/write the HARQ input/outputs.
|
||||
|
||||
For indexing, we use the `pruned_segment_offset`, which is derived from the unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`).
|
||||
|
||||
`harq_combined_input.offset` and `harq_combined_output.offset` is always set to 0 in our implementation.
|
||||
Instead, we provide BBDEV the pointers to our allocated mempool regions through `harq_combined_input.data` and `harq_combined_output.data`.
|
||||
|
||||
In `retrieve_ldpc_dec_op`, we perform a `rte_memcpy` to copy the HARQ combined outputs to `harq_buffers`, along with other necessary metadata information to prepare for subsequent HARQ rounds.
|
||||
Similar to the Intel ACC100, when providing the HARQ input, the `harq_combined_input.length` must be provided.
|
||||
|
||||
### LLR Scaling
|
||||
|
||||
The LLR output from OAI's demodulation block is in 16 bits.
|
||||
However, accelerators typically assume that the LLR inputs are scaled down using fixed-point arithmetic, e.g., 8 bits.
|
||||
|
||||
For the Xilinx T2, saturation is performed.
|
||||
However, our tests have shown that this implementation did not work well for the Intel ACCs.
|
||||
|
||||
To that end, we perform LLR scaling differently for the Intel ACCs.
|
||||
Through the function `llr_scaling`, we look at the min and max LLR values, perform scaling and subsequently saturation accordingly.
|
||||
|
||||
The fixed-point representations supported by the hardware is defined by the `llr_size` and `llr_decimal`.
|
||||
For example, if `llr_size=8`, and `llr_decimal=1`, it means that we use a S8.1 representation (1 fractional bit), the min and max values are -64 to 63.5.
|
||||
|
||||
> *Limitation:*
|
||||
> We observe that in certain configurations, such as when using 2 MIMO layers, the LLR (Log-Likelihood Ratio) outputs exhibit a much wider dynamic range (e.g., from -10,000 to 10,000) compared to the typical sub-1,000 range seen in most other cases.
|
||||
> This suggests potential inconsistencies in the LLR values generated by the demodulator.
|
||||
> We believe that the decoding performance, when compared to the CPU implementation under the same number of LDPC decoding iterations, could be improved by refining the LLR scaling approach or revisiting the LLR generation process during demodulation.
|
||||
> This remains an area of active investigation.
|
||||
>
|
||||
> *Workaround:*
|
||||
> As a temporary measure, increasing the number of LDPC decoding iterations—e.g., to 150—has shown to yield a decoding success rate comparable to that of the CPU implementation.
|
||||
> This comes at a negligible increase in decoding time based on our current evaluation.
|
||||
|
||||
### Special Case(s)
|
||||
|
||||
#### TB mode
|
||||
|
||||
For the T2, only CB mode is supported.
|
||||
|
||||
For the Intel ACCs, while we also use CB mode mostly, there is a special case that must be handled properly as specified by the BBDEV documentations:
|
||||
```
|
||||
... The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. ...
|
||||
```
|
||||
|
||||
In such a case, we perform a check on whether there is only one TB, AND if the TB has only one CB.
|
||||
If so, this has to be handled separately by using TB mode.
|
||||
|
||||
### Future Work(s)
|
||||
|
||||
#### TB mode
|
||||
|
||||
The current channel coding interface assumes LDPC encoding and decoding are performed on a per-code block basis.
|
||||
However, to fully leverage Transport Block (TB) mode as supported in BBDEV, which can improve efficiency, the L1 implementation surrounding channel coding needs to be refactored.
|
||||
This remains an area for future investigation.
|
||||
|
||||
#### CRC24A/CRC16
|
||||
|
||||
CRC24A/CRC16 is supported by the BBDEV device (i.e., Intel ACC200).
|
||||
However, we do not make use of them and rely on OAI's existing CRC functions.
|
||||
To use of BBDEV's CRC24A/CRC16 capability will be looked into in future MRs.
|
||||
+526
-179
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -2,8 +2,8 @@
|
||||
* Copyright(c) 2017 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _MAIN_H_
|
||||
#define _MAIN_H_
|
||||
#ifndef __NRLDPC_CODING_T2_H__
|
||||
#define __NRLDPC_CODING_T2_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/queue.h>
|
||||
@@ -17,6 +17,7 @@
|
||||
enum op_data_type {
|
||||
DATA_INPUT = 0,
|
||||
DATA_HARD_OUTPUT,
|
||||
DATA_HARQ_OUTPUT,
|
||||
DATA_NUM_TYPES,
|
||||
};
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
##########################################################
|
||||
|
||||
# LDPC offload library - AMD T2 Accelerator Card
|
||||
##########################################################
|
||||
|
||||
add_boolean_option(ENABLE_LDPC_T2 OFF "Build support for LDPC Offload to T2 library" OFF)
|
||||
|
||||
if (ENABLE_LDPC_T2)
|
||||
|
||||
pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk>=20.11.9)
|
||||
|
||||
find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_T2_LIBRARY_DIRS})
|
||||
if (NOT PMD_T2)
|
||||
message(FATAL_ERROR "could not find poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)")
|
||||
endif()
|
||||
|
||||
message(STATUS "T2 build: use ${PMD_T2}")
|
||||
add_library(ldpc_t2 MODULE nrLDPC_coding_t2.c)
|
||||
|
||||
set_target_properties(ldpc_t2 PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API")
|
||||
set_target_properties(ldpc_t2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
target_link_libraries(ldpc_t2 PRIVATE ldpc_gen_HEADERS ${LIBDPDK_T2_LDFLAGS} ${PMD_T2})
|
||||
|
||||
add_dependencies(nr-softmodem ldpc_t2)
|
||||
add_dependencies(nr-uesoftmodem ldpc_t2)
|
||||
add_dependencies(nr_ulsim ldpc_t2)
|
||||
add_dependencies(nr_ulschsim ldpc_t2)
|
||||
add_dependencies(nr_dlsim ldpc_t2)
|
||||
add_dependencies(nr_dlschsim ldpc_t2)
|
||||
|
||||
endif()
|
||||
@@ -44,9 +44,7 @@
|
||||
#include "assertions.h"
|
||||
#include "T.h"
|
||||
|
||||
//#define DEBUG_DCI_DECODING 1
|
||||
|
||||
//#define NR_PDCCH_DCI_DEBUG // activates NR_PDCCH_DCI_DEBUG logs
|
||||
// #define NR_PDCCH_DCI_DEBUG // activates NR_PDCCH_DCI_DEBUG logs
|
||||
#ifdef NR_PDCCH_DCI_DEBUG
|
||||
#define LOG_DDD(a, ...) printf("<-NR_PDCCH_DCI_DEBUG (%s)-> " a, __func__, ##__VA_ARGS__ )
|
||||
#define LOG_DSYMB(b) \
|
||||
@@ -63,14 +61,14 @@
|
||||
#define LOG_DDD(a...)
|
||||
#define LOG_DSYMB(a...)
|
||||
#endif
|
||||
|
||||
#define NR_NBR_CORESET_ACT_BWP 3 // The number of CoreSets per BWP is limited to 3 (including initial CORESET: ControlResourceId 0)
|
||||
#define NR_NBR_SEARCHSPACE_ACT_BWP \
|
||||
10 // The number of SearSpaces per BWP is limited to 10 (including initial SEARCHSPACE: SearchSpaceId 0)
|
||||
|
||||
#ifdef LOG_I
|
||||
#undef LOG_I
|
||||
#define LOG_I(A, B...) printf(B)
|
||||
#endif
|
||||
#define RE_PER_RB 12
|
||||
// after removing the 3 DMRS RE, the RB contains 9 RE with PDCCH
|
||||
#define RE_PER_RB_OUT_DMRS 9
|
||||
|
||||
static void nr_pdcch_demapping_deinterleaving(c16_t *llr,
|
||||
c16_t *e_rx,
|
||||
@@ -170,7 +168,7 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr,
|
||||
}
|
||||
|
||||
int rb_count = 0;
|
||||
const int data_sc = 9; // 9 sub-carriers with data per PRB
|
||||
const int data_sc = RE_PER_RB_OUT_DMRS; // 9 sub-carriers with data per PRB
|
||||
for (int c_id = 0; c_id < number_of_candidates; c_id++) {
|
||||
for (int symbol_idx = start_symbol; symbol_idx < start_symbol + coreset_time_dur; symbol_idx++) {
|
||||
for (int cce_count = 0; cce_count < L[c_id]; cce_count++) {
|
||||
@@ -186,67 +184,47 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr,
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms,
|
||||
int32_t rx_size,
|
||||
c16_t rxdataF_comp[][rx_size],
|
||||
c16_t *pdcch_llr,
|
||||
uint8_t symbol,
|
||||
uint32_t coreset_nbr_rb)
|
||||
static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr)
|
||||
{
|
||||
c16_t *rxF = &rxdataF_comp[0][(symbol * coreset_nbr_rb * 12)];
|
||||
c16_t *pdcch_llrp = &pdcch_llr[symbol * coreset_nbr_rb * 9];
|
||||
|
||||
if (!pdcch_llrp) {
|
||||
LOG_E(NR_PHY_DCI, "pdcch_qpsk_llr: llr is null, symbol %d\n", symbol);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DDD("llr logs: pdcch qpsk llr for symbol %d (pos %d), llr offset %ld\n",symbol,(symbol*frame_parms->N_RB_DL*12),pdcch_llrp-pdcch_llr);
|
||||
|
||||
for (int i = 0; i < coreset_nbr_rb * 9; i++) {
|
||||
for (int i = 0; i < sz; i++) {
|
||||
// We clip the signal
|
||||
c16_t res;
|
||||
res.r = min(rxF->r, 31);
|
||||
res.r = max(-32, res.r);
|
||||
res.i = min(rxF->i, 31);
|
||||
res.i = max(-32, res.i);
|
||||
*pdcch_llrp = res;
|
||||
LOG_DDD("llr logs: rb=%d i=%d *rxF:%d => *pdcch_llrp:%d\n",i/18,i,*rxF,*pdcch_llrp);
|
||||
*llr++ = res;
|
||||
LOG_DDD("llr logs: rb=%d i=%d rxF:%d,%d => pdcch_llr:%d,%d\n", i / 18, i, rxF->r, rxF->i, llr->r, llr->i);
|
||||
rxF++;
|
||||
pdcch_llrp++;
|
||||
}
|
||||
}
|
||||
|
||||
//compute average channel_level on each (TX,RX) antenna pair
|
||||
void nr_pdcch_channel_level(int32_t rx_size,
|
||||
c16_t dl_ch_estimates_ext[][rx_size],
|
||||
NR_DL_FRAME_PARMS *frame_parms,
|
||||
int32_t *avg,
|
||||
int symbol,
|
||||
int nb_rb)
|
||||
static int32_t nr_pdcch_channel_level(int nb_re, int arraySz, c16_t dl_ch_estimates_ext[][arraySz], int nb_antennas_rx)
|
||||
{
|
||||
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
|
||||
|
||||
simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx][symbol * nb_rb * 12];
|
||||
|
||||
//compute average level
|
||||
avg[aarx] = simde_mm_average(dl_ch128, nb_rb * 12, 0, nb_rb * 12);
|
||||
//LOG_DDD("Channel level : %d\n", avg[aarx]);
|
||||
int32_t avgs = 0;
|
||||
for (int aarx = 0; aarx < nb_antennas_rx; aarx++) {
|
||||
simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx];
|
||||
// compute average level
|
||||
// if nb_re is not %4, some samples are missed inside simde_mm_average, so let's discard it
|
||||
int32_t tmp = simde_mm_average(dl_ch128, nb_re & ~3, 0, nb_re & ~3);
|
||||
avgs = cmax(avgs, tmp);
|
||||
}
|
||||
return avgs;
|
||||
}
|
||||
|
||||
// This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources)
|
||||
static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz,
|
||||
uint32_t coreset_nbr_rb,
|
||||
c16_t rxdataF[][rxdataF_sz],
|
||||
int32_t est_size,
|
||||
c16_t dl_ch_estimates[][est_size],
|
||||
int32_t rx_size,
|
||||
c16_t rxdataF_ext[][rx_size],
|
||||
c16_t dl_ch_estimates_ext[][rx_size],
|
||||
int arraySz,
|
||||
c16_t rxdataF_ext[][arraySz],
|
||||
c16_t dl_ch_estimates_ext[][arraySz],
|
||||
int symbol,
|
||||
NR_DL_FRAME_PARMS *frame_parms,
|
||||
uint8_t *coreset_freq_dom,
|
||||
uint32_t coreset_nbr_rb,
|
||||
uint32_t n_BWP_start)
|
||||
{
|
||||
/*
|
||||
@@ -260,19 +238,13 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz,
|
||||
*
|
||||
*/
|
||||
|
||||
#define NBR_RE_PER_RB_WITH_DMRS 12
|
||||
// after removing the 3 DMRS RE, the RB contains 9 RE with PDCCH
|
||||
#define NBR_RE_PER_RB_WITHOUT_DMRS 9
|
||||
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
|
||||
const c16_t *dl_ch0 = dl_ch_estimates[aarx] + frame_parms->ofdm_symbol_size * symbol;
|
||||
c16_t *rxFbase = rxdataF[aarx] + frame_parms->ofdm_symbol_size * symbol;
|
||||
LOG_DDD("dl_ch0 = &dl_ch_estimates[aarx = (%d)][0]\n", aarx);
|
||||
|
||||
const int offset = symbol * coreset_nbr_rb * NBR_RE_PER_RB_WITH_DMRS;
|
||||
c16_t *dl_ch0_ext = &dl_ch_estimates_ext[aarx][offset];
|
||||
LOG_DDD("dl_ch0_ext = &dl_ch_estimates_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset);
|
||||
c16_t *rxF_ext = &rxdataF_ext[aarx][offset];
|
||||
LOG_DDD("rxF_ext = &rxdataF_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset);
|
||||
c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx];
|
||||
c16_t *rxF_ext = rxdataF_ext[aarx];
|
||||
|
||||
/*
|
||||
* The following for loop handles treatment of PDCCH contained in table rxdataF (in frequency domain)
|
||||
@@ -297,140 +269,67 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz,
|
||||
int c_rb_by6 = c_rb / 6;
|
||||
|
||||
// skip zeros in frequency domain bitmap
|
||||
while ((coreset_freq_dom[c_rb_by6 >> 3] & (1 << (7 - (c_rb_by6 & 7)))) == 0) {
|
||||
while ((coreset_freq_dom[c_rb_by6 / 8] & (1 << (7 - (c_rb_by6 & 7)))) == 0) {
|
||||
c_rb += 6;
|
||||
c_rb_by6 = c_rb / 6;
|
||||
}
|
||||
|
||||
// first we set initial conditions for pointer to rxdataF depending on the situation of the first RB within the CORESET
|
||||
// (c_rb = n_BWP_start)
|
||||
if ((frame_parms->N_RB_DL & 1) == 1 && (c_rb + n_BWP_start) == (frame_parms->N_RB_DL >> 1)) {
|
||||
// treatment of RB containing the DC
|
||||
// if odd number RBs in system bandwidth and first RB to be treated is higher than middle system bandwidth (around DC)
|
||||
// we have to treat the RB in two parts: first part from i=0 to 5, the data is at the end of rxdataF (pointing at the
|
||||
// end of the table)
|
||||
c16_t *rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start);
|
||||
|
||||
int i = 0, j = 0;
|
||||
for (; i < 6; i++) { // treating first part of the RB note that i=5 would correspond to DC. We treat it in NR
|
||||
if ((i != 1) && (i != 5)) {
|
||||
dl_ch0_ext[j] = dl_ch0[i];
|
||||
rxF_ext[j] = rxF[i];
|
||||
LOG_DSYMB("");
|
||||
j++;
|
||||
} else {
|
||||
LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n");
|
||||
}
|
||||
}
|
||||
|
||||
// then we point at the begining of the symbol part of rxdataF do process second part of RB
|
||||
for (; i < 12; i++) {
|
||||
if ((i != 9)) {
|
||||
dl_ch0_ext[j] = dl_ch0[i];
|
||||
rxF_ext[j] = rxFbase[i - 6];
|
||||
LOG_DSYMB("");
|
||||
j++;
|
||||
} else {
|
||||
LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n");
|
||||
}
|
||||
}
|
||||
|
||||
} else { // treatment of any RB that does not contain the DC
|
||||
c16_t *rxF=NULL;
|
||||
if ((frame_parms->N_RB_DL & 1) == 0) {
|
||||
if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1))
|
||||
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
||||
// at (offset + c_br + symbol * ofdm_symbol_size): even case
|
||||
rxF = rxFbase + (frame_parms->first_carrier_offset + 12 * c_rb) + n_BWP_start * 12;
|
||||
else
|
||||
// number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC)
|
||||
// if these conditions are true the pointer has to be situated at the 1st part of the rxdataF
|
||||
// we point at the 1st part of the rxdataF in symbol
|
||||
rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1));
|
||||
} else {
|
||||
if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1))
|
||||
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
||||
// at (offset + c_br + symbol * ofdm_symbol_size): odd case
|
||||
rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start);
|
||||
else if ((c_rb + n_BWP_start) > (frame_parms->N_RB_DL >> 1))
|
||||
// number of RBs is odd and c_rb is higher than half system bandwidth + 1
|
||||
// if these conditions are true the pointer has to be situated at the 1st part of
|
||||
// the rxdataF just after the first IQ symbols of the RB containing DC
|
||||
// we point at the 1st part of the rxdataF in symbol
|
||||
rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6;
|
||||
}
|
||||
AssertFatal(rxF, "bug");
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if ((i != 1) && (i != 5) && (i != 9)) {
|
||||
rxF_ext[j] = rxF[i];
|
||||
dl_ch0_ext[j] = dl_ch0[i];
|
||||
LOG_DSYMB("");
|
||||
j++;
|
||||
} else {
|
||||
LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n");
|
||||
}
|
||||
}
|
||||
c16_t *rxF = NULL;
|
||||
if ((frame_parms->N_RB_DL & 1) == 0) {
|
||||
if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2)
|
||||
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
||||
// at (offset + c_br + symbol * ofdm_symbol_size): even case
|
||||
rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start);
|
||||
else
|
||||
// number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC)
|
||||
// if these conditions are true the pointer has to be situated at the 1st part of the rxdataF
|
||||
// we point at the 1st part of the rxdataF in symbol
|
||||
rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2);
|
||||
} else {
|
||||
if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2)
|
||||
// if RB to be treated is lower than middle system bandwidth then rxdataF pointed
|
||||
// at (offset + c_br + symbol * ofdm_symbol_size): odd case
|
||||
rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start);
|
||||
else
|
||||
// number of RBs is odd and c_rb is higher than half system bandwidth + 1
|
||||
// if these conditions are true the pointer has to be situated at the 1st part of
|
||||
// the rxdataF just after the first IQ symbols of the RB containing DC
|
||||
// we point at the 1st part of the rxdataF in symbol
|
||||
rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6;
|
||||
}
|
||||
dl_ch0_ext += NBR_RE_PER_RB_WITHOUT_DMRS;
|
||||
rxF_ext += NBR_RE_PER_RB_WITHOUT_DMRS;
|
||||
dl_ch0 += 12;
|
||||
const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11};
|
||||
for (int i = 0; i < sizeofArray(valid_re); i++) {
|
||||
*rxF_ext++ = rxF[valid_re[i]];
|
||||
*dl_ch0_ext++ = dl_ch0[valid_re[i]];
|
||||
}
|
||||
dl_ch0 += RE_PER_RB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define print_shorts(s,x) printf("%s %d,%d,%d,%d,%d,%d,%d,%d\n",s,(x)[0],(x)[1],(x)[2],(x)[3],(x)[4],(x)[5],(x)[6],(x)[7])
|
||||
|
||||
void nr_pdcch_channel_compensation(int32_t rx_size,
|
||||
c16_t rxdataF_ext[][rx_size],
|
||||
c16_t dl_ch_estimates_ext[][rx_size],
|
||||
c16_t rxdataF_comp[][rx_size],
|
||||
int32_t **rho,
|
||||
NR_DL_FRAME_PARMS *frame_parms,
|
||||
uint8_t symbol,
|
||||
uint8_t output_shift,
|
||||
uint32_t coreset_nbr_rb)
|
||||
static void nr_pdcch_channel_compensation(int arraySz,
|
||||
c16_t rxdataF_ext[][arraySz],
|
||||
c16_t dl_ch_estimates_ext[][arraySz],
|
||||
c16_t rxdataF_comp[][arraySz],
|
||||
int antRx,
|
||||
uint8_t output_shift)
|
||||
{
|
||||
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
|
||||
simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx][symbol * coreset_nbr_rb * 12];
|
||||
simde__m128i *rxdataF128 = (simde__m128i *)&rxdataF_ext[aarx][symbol * coreset_nbr_rb * 12];
|
||||
simde__m128i *rxdataF_comp128 = (simde__m128i *)&rxdataF_comp[aarx][symbol * coreset_nbr_rb * 12];
|
||||
//printf("ch compensation dl_ch ext addr %p \n", &dl_ch_estimates_ext[(aatx<<1)+aarx][symbol*20*12]);
|
||||
//printf("rxdataf ext addr %p symbol %d\n", &rxdataF_ext[aarx][symbol*20*12], symbol);
|
||||
//printf("rxdataf_comp addr %p\n",&rxdataF_comp[(aatx<<1)+aarx][symbol*20*12]);
|
||||
|
||||
// multiply by conjugated channel
|
||||
mult_cpx_conj_vector((c16_t *)dl_ch128, (c16_t *)rxdataF128, (c16_t *)rxdataF_comp128, 12 * coreset_nbr_rb, output_shift);
|
||||
|
||||
for (int rb = 0; rb < 12 * coreset_nbr_rb; rb++) {
|
||||
LOG_DDD("rxdataF128[%d]=(%d,%d) X dlch[%d]=(%d,%d) rxdataF_comp128[%d]=(%d,%d)\n",
|
||||
rb,
|
||||
((c16_t *)rxdataF128)[rb].r,
|
||||
((c16_t *)rxdataF128)[rb].i,
|
||||
rb,
|
||||
((c16_t *)dl_ch128)[rb].r,
|
||||
((c16_t *)dl_ch128)[rb].i,
|
||||
rb,
|
||||
((c16_t *)rxdataF_comp128)[rb].r,
|
||||
((c16_t *)rxdataF_comp128)[rb].i);
|
||||
|
||||
}
|
||||
for (int aarx = 0; aarx < antRx; aarx++) {
|
||||
// multiply by conjugated channel, this function require size in _m128i, else it doesn't process all samples
|
||||
mult_cpx_conj_vector(dl_ch_estimates_ext[aarx], rxdataF_ext[aarx], rxdataF_comp[aarx], arraySz, output_shift);
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_pdcch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, int32_t rx_size, c16_t rxdataF_comp[][rx_size], int symbol)
|
||||
static void nr_pdcch_detection_mrc(int sz, c16_t rxdataF_comp[][sz])
|
||||
{
|
||||
if (frame_parms->nb_antennas_rx>1) {
|
||||
const int sz = frame_parms->N_RB_DL * 12;
|
||||
|
||||
simde__m128i *rxdataF_comp128_0 = (simde__m128i *)&rxdataF_comp[0][symbol * sz];
|
||||
simde__m128i *rxdataF_comp128_1 = (simde__m128i *)&rxdataF_comp[1][symbol * sz];
|
||||
LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n");
|
||||
c16_t *rx0 = rxdataF_comp[0];
|
||||
c16_t *rx1 = rxdataF_comp[1];
|
||||
|
||||
// MRC on each re of rb
|
||||
for (int i = 0; i < sz >> 2; i++) {
|
||||
rxdataF_comp128_0[i] = simde_mm_adds_epi16(simde_mm_srai_epi16(rxdataF_comp128_0[i], 1), simde_mm_srai_epi16(rxdataF_comp128_1[i], 1));
|
||||
}
|
||||
// MRC on each re of rb
|
||||
// input always aligned and accepting tail padding to process all actual samples
|
||||
for (int i = 0; i < sz; i += 4) {
|
||||
*(simde__m128i *)(rx0 + i) =
|
||||
simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1), simde_mm_srai_epi16(*(simde__m128i *)(rx1 + i), 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,106 +343,67 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue,
|
||||
{
|
||||
NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
|
||||
|
||||
uint8_t log2_maxh, aarx;
|
||||
int32_t avgs;
|
||||
int32_t avgP[4];
|
||||
int n_rb,rb_offset;
|
||||
get_coreset_rballoc(rel15->coreset.frequency_domain_resource,&n_rb,&rb_offset);
|
||||
|
||||
// Pointers to extracted PDCCH symbols in frequency-domain.
|
||||
int32_t rx_size = ((4 * frame_parms->N_RB_DL * 12 + 31) >> 5) << 5;
|
||||
__attribute__((aligned(32))) c16_t rxdataF_ext[frame_parms->nb_antennas_rx][rx_size];
|
||||
__attribute__((aligned(32))) c16_t rxdataF_comp[frame_parms->nb_antennas_rx][rx_size];
|
||||
__attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[frame_parms->nb_antennas_rx][rx_size];
|
||||
|
||||
memset(rxdataF_comp, 0, sizeof(rxdataF_comp));
|
||||
const int antRx = frame_parms->nb_antennas_rx;
|
||||
|
||||
// Pointer to llrs, 4-bit resolution.
|
||||
int32_t llr_size = 4 * n_rb * 9;
|
||||
int32_t llr_size = (rel15->coreset.duration-rel15->coreset.StartSymbolIndex) * n_rb * RE_PER_RB_OUT_DMRS;
|
||||
c16_t llr[llr_size];
|
||||
|
||||
memset(llr, 0, sizeof(llr));
|
||||
|
||||
LOG_D(NR_PHY_DCI,
|
||||
"pdcch coreset: freq %x, n_rb %d, rb_offset %d\n",
|
||||
rel15->coreset.frequency_domain_resource[0],
|
||||
n_rb,
|
||||
rb_offset);
|
||||
for (int s=rel15->coreset.StartSymbolIndex; s<(rel15->coreset.StartSymbolIndex+rel15->coreset.duration); s++) {
|
||||
for (int s = rel15->coreset.StartSymbolIndex; s < rel15->coreset.StartSymbolIndex + rel15->coreset.duration; s++) {
|
||||
LOG_D(NR_PHY_DCI, "in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n");
|
||||
|
||||
const int arraySz = ceil_mod(n_rb * RE_PER_RB_OUT_DMRS, 32);
|
||||
__attribute__((aligned(32))) c16_t rxdataF_ext[antRx][arraySz];
|
||||
__attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][arraySz];
|
||||
nr_pdcch_extract_rbs_single(ue->frame_parms.samples_per_slot_wCP,
|
||||
n_rb,
|
||||
rxdataF,
|
||||
pdcch_est_size,
|
||||
pdcch_dl_ch_estimates,
|
||||
rx_size,
|
||||
arraySz,
|
||||
rxdataF_ext,
|
||||
pdcch_dl_ch_estimates_ext,
|
||||
s,
|
||||
frame_parms,
|
||||
rel15->coreset.frequency_domain_resource,
|
||||
n_rb,
|
||||
rel15->BWPStart);
|
||||
|
||||
LOG_D(NR_PHY_DCI,
|
||||
"we enter nr_pdcch_channel_level(avgP=%d) => compute channel level based on ofdm symbol 0, "
|
||||
"pdcch_vars[eNB_id]->dl_ch_estimates_ext\n",
|
||||
*avgP);
|
||||
LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_level(dl_ch_estimates_ext -> dl_ch_estimates_ext)\n");
|
||||
// compute channel level based on ofdm symbol 0
|
||||
nr_pdcch_channel_level(rx_size,
|
||||
pdcch_dl_ch_estimates_ext,
|
||||
frame_parms,
|
||||
avgP,
|
||||
s,
|
||||
n_rb);
|
||||
avgs = 0;
|
||||
|
||||
for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++)
|
||||
avgs = cmax(avgs, avgP[aarx]);
|
||||
|
||||
log2_maxh = (log2_approx(avgs) / 2) + 5; //+frame_parms->nb_antennas_rx;
|
||||
int32_t avgs = nr_pdcch_channel_level(n_rb, arraySz, pdcch_dl_ch_estimates_ext, antRx);
|
||||
uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx;
|
||||
|
||||
#ifdef UE_DEBUG_TRACE
|
||||
LOG_D(NR_PHY_DCI, "slot %d: pdcch log2_maxh = %d (%d,%d)\n", proc->nr_slot_rx, log2_maxh, avgP[0], avgs);
|
||||
#endif
|
||||
#if T_TRACER
|
||||
T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgP[0]), T_INT(avgP[1]), T_INT(avgP[2]), T_INT(avgP[3]));
|
||||
T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgs));
|
||||
#endif
|
||||
LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh);
|
||||
LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n");
|
||||
// compute LLRs for ofdm symbol 0 only
|
||||
nr_pdcch_channel_compensation(rx_size, rxdataF_ext,
|
||||
__attribute__((aligned(32))) c16_t rxdataF_comp[antRx][arraySz];
|
||||
nr_pdcch_channel_compensation(arraySz,
|
||||
rxdataF_ext,
|
||||
pdcch_dl_ch_estimates_ext,
|
||||
rxdataF_comp,
|
||||
NULL,
|
||||
frame_parms,
|
||||
s,
|
||||
log2_maxh,
|
||||
n_rb); // log2_maxh+I0_shift
|
||||
antRx,
|
||||
log2_maxh); // log2_maxh+I0_shift
|
||||
|
||||
UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), frame_parms->nb_antennas_rx, rx_size, 0);
|
||||
UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * RE_PER_RB_OUT_DMRS, 0);
|
||||
|
||||
if (frame_parms->nb_antennas_rx > 1) {
|
||||
LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc(frame_parms->nb_antennas_rx=%d)\n", frame_parms->nb_antennas_rx);
|
||||
nr_pdcch_detection_mrc(frame_parms, rx_size, rxdataF_comp,s);
|
||||
}
|
||||
if (antRx > 1)
|
||||
nr_pdcch_detection_mrc(arraySz, rxdataF_comp);
|
||||
|
||||
LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s);
|
||||
LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n");
|
||||
nr_pdcch_llr(frame_parms,
|
||||
rx_size,
|
||||
rxdataF_comp,
|
||||
llr,
|
||||
s,
|
||||
n_rb);
|
||||
|
||||
UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0);
|
||||
|
||||
#ifdef DEBUG_DCI_DECODING
|
||||
printf("demapping: slot %u, mi %d\n",slot,get_mi(frame_parms,slot));
|
||||
#endif
|
||||
nr_pdcch_llr(n_rb * RE_PER_RB_OUT_DMRS, rxdataF_comp[0], llr + s * n_rb * RE_PER_RB_OUT_DMRS);
|
||||
}
|
||||
UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0);
|
||||
|
||||
nr_pdcch_demapping_deinterleaving(llr,
|
||||
pdcch_e_rx,
|
||||
@@ -668,6 +528,6 @@ void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue,
|
||||
rel15->dci_format_options[k]);
|
||||
}
|
||||
}
|
||||
e_rx_cand_idx += 9 * L * 6; // e_rx index for next candidate (L CCEs, 6 REGs per CCE and 9 REs per REG )
|
||||
e_rx_cand_idx += RE_PER_RB_OUT_DMRS * L * 6; // e_rx index for next candidate (L CCEs, 6 REGs per CCE and 9 REs per REG )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,3 @@ MESSAGE_DEF(GTPV1U_ENB_DATA_FORWARDING_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_dat
|
||||
MESSAGE_DEF(GTPV1U_ENB_END_MARKER_REQ, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_req_t, Gtpv1uEndMarkerReq)
|
||||
MESSAGE_DEF(GTPV1U_ENB_END_MARKER_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_ind_t, Gtpv1uEndMarkerInd)
|
||||
MESSAGE_DEF(GTPV1U_REQ, MESSAGE_PRIORITY_MED, Gtpv1uReq, gtpv1uReq)
|
||||
MESSAGE_DEF(GTPV1U_DU_BUFFER_REPORT_REQ, MESSAGE_PRIORITY_MED, gtpv1u_DU_buffer_report_req_t, NRGtpv1uBufferReportReq)
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
|
||||
#define GTPV1U_REQ(mSGpTR) (mSGpTR)->ittiMsg.gtpv1uReq
|
||||
|
||||
#define GTPV1U_DU_BUFFER_REPORT_REQ(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uBufferReportReq
|
||||
|
||||
#define GTPV1U_ALL_TUNNELS_TEID (teid_t)0xFFFFFFFF
|
||||
|
||||
typedef struct gtpv1u_enb_create_x2u_tunnel_req_s {
|
||||
@@ -188,11 +186,4 @@ typedef struct gtpv1u_gnb_delete_tunnel_resp_s {
|
||||
teid_t gnb_NGu_teid; ///< local NGU Tunnel Endpoint Identifier to be deleted
|
||||
} gtpv1u_gnb_delete_tunnel_resp_t;
|
||||
|
||||
|
||||
typedef struct gtpv1u_DU_buffer_report_req_s {
|
||||
uint32_t buffer_availability;
|
||||
ue_id_t ue_id;
|
||||
int pdusession_id;
|
||||
} gtpv1u_DU_buffer_report_req_t;
|
||||
|
||||
#endif /* GTPV1_U_MESSAGES_TYPES_H_ */
|
||||
|
||||
@@ -1497,7 +1497,8 @@ static seq_arr_t *fill_du_sibs(paramdef_t *GNBparamarray)
|
||||
if (NODE_IS_DU(get_node_type()))
|
||||
AssertFatal(GNBparamarray[GNB_CU_SIBS_IDX].numelt == 0, "This is DU, do not input CU SIBs\n");
|
||||
|
||||
if (!config_isparamset(GNBparamarray, GNB_DU_SIBS_IDX))
|
||||
// TODO config_isparamset doesn't seem to work for array types, checking numelt instead
|
||||
if (GNBparamarray[GNB_DU_SIBS_IDX].numelt == 0)
|
||||
return NULL;
|
||||
|
||||
seq_arr_t *du_SIBs = malloc(sizeof(seq_arr_t));
|
||||
@@ -1796,12 +1797,13 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
beam_info->beams_per_period = beams_per_period;
|
||||
beam_info->beam_allocation_size = -1; // to be initialized once we have information on frame configuration
|
||||
}
|
||||
if (config_isparamset(MacRLC_ParamList.paramarray[j], MACRLC_BEAMWEIGHTS_IDX)) {
|
||||
// TODO config_isparamset doesn't seem to work for array types, checking numelt instead
|
||||
int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt;
|
||||
if (n > 0) {
|
||||
if (NFAPI_MODE == NFAPI_MONOLITHIC) {
|
||||
GET_PARAMS_LIST(L1_ParamList, L1_Params, L1PARAMS_DESC, CONFIG_STRING_L1_LIST, NULL);
|
||||
AssertFatal(*(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr) == 0, "No need to set beam weights in case of DAS\n");
|
||||
}
|
||||
int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt;
|
||||
int num_beam = n;
|
||||
if (!ab) {
|
||||
AssertFatal(n % num_tx == 0, "Error! Number of beam input needs to be multiple of TX antennas\n");
|
||||
@@ -1986,7 +1988,8 @@ static seq_arr_t *fill_cu_sibs(paramdef_t *GNBparamarray)
|
||||
if (NODE_IS_CU(get_node_type()))
|
||||
AssertFatal(GNBparamarray[GNB_DU_SIBS_IDX].numelt == 0, "This is CU, do not input DU SIBs\n");
|
||||
|
||||
if (!config_isparamset(GNBparamarray, GNB_CU_SIBS_IDX))
|
||||
// TODO config_isparamset doesn't seem to work for array types, checking numelt instead
|
||||
if (GNBparamarray[GNB_CU_SIBS_IDX].numelt == 0)
|
||||
return NULL;
|
||||
|
||||
seq_arr_t *SIBs = malloc(sizeof(seq_arr_t));
|
||||
|
||||
@@ -548,7 +548,7 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t
|
||||
}
|
||||
}
|
||||
|
||||
void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch)
|
||||
static void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch)
|
||||
{
|
||||
pdcch->otherSI_SS_id = -1;
|
||||
pdcch->ra_SS_id = -1;
|
||||
|
||||
@@ -59,8 +59,6 @@ void nr_ue_mac_default_configs(NR_UE_MAC_INST_t *mac);
|
||||
|
||||
void nr_ue_decode_mib(NR_UE_MAC_INST_t *mac, int cc_id);
|
||||
|
||||
void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch);
|
||||
|
||||
/**\brief decode SIB1 and other SIs pdus in NR_UE, from if_module dl_ind
|
||||
\param mac pointer to MAC instance
|
||||
\param cc_id component carrier id
|
||||
@@ -117,12 +115,6 @@ void nr_ue_dl_scheduler(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info
|
||||
|
||||
csi_payload_t nr_ue_aperiodic_csi_reporting(NR_UE_MAC_INST_t *mac, dci_field_t csi_request, int tda, long *K2);
|
||||
|
||||
/*! \fn int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id);
|
||||
\brief This function schedules a positive or negative SR for schedulingRequestID sr_id
|
||||
depending on the presence of any active SR and the prohibit timer.
|
||||
If the max number of retransmissions is reached, it triggers a new RA */
|
||||
int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id);
|
||||
|
||||
nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci);
|
||||
|
||||
int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac,
|
||||
@@ -138,30 +130,11 @@ bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac,
|
||||
|
||||
int nr_get_csi_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sched_t *pucch);
|
||||
|
||||
csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig);
|
||||
|
||||
csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
CSI_mapping_t mapping_type);
|
||||
|
||||
csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig);
|
||||
|
||||
csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac,
|
||||
int csi_report_id,
|
||||
CSI_mapping_t mapping_type,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig);
|
||||
|
||||
uint8_t get_rsrp_index(int rsrp);
|
||||
uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp);
|
||||
|
||||
/* \brief Get payload (MAC PDU) from UE PHY
|
||||
@param dl_info pointer to dl indication
|
||||
@param ul_time_alignment pointer to timing advance parameters
|
||||
@@ -170,8 +143,6 @@ uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp);
|
||||
*/
|
||||
void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id);
|
||||
|
||||
void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac,nr_downlink_indication_t *dl_info, int pdu_id);
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
NR_BSR_SHORT s;
|
||||
@@ -187,28 +158,12 @@ int nr_write_ce_ulsch_pdu(uint8_t *mac_ce,
|
||||
const type_bsr_t *bsr,
|
||||
uint8_t *mac_ce_end);
|
||||
|
||||
void config_dci_pdu(NR_UE_MAC_INST_t *mac,
|
||||
fapi_nr_dl_config_request_t *dl_config,
|
||||
const int rnti_type,
|
||||
const int slot,
|
||||
const NR_SearchSpace_t *ss);
|
||||
|
||||
void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl_config, const frame_t frame, const int slot);
|
||||
void set_precoding_information_parameters(nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu,
|
||||
int n_antenna_port,
|
||||
long transformPrecoder,
|
||||
int precoding_information,
|
||||
NR_PUSCH_Config_t *pusch_Config);
|
||||
void set_harq_status(NR_UE_MAC_INST_t *mac,
|
||||
uint8_t pucch_id,
|
||||
uint8_t harq_id,
|
||||
int8_t delta_pucch,
|
||||
uint16_t data_toul_fb,
|
||||
uint8_t dai,
|
||||
int n_CCE,
|
||||
int N_CCE,
|
||||
frame_t frame,
|
||||
int slot);
|
||||
|
||||
bool get_downlink_ack(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sched_t *pucch);
|
||||
initial_pucch_resource_t get_initial_pucch_resource(const int idx);
|
||||
@@ -285,11 +240,9 @@ and fills the PRACH PDU per each FD occasion.
|
||||
@param slotP Slot index
|
||||
@returns void
|
||||
*/
|
||||
void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP);
|
||||
void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot);
|
||||
void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot);
|
||||
|
||||
void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
NR_CSI_RS_ResourceMapping_t *resourceMapping,
|
||||
const NR_CSI_RS_ResourceMapping_t *resourceMapping,
|
||||
uint32_t bwp_size,
|
||||
uint32_t bwp_start);
|
||||
|
||||
|
||||
@@ -104,11 +104,11 @@ NR_ControlResourceSet_t *ue_get_coreset(const NR_BWP_PDCCH_t *config, const int
|
||||
return coreset;
|
||||
}
|
||||
|
||||
void config_dci_pdu(NR_UE_MAC_INST_t *mac,
|
||||
fapi_nr_dl_config_request_t *dl_config,
|
||||
const int rnti_type,
|
||||
const int slot,
|
||||
const NR_SearchSpace_t *ss)
|
||||
static void config_dci_pdu(NR_UE_MAC_INST_t *mac,
|
||||
fapi_nr_dl_config_request_t *dl_config,
|
||||
const int rnti_type,
|
||||
const int slot,
|
||||
const NR_SearchSpace_t *ss)
|
||||
{
|
||||
const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
|
||||
const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
|
||||
@@ -385,12 +385,14 @@ bool search_space_monitoring_ocasion_other_si(NR_UE_MAC_INST_t *mac,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_window_valid(NR_UE_MAC_INST_t *mac, int window_slots, int abs_slot)
|
||||
static bool is_window_valid(NR_UE_MAC_INST_t *mac, int window_slots, int abs_slot, int slots_per_frame)
|
||||
{
|
||||
if (mac->si_SchedInfo.si_window_start == -1) {
|
||||
// out of window
|
||||
return false;
|
||||
} else if (abs_slot > mac->si_SchedInfo.si_window_start + window_slots) {
|
||||
} else if ((abs_slot - mac->si_SchedInfo.si_window_start + MAX_FRAME_NUMBER * slots_per_frame)
|
||||
% (MAX_FRAME_NUMBER * slots_per_frame)
|
||||
>= window_slots) {
|
||||
// window expired
|
||||
mac->si_SchedInfo.si_window_start = -1;
|
||||
return false;
|
||||
@@ -407,33 +409,18 @@ static bool monitor_dci_for_other_SI(NR_UE_MAC_INST_t *mac,
|
||||
{
|
||||
// according to 5.2.2.3.2 in 331
|
||||
const int abs_slot = frame * slots_per_frame + slot;
|
||||
const si_schedinfo_config_t *config = mac->si_SchedInfo.si_SchedInfo_list.array[si_idx];
|
||||
const int window_slots = 5 << mac->si_SchedInfo.si_WindowLength;
|
||||
const int x = (config->si_WindowPosition - 1) * window_slots;
|
||||
const int T = 8 << config->si_Periodicity; // radio frame periodicity
|
||||
|
||||
si_schedinfo_config_t *config = mac->si_SchedInfo.si_SchedInfo_list.array[si_idx];
|
||||
int window_slots = 5 << mac->si_SchedInfo.si_WindowLength;
|
||||
int x = (config->si_WindowPosition - 1) * window_slots;
|
||||
int T = 8 << config->si_Periodicity; // radio frame periodicity
|
||||
bool check_valid;
|
||||
switch (config->type) {
|
||||
case NR_SI_INFO :
|
||||
if (mac->si_SchedInfo.si_window_start == -1) {
|
||||
if ((frame % T) == (x / slots_per_frame) && (x % slots_per_frame == 0))
|
||||
mac->si_SchedInfo.si_window_start = abs_slot; // in terms of absolute slot number
|
||||
}
|
||||
check_valid = is_window_valid(mac, window_slots, abs_slot);
|
||||
if (check_valid && search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame))
|
||||
return true;
|
||||
break;
|
||||
case NR_SI_INFO_v1700 :
|
||||
if (mac->si_SchedInfo.si_window_start == -1) {
|
||||
if ((frame % T == floor(x / slots_per_frame)) && (slot == x % slots_per_frame))
|
||||
mac->si_SchedInfo.si_window_start = abs_slot;
|
||||
}
|
||||
if (is_window_valid(mac, window_slots, abs_slot))
|
||||
return search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame);
|
||||
break;
|
||||
default :
|
||||
AssertFatal(false, "Invalid SI-SchedulingInfo case\n");
|
||||
if (mac->si_SchedInfo.si_window_start == -1) {
|
||||
if ((frame % T) == (x / slots_per_frame) && (slot == x % slots_per_frame))
|
||||
mac->si_SchedInfo.si_window_start = abs_slot; // in terms of absolute slot number
|
||||
}
|
||||
if (is_window_valid(mac, window_slots, abs_slot, slots_per_frame))
|
||||
return search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,43 @@ random-access procedure
|
||||
*/
|
||||
static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id);
|
||||
|
||||
/*! \fn int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id);
|
||||
\brief This function schedules a positive or negative SR for schedulingRequestID sr_id
|
||||
depending on the presence of any active SR and the prohibit timer.
|
||||
If the max number of retransmissions is reached, it triggers a new RA */
|
||||
static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id);
|
||||
|
||||
static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig);
|
||||
|
||||
static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
CSI_mapping_t mapping_type);
|
||||
static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig);
|
||||
static uint8_t get_rsrp_index(int rsrp);
|
||||
|
||||
static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp);
|
||||
|
||||
static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id);
|
||||
|
||||
static void set_harq_status(NR_UE_MAC_INST_t *mac,
|
||||
uint8_t pucch_id,
|
||||
uint8_t harq_id,
|
||||
int8_t delta_pucch,
|
||||
uint16_t data_toul_fb,
|
||||
uint8_t dai,
|
||||
int n_CCE,
|
||||
int N_CCE,
|
||||
frame_t frame,
|
||||
int slot);
|
||||
|
||||
int get_pucch0_mcs(const int O_ACK, const int O_SR, const int ack_payload, const int sr_payload)
|
||||
{
|
||||
int mcs = 0;
|
||||
@@ -241,26 +278,27 @@ void nr_ue_decode_mib(NR_UE_MAC_INST_t *mac, int cc_id)
|
||||
}
|
||||
|
||||
static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu,
|
||||
fapi_nr_dl_config_request_t *dl_config,
|
||||
const fapi_nr_dl_config_request_t *dl_config,
|
||||
int rnti_type,
|
||||
int frame,
|
||||
int slot,
|
||||
int mu,
|
||||
int slots_per_frame,
|
||||
NR_PDSCH_Config_t *pdsch_config)
|
||||
const NR_PDSCH_Config_t *pdsch_config)
|
||||
{
|
||||
// only for C-RNTI, MCS-C-RNTI, CS-RNTI (and only C-RNTI is supported for now)
|
||||
if (rnti_type != TYPE_C_RNTI_)
|
||||
return;
|
||||
|
||||
if (pdsch_config && pdsch_config->zp_CSI_RS_ResourceToAddModList) {
|
||||
for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) {
|
||||
NR_ZP_CSI_RS_Resource_t *zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i];
|
||||
NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId;
|
||||
NR_SetupRelease_ZP_CSI_RS_ResourceSet_t *zp_set = pdsch_config->p_ZP_CSI_RS_ResourceSet;
|
||||
AssertFatal(zp_set && zp_set->choice.setup, "Only periodic ZP resource set is implemented\n");
|
||||
bool found = false;
|
||||
for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) {
|
||||
bool found = false;
|
||||
const NR_SetupRelease_ZP_CSI_RS_ResourceSet_t *zp_set = pdsch_config->p_ZP_CSI_RS_ResourceSet;
|
||||
AssertFatal(zp_set && zp_set->choice.setup, "Only periodic ZP resource set is implemented\n");
|
||||
const NR_ZP_CSI_RS_Resource_t *zp_res = NULL;
|
||||
for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) {
|
||||
for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) {
|
||||
zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i];
|
||||
NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId;
|
||||
if (*zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.array[j] == id) {
|
||||
found = true;
|
||||
break;
|
||||
@@ -283,7 +321,7 @@ static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsc
|
||||
|
||||
for (int i = 0; i < dl_config->number_pdus; i++) {
|
||||
// This assumes that CSI-RS are scheduled before this moment which is true in current implementation
|
||||
fapi_nr_dl_config_request_pdu_t *csi_req = &dl_config->dl_config_list[i];
|
||||
const fapi_nr_dl_config_request_pdu_t *csi_req = &dl_config->dl_config_list[i];
|
||||
if (csi_req->pdu_type == FAPI_NR_DL_CONFIG_TYPE_CSI_RS) {
|
||||
AssertFatal(dlsch_pdu->numCsiRsForRateMatching < NFAPI_MAX_NUM_CSI_RATEMATCH, "csiRsForRateMatching out of bounds\n");
|
||||
dlsch_pdu->csiRsForRateMatching[dlsch_pdu->numCsiRsForRateMatching] = csi_req->csirs_config_pdu.csirs_config_rel15;
|
||||
@@ -310,6 +348,7 @@ void nr_ue_decode_BCCH_DL_SCH(NR_UE_MAC_INST_t *mac,
|
||||
if (mac->get_otherSI[i])
|
||||
mac->get_otherSI[i] = false;
|
||||
}
|
||||
mac->si_SchedInfo.si_window_start = -1;
|
||||
T(T_NRUE_MAC_DL_PDU_WITH_DATA,
|
||||
T_INT(SI_RNTI),
|
||||
T_INT(-1 /* frame, unavailable here */),
|
||||
@@ -1434,16 +1473,16 @@ int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_harq_status(NR_UE_MAC_INST_t *mac,
|
||||
uint8_t pucch_id,
|
||||
uint8_t harq_id,
|
||||
int8_t delta_pucch,
|
||||
uint16_t data_toul_fb,
|
||||
uint8_t dai,
|
||||
int n_CCE,
|
||||
int N_CCE,
|
||||
frame_t frame,
|
||||
int slot)
|
||||
static void set_harq_status(NR_UE_MAC_INST_t *mac,
|
||||
uint8_t pucch_id,
|
||||
uint8_t harq_id,
|
||||
int8_t delta_pucch,
|
||||
uint16_t data_toul_fb,
|
||||
uint8_t dai,
|
||||
int n_CCE,
|
||||
int N_CCE,
|
||||
frame_t frame,
|
||||
int slot)
|
||||
{
|
||||
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[harq_id];
|
||||
current_harq->active = true;
|
||||
@@ -2519,7 +2558,7 @@ bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *p
|
||||
return sr_count > 0;
|
||||
}
|
||||
|
||||
int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id)
|
||||
static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id)
|
||||
{
|
||||
// no UL-SCH resources available for this tti && UE has a valid PUCCH resources for SR configuration for this tti
|
||||
NR_UE_SCHEDULING_INFO *si = &mac->scheduling_info;
|
||||
@@ -2803,11 +2842,10 @@ csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac,
|
||||
return csi;
|
||||
}
|
||||
|
||||
|
||||
csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig)
|
||||
static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig)
|
||||
{
|
||||
int nb_ssb = 0; // nb of ssb in the resource
|
||||
int nb_meas = 0; // nb of ssb to report measurements on
|
||||
@@ -2884,11 +2922,11 @@ csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
return csi;
|
||||
}
|
||||
|
||||
csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
CSI_mapping_t mapping_type)
|
||||
static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
CSI_mapping_t mapping_type)
|
||||
{
|
||||
int p1_bits = 0;
|
||||
int p2_bits = 0;
|
||||
@@ -2972,10 +3010,10 @@ csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
return csi;
|
||||
}
|
||||
|
||||
csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig)
|
||||
static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
struct NR_CSI_ReportConfig *csi_reportconfig,
|
||||
NR_CSI_ResourceConfigId_t csi_ResourceConfigId,
|
||||
const NR_CSI_MeasConfig_t *csi_MeasConfig)
|
||||
{
|
||||
int n_bits = 0;
|
||||
uint64_t temp_payload = 0;
|
||||
@@ -3039,8 +3077,8 @@ csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
// returns index from RSRP
|
||||
// according to Table 10.1.6.1-1 in 38.133
|
||||
|
||||
uint8_t get_rsrp_index(int rsrp) {
|
||||
|
||||
static uint8_t get_rsrp_index(int rsrp)
|
||||
{
|
||||
int index = rsrp + 157;
|
||||
if (rsrp>-44)
|
||||
index = 113;
|
||||
@@ -3050,17 +3088,15 @@ uint8_t get_rsrp_index(int rsrp) {
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
// returns index from differential RSRP
|
||||
// according to Table 10.1.6.1-2 in 38.133
|
||||
uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp) {
|
||||
|
||||
static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp)
|
||||
{
|
||||
int diff = best_rsrp-current_rsrp;
|
||||
if (diff>30)
|
||||
return 15;
|
||||
else
|
||||
return (diff>>1);
|
||||
|
||||
}
|
||||
|
||||
void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
|
||||
@@ -3689,7 +3725,7 @@ static int nr_ue_validate_successrar(uint8_t *pduP,
|
||||
// F: lenght of L is 0:8 or 1:16 bits wide
|
||||
// R: Reserved bit, set to zero.
|
||||
////////////////////////////////
|
||||
void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
|
||||
static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id)
|
||||
{
|
||||
frame_t frameP = dl_info->frame;
|
||||
int slot = dl_info->slot;
|
||||
|
||||
@@ -78,6 +78,10 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac,
|
||||
int P_MAX,
|
||||
bool *BSRsent);
|
||||
|
||||
static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP);
|
||||
static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot);
|
||||
static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot);
|
||||
|
||||
static void clear_ul_config_request(NR_UE_MAC_INST_t *mac)
|
||||
{
|
||||
int slots = mac->frame_structure.numb_slots_frame;
|
||||
@@ -1709,7 +1713,7 @@ static bool schedule_uci_on_pusch(NR_UE_MAC_INST_t *mac,
|
||||
return mux_done;
|
||||
}
|
||||
|
||||
void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP)
|
||||
static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP)
|
||||
{
|
||||
PUCCH_sched_t pucch[3] = {0}; // TODO the size might change in the future in case of multiple SR or multiple CSI in a slot
|
||||
|
||||
@@ -1784,7 +1788,7 @@ void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP)
|
||||
}
|
||||
}
|
||||
|
||||
void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot)
|
||||
static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot)
|
||||
{
|
||||
if (!mac->sc_info.csi_MeasConfig)
|
||||
return;
|
||||
@@ -1919,7 +1923,7 @@ uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI
|
||||
}
|
||||
|
||||
void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
|
||||
NR_CSI_RS_ResourceMapping_t *resourceMapping,
|
||||
const NR_CSI_RS_ResourceMapping_t *resourceMapping,
|
||||
uint32_t bwp_size,
|
||||
uint32_t bwp_start)
|
||||
{
|
||||
@@ -2025,7 +2029,7 @@ void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_c
|
||||
}
|
||||
}
|
||||
|
||||
void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot)
|
||||
static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot)
|
||||
{
|
||||
if (!mac->sc_info.csi_MeasConfig)
|
||||
return;
|
||||
|
||||
@@ -775,7 +775,7 @@ static bool test_other_sib_sched_occasion(int window_pos,
|
||||
int rel_frame,
|
||||
int rel_slot)
|
||||
{
|
||||
int x = window_pos * window_len;
|
||||
int x = (window_pos - 1) * window_len;
|
||||
int T = 8 << period;
|
||||
int test_frame = (frame - rel_frame) % MAX_FRAME_NUMBER;
|
||||
int si_slot = (x % n_slots_frame) + rel_slot;
|
||||
@@ -848,7 +848,7 @@ void schedule_nr_other_sib(module_id_t module_idP,
|
||||
for (int ssb = 0; ssb < num_ssb; ssb++) {
|
||||
for (int i = 0; i < schedInfo->schedulingInfoList.list.count; i++) {
|
||||
NR_SchedulingInfo_t *schedulingInfo = schedInfo->schedulingInfoList.list.array[i];
|
||||
if (test_other_sib_sched_occasion(i,
|
||||
if (test_other_sib_sched_occasion(i + 1,
|
||||
window_length_sl,
|
||||
schedulingInfo->si_Periodicity,
|
||||
n_slots_frame,
|
||||
|
||||
@@ -2418,9 +2418,7 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si
|
||||
schedulingInfo->si_BroadcastStatus = NR_SchedulingInfo__si_BroadcastStatus_broadcasting;
|
||||
schedulingInfo->si_Periodicity = NR_SchedulingInfo__si_Periodicity_rf16;
|
||||
bool reg_sib = false;
|
||||
NR_SI_SchedulingInfo_v1700_t *si_schedulingInfo_v17 = NULL;
|
||||
NR_SchedulingInfo2_r17_t *schedulingInfo2_r17 = NULL;
|
||||
bool v17_sib = false;
|
||||
for (int i = 0; i < num_sibs; i++) {
|
||||
int sib_num = sibs[i];
|
||||
AssertFatal(sib_num > 1 && sib_num < 21, "other SIB invalid type\n");
|
||||
@@ -2438,8 +2436,7 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si
|
||||
}
|
||||
asn1cSeqAdd(&schedulingInfo->sib_MappingInfo.list, mapping);
|
||||
} else {
|
||||
if (v17_sib == false) {
|
||||
si_schedulingInfo_v17 = calloc(1, sizeof(*si_schedulingInfo_v17));
|
||||
if (schedulingInfo2_r17 == NULL) {
|
||||
schedulingInfo2_r17 = calloc(1, sizeof(*schedulingInfo2_r17));
|
||||
schedulingInfo2_r17->si_BroadcastStatus_r17 = NR_SchedulingInfo2_r17__si_BroadcastStatus_r17_broadcasting;
|
||||
schedulingInfo2_r17->si_Periodicity_r17 = NR_SchedulingInfo2_r17__si_Periodicity_r17_rf16;
|
||||
@@ -2458,7 +2455,8 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si
|
||||
AssertFatal(reg_sib, "At least 1 SIB from SchedulingInfo (SIB2 to SIB14 included) needs to be present\n");
|
||||
asn1cSeqAdd(&info->schedulingInfoList.list, schedulingInfo);
|
||||
sib1->si_SchedulingInfo = info;
|
||||
if (si_schedulingInfo_v17) {
|
||||
if (schedulingInfo2_r17) {
|
||||
NR_SI_SchedulingInfo_v1700_t *si_schedulingInfo_v17 = calloc_or_fail(1, sizeof(*si_schedulingInfo_v17));
|
||||
asn1cSeqAdd(&si_schedulingInfo_v17->schedulingInfoList2_r17.list, schedulingInfo2_r17);
|
||||
NR_SIB1_v1610_IEs_t *sib1_v1610 = NULL;
|
||||
if (sib1->nonCriticalExtension)
|
||||
|
||||
@@ -7721,9 +7721,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
|
||||
|
||||
case X2AP_HANDOVER_REQ_ACK: {
|
||||
struct rrc_eNB_ue_context_s *ue_context_p = NULL;
|
||||
x2ap_handover_req_ack_t *x2ap_handover_req_ack = NULL;
|
||||
hashtable_rc_t hash_rc = HASH_TABLE_KEY_NOT_EXISTS;
|
||||
gtpv1u_ue_data_t *gtpv1u_ue_data_p = NULL;
|
||||
ue_context_p = rrc_eNB_get_ue_context(RC.rrc[instance], X2AP_HANDOVER_REQ_ACK(msg_p).rnti);
|
||||
|
||||
if (ue_context_p == NULL) {
|
||||
@@ -7738,40 +7735,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
|
||||
if (ue_context_p->ue_context.handover_info->state != HO_REQUEST)
|
||||
abort();
|
||||
|
||||
hash_rc = hashtable_get(RC.gtpv1u_data_g->ue_mapping, ue_context_p->ue_context.rnti, (void **)>pv1u_ue_data_p);
|
||||
|
||||
/* set target enb gtp teid */
|
||||
if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) {
|
||||
LOG_E(RRC, "X2AP_HANDOVER_REQ_ACK func(), hashtable_get failed: while getting ue rnti %x in hashtable ue_mapping\n", ue_context_p->ue_context.rnti);
|
||||
} else {
|
||||
uint8_t nb_e_rabs_tobesetup = 0;
|
||||
ebi_t eps_bearer_id = 0;
|
||||
int ip_offset = 0;
|
||||
in_addr_t in_addr;
|
||||
x2ap_handover_req_ack = &X2AP_HANDOVER_REQ_ACK(msg_p);
|
||||
nb_e_rabs_tobesetup = x2ap_handover_req_ack->nb_e_rabs_tobesetup;
|
||||
ue_context_p->ue_context.nb_x2u_e_rabs = nb_e_rabs_tobesetup;
|
||||
|
||||
for (int i = 0; i < nb_e_rabs_tobesetup; i++) {
|
||||
ip_offset = 0;
|
||||
eps_bearer_id = x2ap_handover_req_ack->e_rabs_tobesetup[i].e_rab_id;
|
||||
ue_context_p->ue_context.enb_gtp_x2u_ebi[i] = eps_bearer_id;
|
||||
ue_context_p->ue_context.enb_gtp_x2u_teid[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid;
|
||||
gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].teid_teNB = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid;
|
||||
|
||||
if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 4) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) {
|
||||
in_addr = *((in_addr_t *)x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer);
|
||||
ip_offset = 4;
|
||||
gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip_addr = in_addr;
|
||||
ue_context_p->ue_context.enb_gtp_x2u_addrs[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr;
|
||||
}
|
||||
|
||||
if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 16) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) {
|
||||
memcpy(gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip6_addr.s6_addr, &x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer[ip_offset], 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rrc_eNB_process_handoverCommand(instance, ue_context_p, &X2AP_HANDOVER_REQ_ACK(msg_p));
|
||||
ue_context_p->ue_context.handover_info->state = HO_PREPARE;
|
||||
break;
|
||||
|
||||
+515
-559
File diff suppressed because it is too large
Load Diff
+27
-10
@@ -1,16 +1,40 @@
|
||||
#ifndef __GTPUNEW_ITF_H__
|
||||
#define __GTPUNEW_ITF_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define GTPNOK -1
|
||||
|
||||
# define GTPU_HEADER_OVERHEAD_MAX 64
|
||||
|
||||
#include "openair2/COMMON/gtpv1_u_messages_types.h"
|
||||
#include "common/utils/hashtable/hashtable.h"
|
||||
#include "common/platform_types.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* forward declaration */
|
||||
struct protocol_ctxt_s;
|
||||
typedef struct protocol_ctxt_s protocol_ctxt_t;
|
||||
struct gtpv1u_enb_create_tunnel_req_s;
|
||||
typedef struct gtpv1u_enb_create_tunnel_req_s gtpv1u_enb_create_tunnel_req_t;
|
||||
struct gtpv1u_enb_create_tunnel_resp_s;
|
||||
typedef struct gtpv1u_enb_create_tunnel_resp_s gtpv1u_enb_create_tunnel_resp_t;
|
||||
struct gtpv1u_enb_delete_tunnel_req_s;
|
||||
typedef struct gtpv1u_enb_delete_tunnel_req_s gtpv1u_enb_delete_tunnel_req_t;
|
||||
struct gtpv1u_enb_create_x2u_tunnel_req_s;
|
||||
typedef struct gtpv1u_enb_create_x2u_tunnel_req_s gtpv1u_enb_create_x2u_tunnel_req_t;
|
||||
struct gtpv1u_enb_create_x2u_tunnel_resp_s;
|
||||
typedef struct gtpv1u_enb_create_x2u_tunnel_resp_s gtpv1u_enb_create_x2u_tunnel_resp_t;
|
||||
struct gtpv1u_gnb_create_tunnel_req_s;
|
||||
typedef struct gtpv1u_gnb_create_tunnel_req_s gtpv1u_gnb_create_tunnel_req_t;
|
||||
struct gtpv1u_gnb_create_tunnel_resp_s;
|
||||
typedef struct gtpv1u_gnb_create_tunnel_resp_s gtpv1u_gnb_create_tunnel_resp_t;
|
||||
struct gtpv1u_gnb_delete_tunnel_req_s;
|
||||
typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t;
|
||||
|
||||
typedef bool (*gtpCallback)(protocol_ctxt_t *ctxt_pP,
|
||||
const srb_flag_t srb_flagP,
|
||||
const rb_id_t rb_idP,
|
||||
@@ -52,7 +76,6 @@ extern "C" {
|
||||
|
||||
// the init function create a gtp instance and return the gtp instance id
|
||||
// the parameter originInstance will be sent back in each message from gtp to the creator
|
||||
void gtpv1uReceiver(int h);
|
||||
void gtpv1uProcessTimeout(int handle,void *arg);
|
||||
int gtpv1u_create_s1u_tunnel(const instance_t instance,
|
||||
const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req,
|
||||
@@ -91,7 +114,6 @@ extern "C" {
|
||||
teid_t teid,
|
||||
int outgoing_qfi,
|
||||
transport_layer_addr_t remoteAddr,
|
||||
int port,
|
||||
gtpCallback callBack,
|
||||
gtpCallbackSDAP callBackSDAP);
|
||||
|
||||
@@ -110,13 +132,8 @@ extern "C" {
|
||||
instance_t gtpv1Init(openAddr_t context);
|
||||
void *gtpv1uTask(void *args);
|
||||
|
||||
// Legacy to fix
|
||||
typedef struct gtpv1u_data_s {
|
||||
/* RB tree of UEs */
|
||||
hash_table_t *ue_mapping;
|
||||
} gtpv1u_data_t;
|
||||
#define GTPV1U_BEARER_OFFSET 3
|
||||
#define GTPV1U_MAX_BEARERS_ID (max_val_LTE_DRB_Identity - GTPV1U_BEARER_OFFSET)
|
||||
#define GTPV1U_MAX_BEARERS_ID (32 - GTPV1U_BEARER_OFFSET) /* max_val_LTE_DRB_Identity */
|
||||
typedef enum {
|
||||
BEARER_DOWN = 0,
|
||||
BEARER_IN_CONFIG,
|
||||
|
||||
@@ -485,19 +485,19 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in
|
||||
char *shlibversion = NULL; // version of the LDPC coding library
|
||||
paramdef_t LoaderParams_shlibversion[] = {{"shlibversion", NULL, 0, .strptr = &shlibversion, .defstrval = NULL, TYPE_STRING, 0, NULL}};
|
||||
config_get(config_get_if(), LoaderParams_shlibversion, sizeofArray(LoaderParams_shlibversion), "loader.ldpc");
|
||||
if (shlibversion != NULL && strncmp(shlibversion, "_t2", 3) == 0) {
|
||||
uint32_t eal_init_bbdev = 0; // If not 0 then include the BBDEV device in the EAL init for FHI
|
||||
if (shlibversion != NULL && strncmp(shlibversion, "_aal", 4) == 0) {
|
||||
uint32_t is_t2 = 0; // If not 0 then include the BBDEV device in the EAL init for FHI
|
||||
char *dpdk_dev = NULL; // PCI address of the card
|
||||
char *vfio_vf_token = NULL; // vfio token for the bbdev card
|
||||
paramdef_t LoaderParams[] = {
|
||||
{"eal_init_bbdev", NULL, 0, .uptr = &eal_init_bbdev, .defuintval = 0, TYPE_UINT, 0, NULL},
|
||||
{"is_t2", NULL, 0, .uptr = &is_t2, .defuintval = 0, TYPE_UINT, 0, NULL},
|
||||
{"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL},
|
||||
{"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL}
|
||||
};
|
||||
config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2");
|
||||
config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal");
|
||||
|
||||
if (eal_init_bbdev != 0) {
|
||||
AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_t2.dpdk_dev was not provided");
|
||||
if (!is_t2) {
|
||||
AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_aal.dpdk_dev was not provided");
|
||||
snprintf(&bbdev_dev[0], sizeof(bbdev_dev), "%s", dpdk_dev);
|
||||
io_cfg->bbdev_dev[0] = &bbdev_dev[0]; // BBDev dev name; max devices = 1
|
||||
if(vfio_vf_token != NULL) {
|
||||
|
||||
@@ -733,6 +733,10 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t,
|
||||
timestamp,
|
||||
timestamp + nsamps,
|
||||
signal_energy(samplesVoid[0], nsamps));
|
||||
|
||||
/* trace only first antenna */
|
||||
T(T_USRP_TX_ANT0, T_INT(timestamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1)));
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
@@ -1073,6 +1077,10 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest
|
||||
*ptimestamp,
|
||||
t->nextRxTstamp,
|
||||
signal_energy(samplesVoid[0], nsamps));
|
||||
|
||||
/* trace only first antenna */
|
||||
T(T_USRP_RX_ANT0, T_INT(t->nextRxTstamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1)));
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
Active_eNBs = ( "eNB-CU-Eurecom-LTEBox");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
eNBs = (
|
||||
{
|
||||
////////// Identification parameters:
|
||||
eNB_ID = 0xe00;
|
||||
|
||||
cell_type = "CELL_MACRO_ENB";
|
||||
|
||||
eNB_name = "eNB-CU-Eurecom-LTEBox";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } )
|
||||
|
||||
nr_cellid = 12345678L
|
||||
|
||||
tr_s_preference = "f1"
|
||||
|
||||
remote_s_address = "192.168.12.4";
|
||||
local_s_address = "192.168.12.45";
|
||||
local_s_portc = 501;
|
||||
remote_s_portc = 500;
|
||||
local_s_portd = 601;
|
||||
remote_s_portd = 600;
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
component_carriers = (
|
||||
{
|
||||
node_function = "3GPP_eNodeB";
|
||||
node_timing = "synch_to_ext_device";
|
||||
node_synch_ref = 0;
|
||||
frame_type = "FDD";
|
||||
tdd_config = 3;
|
||||
tdd_config_s = 0;
|
||||
prefix_type = "NORMAL";
|
||||
eutra_band = 7;
|
||||
downlink_frequency = 2665000000L;
|
||||
uplink_frequency_offset = -120000000;
|
||||
Nid_cell = 0;
|
||||
N_RB_DL = 50;
|
||||
pbch_repetition = "FALSE";
|
||||
prach_root = 0;
|
||||
prach_config_index = 0;
|
||||
prach_high_speed = "DISABLE";
|
||||
prach_zero_correlation = 1;
|
||||
prach_freq_offset = 2;
|
||||
pucch_delta_shift = 1;
|
||||
pucch_nRB_CQI = 0;
|
||||
pucch_nCS_AN = 0;
|
||||
pucch_n1_AN = 0;
|
||||
pdsch_referenceSignalPower = -27;
|
||||
pdsch_p_b = 0;
|
||||
pusch_n_SB = 1;
|
||||
pusch_enable64QAM = "DISABLE";
|
||||
pusch_hoppingMode = "interSubFrame";
|
||||
pusch_hoppingOffset = 0;
|
||||
pusch_groupHoppingEnable = "ENABLE";
|
||||
pusch_groupAssignment = 0;
|
||||
pusch_sequenceHoppingEnabled = "DISABLE";
|
||||
pusch_nDMRS1 = 1;
|
||||
phich_duration = "NORMAL";
|
||||
phich_resource = "ONESIXTH";
|
||||
srs_enable = "DISABLE";
|
||||
/*
|
||||
srs_BandwidthConfig =;
|
||||
srs_SubframeConfig =;
|
||||
srs_ackNackST =;
|
||||
srs_MaxUpPts =;
|
||||
*/
|
||||
|
||||
pusch_p0_Nominal = -96;
|
||||
pusch_alpha = "AL1";
|
||||
pucch_p0_Nominal = -104;
|
||||
msg3_delta_Preamble = 6;
|
||||
pucch_deltaF_Format1 = "deltaF2";
|
||||
pucch_deltaF_Format1b = "deltaF3";
|
||||
pucch_deltaF_Format2 = "deltaF0";
|
||||
pucch_deltaF_Format2a = "deltaF0";
|
||||
pucch_deltaF_Format2b = "deltaF0";
|
||||
|
||||
rach_numberOfRA_Preambles = 64;
|
||||
rach_preamblesGroupAConfig = "DISABLE";
|
||||
/*
|
||||
rach_sizeOfRA_PreamblesGroupA = ;
|
||||
rach_messageSizeGroupA = ;
|
||||
rach_messagePowerOffsetGroupB = ;
|
||||
*/
|
||||
rach_powerRampingStep = 4;
|
||||
rach_preambleInitialReceivedTargetPower = -108;
|
||||
rach_preambleTransMax = 10;
|
||||
rach_raResponseWindowSize = 10;
|
||||
rach_macContentionResolutionTimer = 48;
|
||||
rach_maxHARQ_Msg3Tx = 4;
|
||||
|
||||
pcch_default_PagingCycle = 128;
|
||||
pcch_nB = "oneT";
|
||||
bcch_modificationPeriodCoeff= 2;
|
||||
ue_TimersAndConstants_t300 = 1000;
|
||||
ue_TimersAndConstants_t301 = 1000;
|
||||
ue_TimersAndConstants_t310 = 1000;
|
||||
ue_TimersAndConstants_t311 = 10000;
|
||||
ue_TimersAndConstants_n310 = 20;
|
||||
ue_TimersAndConstants_n311 = 1;
|
||||
ue_TransmissionMode = 1;
|
||||
|
||||
//Parameters for SIB18
|
||||
rxPool_sc_CP_Len = "normal";
|
||||
rxPool_sc_Period = "sf40";
|
||||
rxPool_data_CP_Len = "normal";
|
||||
rxPool_ResourceConfig_prb_Num = 20;
|
||||
rxPool_ResourceConfig_prb_Start = 5;
|
||||
rxPool_ResourceConfig_prb_End = 44;
|
||||
rxPool_ResourceConfig_offsetIndicator_present = "prSmall";
|
||||
rxPool_ResourceConfig_offsetIndicator_choice = 0;
|
||||
rxPool_ResourceConfig_subframeBitmap_present = "prBs40";
|
||||
rxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "00000000000000000000";
|
||||
rxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5;
|
||||
rxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0;
|
||||
/*
|
||||
rxPool_dataHoppingConfig_hoppingParameter = 0;
|
||||
rxPool_dataHoppingConfig_numSubbands = "ns1";
|
||||
rxPool_dataHoppingConfig_rbOffset = 0;
|
||||
rxPool_commTxResourceUC-ReqAllowed = "TRUE";
|
||||
*/
|
||||
|
||||
// Parameters for SIB19
|
||||
discRxPool_cp_Len = "normal"
|
||||
discRxPool_discPeriod = "rf32"
|
||||
discRxPool_numRetx = 1;
|
||||
discRxPool_numRepetition = 2;
|
||||
discRxPool_ResourceConfig_prb_Num = 5;
|
||||
discRxPool_ResourceConfig_prb_Start = 3;
|
||||
discRxPool_ResourceConfig_prb_End = 21;
|
||||
discRxPool_ResourceConfig_offsetIndicator_present = "prSmall";
|
||||
discRxPool_ResourceConfig_offsetIndicator_choice = 0;
|
||||
discRxPool_ResourceConfig_subframeBitmap_present = "prBs40";
|
||||
discRxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "f0ffffffff";
|
||||
discRxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5;
|
||||
discRxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
srb1_parameters :
|
||||
{
|
||||
# timer_poll_retransmit = (ms) [5, 10, 15, 20,... 250, 300, 350, ... 500]
|
||||
timer_poll_retransmit = 80;
|
||||
|
||||
# timer_reordering = (ms) [0,5, ... 100, 110, 120, ... ,200]
|
||||
timer_reordering = 35;
|
||||
|
||||
# timer_reordering = (ms) [0,5, ... 250, 300, 350, ... ,500]
|
||||
timer_status_prohibit = 0;
|
||||
|
||||
# poll_pdu = [4, 8, 16, 32 , 64, 128, 256, infinity(>10000)]
|
||||
poll_pdu = 4;
|
||||
|
||||
# poll_byte = (kB) [25,50,75,100,125,250,375,500,750,1000,1250,1500,2000,3000,infinity(>10000)]
|
||||
poll_byte = 99999;
|
||||
|
||||
# max_retx_threshold = [1, 2, 3, 4 , 6, 8, 16, 32]
|
||||
max_retx_threshold = 4;
|
||||
}
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// MME parameters:
|
||||
mme_ip_address = ({ ipv4 = "127.0.1.10"; });
|
||||
|
||||
NETWORK_INTERFACES : {
|
||||
ENB_IPV4_ADDRESS_FOR_S1_MME = "127.0.1.30/24";
|
||||
ENB_IPV4_ADDRESS_FOR_S1U = "127.0.1.30/24";
|
||||
ENB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
ENB_IPV4_ADDRESS_FOR_X2C = "127.0.1.30/24";
|
||||
ENB_PORT_FOR_X2C = 36422; # Spec 36422
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
log_config = {
|
||||
global_log_level = "info";
|
||||
pdcp_log_level = "info";
|
||||
rrc_log_level = "info";
|
||||
gtp_log_level = "info";
|
||||
};
|
||||
@@ -1,102 +0,0 @@
|
||||
Active_eNBs = ( "eNB-Eurecom-DU");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
eNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
eNB_CU_ID = 0xe00;
|
||||
|
||||
eNB_name = "eNB-Eurecom-DU";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } )
|
||||
|
||||
nr_cellid = 12345678L
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
component_carriers = (
|
||||
{
|
||||
node_function = "3GPP_eNODEB";
|
||||
node_timing = "synch_to_ext_device";
|
||||
node_synch_ref = 0;
|
||||
frame_type = "FDD";
|
||||
tdd_config = 3;
|
||||
tdd_config_s = 0;
|
||||
prefix_type = "NORMAL";
|
||||
eutra_band = 7;
|
||||
downlink_frequency = 2665000000L;
|
||||
uplink_frequency_offset = -120000000;
|
||||
Nid_cell = 0;
|
||||
N_RB_DL = 50;
|
||||
Nid_cell_mbsfn = 0;
|
||||
nb_antenna_ports = 1;
|
||||
nb_antennas_tx = 1;
|
||||
nb_antennas_rx = 1;
|
||||
tx_gain = 90;
|
||||
rx_gain = 125;
|
||||
|
||||
pucch_deltaF_Format1 = "deltaF2";
|
||||
pucch_deltaF_Format1b = "deltaF3";
|
||||
pucch_deltaF_Format2 = "deltaF0";
|
||||
pucch_deltaF_Format2a = "deltaF0";
|
||||
pucch_deltaF_Format2b = "deltaF0";
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
remote_n_address = "127.0.0.4";
|
||||
local_n_address = "127.0.0.3";
|
||||
local_n_portc = 500;
|
||||
remote_n_portc = 501;
|
||||
local_n_portd = 600;
|
||||
remote_n_portd = 601;
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes";
|
||||
nb_tx = 1;
|
||||
nb_rx = 1;
|
||||
att_tx = 10;
|
||||
att_rx = 10;
|
||||
bands = [7];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 125;
|
||||
eNB_instances = [0];
|
||||
}
|
||||
);
|
||||
|
||||
log_config = {
|
||||
global_log_level = "info";
|
||||
hw_log_level = "info";
|
||||
phy_log_level = "info";
|
||||
mac_log_level = "info";
|
||||
rlc_log_level = "info";
|
||||
};
|
||||
@@ -22,9 +22,7 @@ gNBs =
|
||||
|
||||
local_s_address = "127.0.0.4";
|
||||
remote_s_address = "127.0.0.3";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2152;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2152;
|
||||
|
||||
# ------- SCTP definitions
|
||||
|
||||
@@ -168,9 +168,7 @@ MACRLCs = (
|
||||
tr_n_preference = "f1";
|
||||
local_n_address = "127.0.0.3";
|
||||
remote_n_address = "127.0.0.4";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2152;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2152;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,12 +17,9 @@ gNBs =
|
||||
|
||||
tr_s_preference = "f1";
|
||||
|
||||
local_s_if_name = "lo";
|
||||
local_s_address = "127.0.0.3";
|
||||
remote_s_address = "0.0.0.0"; # multiple DUs
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2152;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2152;
|
||||
|
||||
|
||||
|
||||
@@ -163,12 +163,9 @@ MACRLCs = ({
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "lo";
|
||||
local_n_address = "127.0.0.4";
|
||||
remote_n_address = "127.0.0.3";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2152;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2152;
|
||||
pusch_FailureThres = 1000;
|
||||
});
|
||||
|
||||
@@ -163,12 +163,9 @@ MACRLCs = ({
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "lo";
|
||||
local_n_address = "127.0.0.5";
|
||||
remote_n_address = "127.0.0.3";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2152;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2152;
|
||||
pusch_FailureThres = 1000;
|
||||
});
|
||||
|
||||
@@ -172,12 +172,9 @@ MACRLCs = (
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "enp193s0f0";
|
||||
local_n_address = "172.21.16.51";
|
||||
remote_n_address = "172.21.6.90";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 170;
|
||||
pucch_TargetSNRx10 = 230;
|
||||
|
||||
-3
@@ -172,12 +172,9 @@ MACRLCs = (
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "enp193s0f0";
|
||||
local_n_address = "172.21.16.51";
|
||||
remote_n_address = "172.21.6.90";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 120;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
|
||||
-3
@@ -172,12 +172,9 @@ MACRLCs = (
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "enp193s0f0";
|
||||
local_n_address = "172.21.16.51";
|
||||
remote_n_address = "172.21.6.90";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2153;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2153;
|
||||
pusch_TargetSNRx10 = 120;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
|
||||
@@ -160,12 +160,9 @@ MACRLCs = ({
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "lo";
|
||||
local_n_address = "127.0.0.4";
|
||||
remote_n_address = "127.0.0.3";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2152;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2152;
|
||||
pusch_FailureThres = 1000;
|
||||
});
|
||||
|
||||
@@ -160,12 +160,9 @@ MACRLCs = ({
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "f1";
|
||||
local_n_if_name = "lo";
|
||||
local_n_address = "127.0.0.5";
|
||||
remote_n_address = "127.0.0.3";
|
||||
local_n_portc = 500;
|
||||
local_n_portd = 2152;
|
||||
remote_n_portc = 501;
|
||||
remote_n_portd = 2152;
|
||||
pusch_FailureThres = 1000;
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(nr-cuup)
|
||||
@@ -0,0 +1,4 @@
|
||||
add_executable(nr-cuup-load-test nr-cuup-load-test.c)
|
||||
target_link_libraries(nr-cuup-load-test PRIVATE GTPV1U ${T_LIB} utils ITTI e1ap
|
||||
SCTP_CLIENT)
|
||||
target_link_libraries(nr-cuup-load-test PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) # ITTI
|
||||
@@ -0,0 +1,78 @@
|
||||
This is a simple tester for the CU-UP. It configures the CU-UP via E1, and can
|
||||
stream data via GTP in uplink and downlink directions.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
# Overview
|
||||
|
||||
From a schematic point of view, the tester/CU-UP interaction looks like this:
|
||||
|
||||
```
|
||||
+-------+
|
||||
+------>| CU-UP |<------+
|
||||
| +-------+ |
|
||||
| | |
|
||||
| F1-U | E1 | NG-U
|
||||
| (data) | (control) | (data)
|
||||
| | |
|
||||
| +-------+ |
|
||||
+------>|tester |<------+
|
||||
+-------+
|
||||
```
|
||||
|
||||
The tester is for performance testing of a CU-UP, and behaves like an
|
||||
integrated DU/CU-CP/UPF from the CU-UP's point of view.
|
||||
|
||||
The tester and CU-UP have an E1 connection through which control information is
|
||||
exchanged, e.g., configuration of UEs/bearers and associated information. In
|
||||
other words, towards the CU-UP, the tester appears like a CU-CP.
|
||||
|
||||
Further, for each UE/bearer, CU-UP is configured with GTP IP information and
|
||||
the corresponding TEID. The CU-UP will forward downlink data arriving on NG-U
|
||||
on the F1-U interface, and uplink data arriving on F1-U on the NG-U interface.
|
||||
In other words, on the F1-U interface, the tester appears like a DU to the
|
||||
CU-UP, and on the NG-U interface, the tester appears like a UPF to the CU-UP.
|
||||
|
||||
A test scenario is fixed to these steps:
|
||||
|
||||
1. The tester sets up a number of UEs via the E1 interface (it is possible to
|
||||
see the message exchange in Wireshark).
|
||||
2. The tester streams data in downlink/uplink (from the CU-UP's point of view)
|
||||
and measures how much data is lost.
|
||||
3. The tester releases all the UEs via the E1 interface, and disconnects from
|
||||
the CU-UP.
|
||||
|
||||
Note: The tester uses the same GTP module as the CU-UP. Thus, data might not
|
||||
only be lost at the GTP interface of the CU-UP, but also at the tester.
|
||||
|
||||
# Usage
|
||||
|
||||
You can build the CU-UP and the tester like so:
|
||||
|
||||
cd ~/openairinterface5g
|
||||
mkdir build && cd build && cmake .. -GNinja && ninja nr-cuup nr-cuup-load-test params_libconfig
|
||||
./tests/nr-cuup/nr-cuup-load-test
|
||||
./nr-cuup -O ../tests/nr-cuup/load-test.conf
|
||||
|
||||
This builds both tester and CU-UP in the directory `build/`, then starts the
|
||||
load tester with default values
|
||||
|
||||
- 1 UE
|
||||
- 10s of Test
|
||||
- 60 Mbps of traffic in both downlik/uplink with a packet size of 1400 bytes
|
||||
|
||||
To see the available options, run
|
||||
|
||||
./tests/nr-cuup/nr-cuup-load-test -h
|
||||
|
||||
The configuration file [`load-test.conf`](./load-test.conf) matches the default
|
||||
tester configuration (F1-U GTP traffic over non-standard port 2153, NG-U GTP
|
||||
over 2152, tests on localhost). The configuration file includes this
|
||||
non-standard port 2153 as the GTP module, as of now, cannot bind on the same
|
||||
interface and port for both F1-U and NG-U due to internal limitations.
|
||||
|
||||
# Limitations
|
||||
|
||||
- The tester does not yet create/remove UEs during a traffic test.
|
||||
- It might be possible to integrate with an external GTP traffic generator,
|
||||
but we did not test this.
|
||||
@@ -0,0 +1,58 @@
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
gNB_CU_UP_ID = 0xe00;
|
||||
|
||||
# cell_type = "CELL_MACRO_GNB";
|
||||
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
|
||||
|
||||
|
||||
tr_s_preference = "f1";
|
||||
|
||||
local_s_address = "127.0.0.6";
|
||||
remote_s_address = "127.0.0.1";
|
||||
local_s_portc = 501;
|
||||
local_s_portd = 2153;
|
||||
remote_s_portc = 500;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
E1_INTERFACE =
|
||||
(
|
||||
{
|
||||
type = "up";
|
||||
ipv4_cucp = "127.0.0.1";
|
||||
ipv4_cuup = "127.0.0.6";
|
||||
}
|
||||
)
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.109";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "127.0.0.7";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
log_config: {
|
||||
global_log_level = "info";
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user