Compare commits

...

127 Commits

Author SHA1 Message Date
Robert Schmidt
578979121d Revert "needed or not?"
This reverts commit 1d39400c6c5de0e118ab68f425788b8ad052da48.
2026-05-29 17:49:47 +02:00
Robert Schmidt
0d1f7470ae needed or not? 2026-05-29 17:49:47 +02:00
Sagar Arora
f9f925cdb2 doc: nrue ldpc gpu offload
Signed-off-by: Sagar Arora <sagar.arora@openairinterface.org>
2026-05-29 17:49:47 +02:00
Jaroslava Fiedlerova
b5719c49a4 CI: Add confs for test with nrUE using CUDA 2026-05-29 17:49:47 +02:00
Jaroslava Fiedlerova
8f935bd32d CI: Enable build of nrUE on Jetson with CUDA support
Add new Dockerfiles to enable building nrUE and gNB with CUDA support on Jetson.
Compared to the standard build, these images use Ubuntu 22.04 instead of Ubuntu
24.04. This change is required because CUDA 12.2.0 runtime and development
images are only available for Ubuntu 22.04.

To build the nrUE and gNB image with CUDA support, run the following commands:

    docker build  --target ran-base-cuda --tag ran-base-cuda:latest --file docker/Dockerfile.base.cuda.ubuntu .
    docker build  --target ran-build-cuda --tag ran-build-cuda:latest --file docker/Dockerfile.build.cuda.ubuntu .
    docker build  --target oai-gnb --tag oai-gnb:test --file docker/Dockerfile.gNB.cuda.ubuntu .
    docker build  --target oai-nr-ue --tag oai-nr-ue:test --file docker/Dockerfile.nrUE.cuda.ubuntu .

A new XML file was added to build images on Jetson3 and push images with CUDA
support, using the prefix "armv8_cuda".
2026-05-29 17:49:47 +02:00
Raymond Knopp
e83df8ad0c Fixes E2E 2026-05-29 17:49:47 +02:00
Robert Schmidt
93ef3a0d60 Ask Raymond 2026-05-29 17:49:47 +02:00
Raymond Knopp
a51770341e use_gpu/ENABLE_CUDA/USE_CUDA + sims
Remove double nr_dlsim eval

Remove CUDA from normal LDPC coding, make standalone

nr_dlsim seems to work.
ldpctest: need to implement memcpy() in _cuda.c
nr_ulsim fails

fixup ldpctest: make it run

Initial version of ldpc_decoder_cuda done. Change configuration to GH200

modify -h option in dlsim and ulsim. First scratch for nrLDPC GPU decoder tutorial

Change code

add missing initialization on jetson for nr_ulsim. Bug fixed

add buffer memory management; remove an extra synchronization in cuda decoder

fix for ulsim via cuda encoder on jetson

fix for 256QAM

Move LDPC CUDA to separate subdirectory

Only functional change: remove USE_CUDA

Rename constant for clarity

ldpc_cuda: add tests to verify it works

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>

Implement fallback CPU

Remove use_gpu

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-29 17:49:42 +02:00
Robert Schmidt
9a6b332912 Set up CUDA infrastructure 2026-05-29 17:42:50 +02:00
Raymond Knopp
c574fa68c5 SIMD channel compensation 2026-05-29 16:06:16 +02:00
Raymond Knopp
ad85919ba7 ARM rate matching 2026-05-29 16:06:16 +02:00
Raymond Knopp
5cd9d6206e TODO: nr ulsch decoding: is this necessary?
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:11:56 +02:00
Raymond Knopp
1c4c8d3e1b Rate matching through SIMD on ARM
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:11:54 +02:00
Raymond Knopp
347dda4dc5 TODO: MM512 necessary? ARM-specific complex number operations
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:10:59 +02:00
Raymond Knopp
a8cb7d807c Update base LDPC encoder for CUDA code generation
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:10:58 +02:00
Raymond Knopp
18f2ff3608 limit log2_maxh \in [1, 14]
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:10:47 +02:00
Jaroslava Fiedlerova
1dfb4fc4c3 CI: Ignore errors present in build logs
Report errors found in the build logs, but do not fail the job if some are
present. Based on observations, certain errors (for example):

    update-alternatives: error: alternative path /usr/share/man/man7/bash-builtins.7.gz doesn't exist

do not actually cause the build to fail. Instead of relying on log parsing, we should maybe
check the return code of the build process to determine whether the build truly failed?

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-26 18:09:27 +02:00
Robert Schmidt
cb0e501293 Merge branch 'integration_2026_w21' into 'develop'
Integration `2026.w21`

* !4130 Add RHEL 9.8 to list of supported distributions
* !4135 fix(clang): disable default-const-init-field-unsafe
* !4118 NR UE: remove hardcoded 64KB limit for UE capability file
* !4120 bugfix: fix A3 event configuration
* !4056 restructuring of UE DLSCH code to handle 2 codewords
* !4132 Cleanup conflict leftover in doc/SW_archi.md
* !4113 move vrtsim channel and per-UE param configuration from CLI flags to config file
* !4127 Bugfix overflow uint16_t variable when FFT size is 6144
* !3902 Refactor DLSCH/ULSCH scheduler: extract proportional fair policy behind function pointer interface
* !4136 hotfix for correct GFNI detection
* !4112 NAS UE Service Request fixes and improvements for Paging
* !4128 Iterate all NZP-CSI-RS resources per ResourceSet in scheduler
* !4131 Fix 8 DL ports
* !4137 changes in cudaMemadvise/cudaPrefetch API for v13
* !4122 Add Fedora 44/Ubuntu 26 to list of supported distributions
* !4117 NR_MAC/PHY: align SRS time_start_position semantics between L2 and L1
* !4129 CI: Clone from internal git repo
* !4124 Preparatory changes for LDPC CUDA integration

See merge request oai/openairinterface5g!4123
2026-05-23 09:29:41 +00:00
Merkebu Girmay
726293c3d4 vrtsim: Fix incorrect assertion
an incorrect assertion that was enforcing symmetric antenna
configurations only. I removed the assertion and updated the logging for
UE antenna dimensions and the channel model antenna dimensions used to
search in CIRDB.

Signed-off-by: Merkebu Girmay <merkebu.girmay@openairinterface.org>
2026-05-23 11:14:09 +02:00
Robert Schmidt
c9958bef59 Merge remote-tracking branch 'origin/ldpc_cuda_prep' into integration_2026_w21 (!4124)
Preparatory changes for LDPC CUDA integration

This changeset is preparatory work to merge LDPC CUDA offload in !4097.
Notably, it includes changes for cleaning up some defines, updates to
the LDPC interface, and CI specific changes to prepare for the other MR.
The goal is to merge changes in a first step such that, in a second
step, only the actual LDPC CUDA implementation and ancillary changes
(CUDA memory allocations, CI files, ...) need to be merged.

Reviewed-By: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-23 10:17:17 +02:00
Robert Schmidt
470a5316e8 Remove superfluous memset() in UE DLSCH decoding
This memset() seems to be superfluous: following the code flow,
d_to_be_cleared is set on the same condition which guards the memset.
Following nrLDPC_coding_decoder() -> nrLDPC_prepare_TB_decoding() ->
nr_process_decode_segment() -> nr_rate_matching_ldpc_rx(), another
memset() is done using d_to_be_cleared.

Reported-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 19:06:26 +02:00
Jaroslava Fiedlerova
6afbb081a9 Merge remote-tracking branch 'origin/ci-clone-repo-2' into integration_2026_w21 (!4129)
CI: Clone from internal git repo

This MR refactors the CI pipeline as a preparatory step for the GitLab
--> GitHub migration.  The core idea is to use a dedicated stage
RAN-Local-Repo-Push (introduced in !4017 (merged)) to clone source and
target branches, merges them and push a resulting branch into internal
git mirror. All downstream test jobs then check out exclusively from
that mirror.  This makes the CI subpipelines independent of the remote
repository and improves stability in case of disruptions - test jobs
always check out from the internal mirror, not from GitLab/GitHub
directly.

Changes:
- doGitLabMerge.sh removed - merge is now done once in
  RAN-Local-Repo-Push, slave jobs check out the pre-merged branch from
  the internal mirror via SCM RAN-Local-Repo-Push extended:
- mergeWithTarget boolean parameter makes the merge optional (skipped on
  direct push events) source/target remotes replace reliance on origin,
- targetRepo is now a configurable parameter to support fork-based MRs
- CreateTag() removed from Python - testBranch and testRepository
  (default: internal mirror) are computed in the Jenkinsfile and passed
  directly, two optional override parameters (customBranch,
  customRepository) allow targeting a non-default branch or repo
- create_workspace.sh is reduced to git clone --depth=1 --branch
- all eNB_/ran-prefixed Jenkinsfile parameters and Python CLI arguments
  renamed to neutral names (sourceBranch, sourceCommit, workspace,
  repository,...), both old and new names accepted during the transition
  period

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 17:09:35 +02:00
Robert Schmidt
40028ef0ce Remove unused variables d_to_be_cleared
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 15:26:01 +02:00
Robert Schmidt
b506115bf9 cmake: Move physim macros to global scope
A later commit (for LDPC CUDA) will reuse add_physim_test(), but is in a
completely different location than the existing physim test definitions.
Hence, move it to global scope for later reuse.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 15:26:01 +02:00
Raymond Knopp
a47d958636 Add a log to signal LDPC segment decode NOK
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 15:26:01 +02:00
Raymond Knopp
aaf2e2aed9 LDPC interface: init: provide number of PxSCH
Provide the memory buffer size for storing LLRs across HARQ rounds. then
we don't need to check for this in the API users.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 15:26:01 +02:00
Raymond Knopp
28cff29178 Simplify TB_parameters, c, d, d_to_be_cleared
The upcoming LDPC CUDA offload library needs a contiguous buffer instead
of individual segments. Correspondingly, change the interface to
accommodate this need, in the CPU slot decoding library and AAL.

Co-authored-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Co-authored-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Co-authored-by: Romain Beurdouche <romain.beurdouche@eurecom.fr>
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 15:26:01 +02:00
Raymond Knopp
aab585900b LLR type and optimization
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:35:08 +02:00
Robert Schmidt
528da705df Increase measurement coverage for better reporting
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:35:08 +02:00
Raymond Knopp
a53545da38 Align to 64 bytes
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:35:07 +02:00
Robert Schmidt
dc3011c298 Remove #ifndef PHYSIM from openairinterface5g_limits.h
git grep -w PHYSIM shows no definition of PHYSIM anywhere.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:35:07 +02:00
Robert Schmidt
d7531cfe15 Remove usage of non-existing EMOS define
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:35:07 +02:00
Robert Schmidt
77db1ac9c0 Rename CUDA channel sim cmake option to ENABLE_CHANNEL_SIM_CUDA
Rename this option from CUDA_ENABLE, as it might otherwise conflict with
another option for LDPC CUDA. For consistency, name the compile option
to CHANNEL_SIM_CUDA. This way, the LDPC CUDA option (ENABLE_LDPC_CUDA
and compile definition LDPC_CUDA) will not conflict, and the
corresponding code is clearly "labelled".

A further change is the use of target_sources to simplify the definition
of the channel_pipeline executable.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-22 09:34:36 +02:00
Jaroslava Fiedlerova
137fa62d6c CI: Shorten HTML header
Information about sourceBranch and sourceCommit is already encoded in
the testBranch name, making it redundant in the HTML header.
Also remove the Job Trigger and Target Branch lines, as these parameters
provide limited value and are not particularly relevant for the report.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 23:53:02 +02:00
Jaroslava Fiedlerova
78108290ab CI: drop server index from workspace description in XML test files
"Create new Workspace for server 0/1" no longer makes sense now that
workspace creation is not tied to a specific server index.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 23:47:55 +02:00
Jaroslava Fiedlerova
5a4c1c5fd9 CI: Rename and cleanup python main args
eNB_SourceCodePath --> workspace
ranRepository      --> repository
ranBranch          --> branch
ranCommitID        --> commitID
ranTargetBranch    --> targetBranch

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 23:47:55 +02:00
Robert Schmidt
ac6c40b1f2 Merge remote-tracking branch 'origin/aerial-srs-time-position-fix' into integration_2026_w21 (!4117)
NR_MAC/PHY: align SRS time_start_position semantics between L2 and L1

Problem

nfapi_nr_srs_pdu_t::time_start_position was being interpreted
inconsistently between OAI MAC (the producer) and the various L1
consumers:

- MAC populated the field with SRS-ResourceMapping.startPosition from
  the RRC config — the value 3GPP defines as an offset counted backwards
  from the end of the slot (TS 38.331).
- L1 consumers (openair1/PHY/NR_TRANSPORT/srs_rx.c,
  openair1/SCHED_NR/phy_procedures_nr_gNB.c) and the MAC's own VRB-map
  consumer (gNB_scheduler_srs.c) all re-derived the absolute symbol
  index with NR_SYMBOLS_PER_SLOT - 1 - time_start_position.

This works for OAI's in-tree soft L1 (every consumer applies the same
inversion), but external FAPI L1s (NVIDIA Aerial / cuBB) treat
time_start_position as the absolute symbol index per the SCF FAPI 222
PDU semantics. The MAC and external L1 therefore disagreed by one symbol
inversion. Net effect: SRS was scheduled on the wrong OFDM symbol, the
RU sampled noise instead of the SRS, and the channel-matrix returned in
SRS.indication was all zeros for almost every occasion.  Fix

Move the inversion to the producer side. MAC populates the PDU with the
absolute symbol index (NR_SYMBOLS_PER_SLOT - 1 - startPosition), and
every consumer uses srs_pdu->time_start_position directly as l0.

The two semantics are mathematically equivalent for OAI's soft L1 (l0 =
NR_SYMBOLS_PER_SLOT - 1 - startPosition either way), so the symbol index
used internally is unchanged on non-Aerial builds. External L1 consumers
now receive the value they expect.  Testing status

- [x] OAI L1 + L2
- [x] Aerial L1 in CAT-B / mMIMO mode
- [x] Aerial L1 in CAT-A mode
- [x] RFsim / nrUE end-to-end loopback:

Reviewed-By: Rúben Soares Silva <rsilva@allbesmart.pt>
2026-05-21 19:01:59 +02:00
Robert Schmidt
c2e43c025d Merge remote-tracking branch 'origin/u26-f44' into integration_2026_w21 (!4122)
Add Fedora 44/Ubuntu 26 to list of supported distributions

See commits for more details. Includes a minor cleanup. What I tested
for U22/U24/U26 and F44 is

    ./build_oai --ninja -c -I -w USRP --gNB --nrUE

Reviewed-By: Luis Pereira <lpereira@allbesmart.pt>
2026-05-21 17:33:24 +02:00
Robert Schmidt
e518d49517 Merge remote-tracking branch 'origin/cuda13_channel_pipeline_hotfix' into integration_2026_w21 (!4137)
changes in cudaMemadvise/cudaPrefetch API for v13.

Build fails on DGX spark or any Blackwell GPU target without this fix.
It just checks the CUDA version in a couple of places where GPU offload
is used for channel simulation (nr_dlsim,nr_ulsim and
channel_pipeline.cu) and adapts the usage of the CUDA API accordingly.

Reviewed-By: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-21 17:32:47 +02:00
Robert Schmidt
68d9258b54 Merge remote-tracking branch 'origin/make_8x8_work_again' into integration_2026_w21 (!4131)
Fix 8 DL ports

In current develop 8 CSI ports scenario doesn't work because of #1067.
This MR works around that issue by decreasing number of PRBs for PUCCH
F2.

Tested-by: Teodora Vladić <teodora.vladic@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 17:32:05 +02:00
Jaroslava Fiedlerova
22365c3d28 CI: Rename and cleanup Jenkinsfile parameters, remove eNB_ prefix
eNB_Repository   --> targetRepo
SourceRepo       --> sourceRepo
eNB_Branch       --> sourceBranch
eNB_CommitID     --> sourceCommit
eNB_MR           --> requestNumber
eNB_mergeRequest --> mergeWithTarget
eNB_TargetBranch --> targetBranch

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 16:48:41 +02:00
Jaroslava Fiedlerova
f4c8158825 CI: Allow to skip merge with target in RAN-Local-Repo-Push
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 16:46:38 +02:00
Jaroslava Fiedlerova
63c550802a CI: Make target repo configurable in RAN-Local-Repo-Push
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 16:46:38 +02:00
Jaroslava Fiedlerova
04c5113bc1 CI: Minor cleanup of Jenkinsfile
Remove duplicated InitiateHtml step

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 16:46:38 +02:00
Jaroslava Fiedlerova
84299bcd27 CI: replace CreateTag and merge-based workspace logic with explicit branch/repo
Remove CreateTag() and the merge/targetBranch parameters from
CreateWorkspace, passing the final branch name and repository directly
from Jenkins instead. Jenkinsfile computes testBranch and testRepository
(with INTERNAL_REPO as default). create_workspace.sh now does a simple
shallow clone by branch name.

Introduce two optional Jenkins parameters that allow overriding the
default branch reference and git repository URL used across the CI
pipeline.
- customBranch: when set, replaces the entire computed
  {sourceBranch}-{commitID}
- customRepository: when set, replaces INTERNAL_REPO as the git remote
  used by Create_Workspace to clone the source code onto the test node.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-21 16:46:12 +02:00
Robert Schmidt
076c722eb9 build_oai UHD source installation: default to UHD v4.8
Use the current OAI CI default UHD version. Update the documentation to
show how to install a recent version.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 15:21:21 +02:00
Robert Schmidt
19e3cbd66a Extend UHD patch to v4.9/v4.10, remove sed
The existing patch (generated through sed) works also in UHD versions
beyond 4.8, so allow to use that.

Further, since sed is confusing, store the patch directly. Update the
dockerfiles so that docker copies the right file for UHD 4.8.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 15:21:20 +02:00
Robert Schmidt
3192251471 Add Ubuntu 26 to list of supported distributions
Also, for UHD install from packages, add the right version for both
Ubuntu 24 and Ubuntu 26: in U24, use the currently recommended 4.8, and
bump to latest in U26.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 15:21:20 +02:00
Robert Schmidt
2e54a90ad0 build_helper: Remove retry mechanism
The CI does not use installation of UHD from package, hence this
mechanism is not needed (and should not be, anyway).

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 15:21:20 +02:00
Robert Schmidt
c803643fad Add Fedora 44 to list of supported distributions
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 15:21:20 +02:00
Robert Schmidt
10f94ac56e Merge remote-tracking branch 'origin/fix-csirs-multi-resource' into integration_2026_w21 (!4128)
Iterate all NZP-CSI-RS resources per ResourceSet in scheduler

It was necessary to use 2 CSI-RS resources, but it was discovered that
gNB only transmitted the first one.

nr_csirs_scheduling() was comparing nzp_CSI_RS_ResourceId against an
NZP-CSI-RS-ResourceSetId picked from the first entry of the first
matching CSI-ResourceConfig. This silently filtered out every Resource
whose ID did not happen to match the ResourceSet ID, making setups with
more than one NZP-CSI-RS-Resource per UE impossible: only the first
resource was ever transmitted, while the UE kept scheduling reception
for the remaining ones and reported them as missing (RSRP at the noise
floor).

This MR fixes it, and allows multiple Resources per Set.

Reviewed-By: Francesco Mani <email@francescomani.it>
2026-05-21 10:39:53 +02:00
Robert Schmidt
7da624fc80 Merge remote-tracking branch 'origin/gfni_fix' into integration_2026_w21 (!4136)
hotfix for correct GFNI detection

Currently, GFNI will be activated for all x86 builds which fails on
older machines (prior to skylake or Zen4). This patch just detects GFNI
from the CPUFLAGS and adds only if it is supported by the x86 target.

Includes two ancillary fixes for correctly using CPUFLAGS and DFTS
compiler optimization flag.

Reviewed-By: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-21 10:08:23 +02:00
Robert Schmidt
b5179e3c50 Merge remote-tracking branch 'origin/nas-paging' into integration_2026_w21 (!4112)
NAS UE Service Request fixes and improvements for Paging

This MR improves the NAS UE Service Request path and makes
paging-related resume handling more robust. It fixes KSI/KgNB
synchronization, corrects 5G-S-TMSI and encoder/decoder length handling,
and adds Service Accept handling in the NAS_CONN_ESTABLI_CNF path.

It also zero-initializes the initial NAS buffer used during RRC setup
complete to avoid carrying stale data into early NAS message handling.

Reviewed-By: Cedric Roux <cedric.roux@eurecom.fr>
2026-05-21 10:07:57 +02:00
Robert Schmidt
8698186d47 Merge remote-tracking branch 'origin/dl-scheduler-refactor' into integration_2026_w21 (!3902)
Refactor DLSCH/ULSCH scheduler: extract proportional fair policy behind function pointer interface

Motivation

The current `pf_dl()` is a ~600-line monolithic function that mixes
infrastructure concerns (UE iteration, HARQ management, CCE allocation,
MAC PDU generation) with the scheduling policy (PF priority, RB
allocation, MCS selection). This makes it hard to modify the scheduling
strategy, test alternatives, or offload scheduling to a GPU (cuMAC).

This MR refactors the DL and UL schedulers into a clean separation
between **infrastructure** and **policy**, using function pointers for
beam allocation and scheduling decisions. The data structures
(`nr_dl_candidate_t`, `nr_dl_sched_params_t`) are also designed to map
directly to cuMAC's
`cumacCellGrpUeStatus`/`cumacSchdSol`/`CumacCellGrpPrms`, to make future
integration smooth.

Changes

Goodput tracking fix

`dl_thr_ue` now tracks actual goodput in bps (EWMA of SDU byte deltas
per frame) instead of accumulating raw byte counts per slot, which are
not as straightforward to interpret (due to variations in TDD patterns
mostly). The new calculation matches closely with the throughput
measured with e.g. iperf.

Helper extraction from `pf_dl()` and `post_process_dlsch()`

- `find_first_available_rbs()` — first-fit contiguous RB allocation
- `setup_dl_harq_process()` — HARQ process management
- `generate_dl_mac_pdu()` — MAC CE + RLC data + padding
- `fill_dl_tx_request()` — FAPI TX_req filling

New scheduling interface

- **`nr_dl_candidate_t`** — per-UE flat struct with all immutable inputs
  (buffer status, BLER, MCS limits, beam, BWP) and outputs (scheduled,
  rbStart, rbSize, MCS). Kept relatively minimal for now, but it should
  be easy to add more input metrics in the future.
- **`nr_dl_sched_params_t`** — per-beam cell-level context (VRB map,
  available RBs, slot bitmap)

Function pointers (DL)

| Pointer | Default implementation | Role |
|---------|----------------------|------|
| `dl_ri_pmi_select` | `nr_dl_ri_pmi_select_default` | Rank/PMI selection |
| `dl_beam_select` | `nr_dl_beam_select_default` | Beam direction assignment |
| `dl_tda_select` | `nr_dl_tda_select_default` | Time-domain allocation |
| `dl_mcs_select` | `nr_dl_mcs_select_default` | MCS from BLER/SINR |
| `dl_rb_alloc` | `nr_dl_proportional_fair` | PRB allocation (PF policy) |

Function pointers (UL)

| Pointer | Default implementation | Role |
|---------|----------------------|------|
| `ul_ri_tpmi_select` | `nr_ul_ri_tpmi_select_default` | Rank/TPMI from SRS feedback |
| `ul_beam_select` | `nr_ul_beam_select_default` | Beam direction assignment |
| `ul_tda_select` | `nr_ul_tda_select_default` | Time-domain allocation |
| `ul_mcs_select` | `nr_ul_mcs_select_default` | MCS from BLER/SINR |
| `ul_rb_alloc` | `nr_ul_proportional_fair` | PRB allocation (retx first, then PF-sorted new-tx) |

All default implementations are in `gNB_scheduler_dlsch.c` (DL) and
`gNB_scheduler_ulsch.c` (UL).

MCS selection flow

The old `get_mcs_from_bler()` entangled two concerns: updating the BLER
estimate from HARQ feedback and deciding the MCS. These are now split:

- **BLER tracking is infrastructure's job**: `collect_dl_candidates()`
  calls `update_dl_bler_stats()` which updates the BLER estimate from
  HARQ round statistics.
- **MCS selection is the policy's job**: the proportional fair policy
  calls `select_mcs_from_bler()` internally to adapt MCS based on the
  BLER value. A different policy could use an entirely different MCS
  strategy (e.g. cuMAC has its own `mcsSelectionLUT` + OLLA, one could
  decide to opportunistically lower the MCS while increasing the PRB
  allocation for reliability in some cases, etc).

For retransmissions, MCS/number of PRBs are passed as hints so the
policy can use them as-is if desired, but we don't enforce it (adaptive
HARQ possible too: the standard requires us to maintain TBS but in
theory it could be achieved via changing the MCS and number of RBs if we
wanted to).

Refactored flow

`nr_dl_schedule()` (formerly `pf_dl()`):

```
collect_dl_candidates()     → build candidate array from UE list
schedule_dl_ues()           → beam alloc + per-beam policy calls
  for each scheduled candidate → CCE/PUCCH/TBS validation + post_process
```

Beam allocation and scheduling policy are two separate function
pointers, allowing each to be developed and tested independently (with
the goal in the future to add a parameter in the config file for each,
and telnet commands to hotswap).

`schedule_dl_ues()` wraps both into a single function: it first calls
beam allocation to assign candidates to beams, then iterates over beams
and calls the scheduling policy for each one. cuMAC performs joint beam
+ PRB allocation on the GPU, so when integrating later it will replace
`schedule_dl_ues()`.

Future work

- Channel matrix H from SRS on candidates for beam-aware scheduling
- Per-RB channel magnitude derived from SRS on candidates
- Config file parameters and telnet commands for hotswapping policies
- cuMAC integration via `schedule_dl_ues()` replacement

Reviewed-By: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-20 19:10:44 +02:00
Raymond Knopp
7d099f7918 Use no-semantic-interposition for DFTS
This helps the performance: In the FFTs there is a chain like
dft1024->dft256->dft64->dft16 with nested function calls. This improves
the compilers behaviour when handling the nesting.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-20 15:56:19 +02:00
Raymond Knopp
92b3d5fa87 Correct CPUFLAGS variable
Use the correct variable, CPUINFO does not exist.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-20 15:52:50 +02:00
Raymond Knopp
7e444cb544 Fix GFNI detection
Currently, GFNI will be activated for all x86 builds which fails on
older machines (prior to skylake or Zen4). Properly detect based on CPU
flags.

Signed-off-by: Raymond Knopp <raymond.knopp@eurecom.fr>
2026-05-20 15:52:35 +02:00
Guido Casati
7c62ed582f NAS UE: handle Service Accept in NAS_CONN_ESTABLI_CNF path
Process Service Accept when it is delivered through NAS connection
establish confirm.

Changes:
- add `FGS_SERVICE_ACCEPT` branch in `NAS_CONN_ESTABLI_CNF` message
  dispatch and call `handle_service_accept

Refs:
- TS 24.501 5.6.1.4, 8.2.17 (Service accept)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-20 11:05:24 +02:00
Guido Casati
607f9c0f33 fix (NAS UE): add Service Request NAS container with PDU status and align security and KgNB sync
Extend the UE 5GS Service Request path to carry `PDU session status`
inside a NAS message container and cipher only that container value when
valid NAS contexts exist. This aligns the initial Service
Request handling with the TS 24.501 rules for non-cleartext IEs.

Treat the generated Service Request as integrity-protected whenever an
integrity context is available, and keep the post-request KgNB refresh
aligned with the NAS UL count used for that protected message.

Changes:
- update `generateServiceRequest()` in `nr_nas_msg.c` to derive a
  `PDU session status` bitmap from configured UE PDU sessions and treat
  it as the non-cleartext trigger for the initial Service Request
- build an inner plain Service Request carrying `PDU session status`,
  place it in the `NAS message container`, cipher only the container
  value with the NAS ciphering context, and keep the outer Service
  Request integrity protected
- extend `fgs_service_request` lib to support optional `PDU session status`
  and `NAS message container` fields
- decode known optional Service Request TLV IEs with a `switch` and skip
  unsupported ones
- add `free_fgs_service_request()` and `eq_fgs_service_request()`
- extend `nas_lib_test` to cover Service Request encoding and decoding
  with `PDU session status`, `NAS message container`, and skipped
  optional IEs, and initialize the test logging/config stubs
- set the outgoing ngKSI with `set_fgs_ksi(nas)` instead of hardcoding
  `NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE`
- move `initialNasMsg->nas_data` allocation into the protected and plain
  branches so each path allocates after its final size accounting
- increment `nas->security.nas_count_ul` after integrity MAC computation
- derive a refreshed KgNB with `derive_kgnb()` and send it through
  `nas_itti_kgnb_refresh_req()` after the protected Service Request is
  built

Refs:
- TS 24.501 §4.4.6 (protection of initial NAS signalling messages)
- TS 24.501 §4.4.4.1 and §4.4.6 allow an initial Service Request to be
  integrity protected and unciphered when a valid 5G NAS security
  context exists and no NAS message container is included.
- TS 24.501 §8.2.16.1 (Service Request message content)
- TS 24.501 §8.2.16.3 (PDU session status)
- TS 24.501 §9.11.3.33 (NAS message container)
- TS 33.501 §6.4.3.1 (NAS integrity inputs)
- TS 33.501 §6.4.4.1 (NAS confidentiality inputs)
- TS 33.501 §6.8.1.2.2 derives KgNB from the UL NAS COUNT of the NAS
  message that moves the UE from CM-IDLE to CM-CONNECTED, unless a
  subsequent NAS Security Mode Complete exists, in which case that newer
  UL NAS COUNT becomes the freshness input.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-20 11:05:24 +02:00
Roberto Louro Magueta
b54afbbc36 Iterate all NZP-CSI-RS resources per ResourceSet in scheduler
Signed-off-by: Roberto Louro Magueta <rmagueta@allbesmart.pt>
2026-05-20 09:25:14 +01:00
Jaroslava Fiedlerova
db3b89b5da Merge remote-tracking branch 'origin/bugfix-fft_shift' into integration_2026_w21 (!4127)
Bugfix overflow uint16_t variable when FFT size is 6144

When using USRP X310 and 100 MHz, the sampling rate is 184320000 Msps,
this leads in a FFT size of 6144 that was causing an overflow in the
fft_shift function (6144 * 14 = 86016 > UINT16_MAX)

This bug was introduced recently, by !3834

Reviewed-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-05-20 10:13:07 +02:00
Raymond Knopp
bc2029b62a changes in cudaMemadvise/cudaPrefetch API for v13. Build fails on DGX spark or any Blackwell GPU target without this fix.
Signed-off-by: Raymond Knopp <raymond.knopp@eurecom.fr>
2026-05-20 10:02:18 +02:00
Luis Pereira
973db30ea2 Bugfix overflow uint16_t variable when FFT size is 6144
When using USRP X310 and 100 MHz, the sampling rate is 184320, this leads in a FFT size of 6144 that was causing an overflow in the fft_shift function (6144 * 14 = 86016 > MAX_INT_16)

Signed-off-by: Luis Pereira <lpereira@allbesmart.pt>
2026-05-20 08:47:03 +01:00
Robert Schmidt
abb7cbe2bd Merge branch 'clang-20-no-const-init-field-unsafe' into 'develop'
fix(clang): disable default-const-init-field-unsafe

See merge request oai/openairinterface5g!4135
2026-05-20 07:34:24 +00:00
Robert Schmidt
9feed9f4d1 fix(clang): disable default-const-init-field-unsafe
clang was silently upgraded in CI to v21, and now warns

    openair2/RRC/NR/rrc_gNB_UE_context.c:70:24: error: default initialization of an object of type 'rrc_gNB_ue_context_t' (aka 'struct rrc_gNB_ue_context_s') with const member leaves the object uninitialized [-Werror,-Wdefault-const-init-field-unsafe]
    openair2/RRC/NR/rrc_gNB_UE_context.c:70:24: error: default initialization of an object of type 'rrc_gNB_ue_context_t' (aka 'struct rrc_gNB_ue_context_s') with const member leaves the object uninitialized [-Werror,-Wdefault-const-init-field-unsafe]
    openair2/RRC/NR/rrc_gNB_cuup.c:183:13: error: default initialization of an object of type 'seq_arr_t' (aka 'struct seq_arr_s') with const member leaves the object uninitialized [-Werror,-Wdefault-const-init-field-unsafe]
    openair2/RRC/NR/rrc_gNB_cuup.c:185:13: error: default initialization of an object of type 'seq_arr_t' (aka 'struct seq_arr_s') with const member leaves the object uninitialized [-Werror,-Wdefault-const-init-field-unsafe]
    openair2/RRC/NR/rrc_gNB_du.c:864:13: error: default initialization of an object of type 'seq_arr_t' (aka 'struct seq_arr_s') with const member leaves the object uninitialized [-Werror,-Wdefault-const-init-field-unsafe]

Since this warning is new, and due to -Werror being used, the build now
fails on the same code base. Disable this warning to get it build.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-20 09:33:38 +02:00
Jaroslava Fiedlerova
92805a4624 Merge remote-tracking branch 'origin/vrtsim-config-file-params' into integration_2026_w21 (!4113)
move vrtsim channel and per-UE param configuration from CLI flags to config file

Add vrtsim channel and per-UE parameter configuration to the gNB config
file. Previously, running a multi-UE vrtsim test required a long list of
CLI flags; these can now be configured directly in the config file under
a vrtsim: section. The gNB can be started with simply:

  sudo ./nr-softmodem -O gnb.sa.band78.106prb.vrtsim.2x2.yaml --device.name vrtsim

Reviewed-By: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-20 08:46:02 +02:00
Robert Schmidt
8a5960461e Merge branch 'rhel-9.8' into 'develop'
!4130: Add RHEL 9.8 to list of supported distributions

See merge request oai/openairinterface5g!4130
2026-05-20 05:55:47 +00:00
Jaroslava Fiedlerova
dc28488e0a Merge remote-tracking branch 'origin/cleanup-sw-archi' into integration_2026_w21 (!4132)
Cleanup conflict leftover in doc/SW_archi.md

Coming from 5c480ffd.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-19 22:19:22 +02:00
Guido Casati
2d05db4819 Cleanup conflict leftover in doc/SW_archi.md
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-19 18:07:20 +02:00
Francesco Mani
fc25f6b075 prevent asserion on PUCCH polar for 8 port CSI by decreasing the number of PRBs in PUCCH F2
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-05-19 17:13:13 +02:00
Robert Schmidt
458fd3623a Add RHEL 9.8 to list of supported distributions
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-19 16:26:29 +02:00
Jaroslava Fiedlerova
06fa702c7c CI: switch to full commit ID in DockerHub push stage
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-19 14:11:58 +02:00
Jaroslava Fiedlerova
0a147851b7 CI: Remove doGitLabMerge.sh
Checkout to tested branch is performed via Jenkins SCM checkout from
internal git repository (git@asterix:/home/git/openairinterface5g.git).
Target branch to checkout is given as {sourceBranch}-{sourceCommit},
which was created and pushed to the internal repository in
RAN-Local-Repo-Push stage.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-19 14:11:48 +02:00
Gabriele Gemmi
da5e5892e4 NR_MAC/PHY: align SRS time_start_position semantics between L2 and L1
3GPP RRC SRS-ResourceMapping.startPosition is defined as an offset
counted backwards from the end of the slot (TS 38.331). OAI MAC was
previously passing the raw RRC value straight into the FAPI SRS PDU's
time_start_position field, and every L1 consumer was then re-deriving
the absolute symbol via NR_SYMBOLS_PER_SLOT - 1 - time_start_position.

External L1 implementations (e.g. NVIDIA Aerial / cuBB) interpret
time_start_position as the absolute symbol index where the SRS lives,
which left the MAC and external L1 disagreeing by one inversion. The
result was SRS being scheduled on the wrong OFDM symbol and the
returned channel matrix being all-zero for most occasions.

Make MAC populate time_start_position with the absolute symbol index
(NR_SYMBOLS_PER_SLOT - 1 - startPosition) and remove the matching
inversion from the L1 consumers so the value flows through unchanged.
The UE side and the shared SRS generator are harmonized to the same
absolute-symbol convention so generate_srs_nr can read nr_srs_info->
l_offset directly regardless of caller (gNB or UE).

* gNB MAC: gNB_scheduler_srs.c invert at PDU-build time; consume the
  already-inverted value when computing l0 for the VRB mask.
* gNB PHY: srs_rx.c (fill + get_signal) and SCHED_NR/phy_procedures_
  nr_gNB.c use srs_pdu->time_start_position directly as l0.
* UE MAC: nr_ue_scheduler.c invert at PDU-build time.
* UE PHY: phy_procedures_nr_ue.c consumes srs_config_pdu->time_start_
  position directly as l0.
* Shared SRS generator: nr_phy_common_srs.c reads nr_srs_info->l_offset
  as the absolute symbol index, matching both gNB and UE call sites.
* Simulators: srssim.c and ulsim.c populate both the gNB-side and
  UE-side SRS PDUs with the absolute symbol index so the nr_srssim
  and nr_ulsim unit tests pass with the new convention.

Signed-off-by: Gabriele Gemmi <g.gemmi@northeastern.edu>
2026-05-19 00:52:08 -04:00
Maxime
0c782e3ee3 Add scheduler architecture documentation
Assisted By Claude Code:Opus-4.6

Signed-off-by: Maxime Elkael <m.elkael@northeastern.edu>
2026-05-18 16:44:20 -04:00
Maxime
410f1a18d7 Refactor UL scheduler into staged pipeline with pluggable function pointers
Assisted By Claude Code:Opus-4.6

Signed-off-by: Maxime Elkael <m.elkael@northeastern.edu>
2026-05-18 16:44:20 -04:00
Maxime
8ed54d7077 Refactor DL scheduler into staged pipeline with pluggable function pointers
Assisted By Claude Code:Opus-4.6

Signed-off-by: Maxime Elkael <m.elkael@northeastern.edu>
2026-05-18 16:44:20 -04:00
Merkebu Girmay
fd6c9efa72 move vrtsim channel and per-UE param configuration from CLI flags to config file
Signed-off-by: Merkebu Girmay <merkebu.girmay@openairinterface.org>
2026-05-18 11:45:31 -07:00
Jaroslava Fiedlerova
e5f30d9033 Merge remote-tracking branch 'origin/NR_UE_2_CW_PDSCH' into integration_2026_w21 (!4056)
restructuring of UE DLSCH code to handle 2 codewords

While making changes in another MR I realized the mess in the handling
of 2 codewords in UE DLSCH code. The intention of the MR is not much to
support 2 codewords at the UE but the cleanup the code which currently
in many parts is written confusedly in between support and non-support.

The key point is that the two codewords need to be treated separately as
they have their own modulation and coding scheme and their own
retransmission information.

Reviewed-By: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-18 19:52:55 +02:00
Jaroslava Fiedlerova
ac6443d9ae Merge remote-tracking branch 'origin/bugfix-a3-config' into integration_2026_w21 (!4120)
bugfix: fix A3 event configuration

Reviewed-by: Cedric Roux <cedric.roux@eurecom.fr>
2026-05-18 19:51:07 +02:00
francescomani
e41766d7f6 restructuring of UE DLSCH code to handle 2 codewords
even if 2 codewords are currently not supported and not tested, the way UE handled the possibility of having 2 codewords was messy, in between support and non-support
the intention of this MR is to cleanup the code more than extend the support

Signed-off-by: Francesco Mani <email@francescomani.it>
2026-05-18 17:36:34 +02:00
Daniele Pugliese
418ef390ca bugfix: fix A3 event configuration
Signed-off-by: Daniele Pugliese <daniele.pugliese@poliba.it>
2026-05-18 12:55:03 +02:00
Jaroslava Fiedlerova
f3acdef63a Merge remote-tracking branch 'origin/ue-cap-file-size-fix' into integration_2026_w21 (!4118)
NR UE: remove hardcoded 64KB limit for UE capability file

When trying to parse UE capability file (xml converted from a COTS UE),
there will be an error:

  [NR_RRC] UE Capabilities XER file /home/ue_caps/ue_cap_20260514_172636.xml is too large (65536)

This is fixed by replacing fixed-size stack buffer with heap allocation
sized to the actual file, allowing large UE capability XER files to be
loaded without hitting the previous 65536-byte limit.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-18 11:15:46 +02:00
Robert Schmidt
6fc2d06077 Merge branch 'integration_2026_w20' into 'develop'
Integration `2026.w20`

- !4110 T-tracer: fix UE_PHY_PDSCH_IQ event fields
- !4114 nrUE: fix noise_power_dB field name in config
- !4109 prepare FAPI PUCCH PDU for MSG4 ACK in advance
- !4115 gNB specific RC
- !4042 Fix Wt (orthogonal code) multiplication for PUSCH DMRS length 2 and noise power estimation
- !4101 bug: LTE: avoid RRC use-after-free, NR: gNB-DU config update: verify cell's origin DU
- ci: skip merge commits when validating Signed-off-by
- !4047 XNAP: add encode/decode, helpers, and unit test for Xn Handover Request
- !4119 Remove PUCCH formar limitation in PRB size function
- !4083 nr_rate_matching: replace byte-by-byte repetition loop with memcpy chunks
- !4121 Collect iperf logs if command fails
- !4116 CI: Cleanup of parameters, change of branch and image naming, remove BuildProxy
- CI: Adjust RAN-PhySim-GraceHopper-5G timing thresholds

Closes #1082, #1081, #1087, and #1077

See merge request oai/openairinterface5g!4111
2026-05-16 09:12:55 +00:00
Jaroslava Fiedlerova
428ed54db4 CI: Adjust RAN-PhySim-GraceHopper-5G timing thresholds
!4083 improved encoding time in some nr_dlsim tests running in
RAN-PhySim-GraceHopper-5G pipeline. Update the timing thresholds
accordingly.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-16 10:20:24 +02:00
Jaroslava Fiedlerova
a2719bd9d7 Merge remote-tracking branch 'origin/ci-clone-repo-1' into integration_2026_w20 (!4116)
CI: Cleanup of parameters, change of branch and image naming, remove BuildProxy

MR Summary:

1. Remove merge step from Jenkinsfile-GitLab-Container

Removes the manual git merge step that was previously performed inside
the `RAN-Container-Parent` pipeline before running CI. The
`doGitLabMerge.sh` call and its merge-conflict failure handler are
dropped from the init stage. The `RAN-Local-Repo-Push` stage no longer
wraps triggerSlaveJob in catchError, instead, its failure posts an MR
comment directly on GitLab to report the issue, and abort the follow-up
test stages.

2. Switch to full commit IDs for image and branch naming

Replaces abbreviated 8-character commit hashes with full commit IDs
everywhere they are used to construct Docker image tags and CI branch
names in internal git repository.

3. Remove L2sim proxy build support

For the `RAN-L2-Sim-Test-4G`, the CI now assumes that the required proxy
image is already present on the testing machine. Build proxy step is no
longer needed, since there haven't been any recent updates in the proxy
repo, and the proxyCommit we currently use is outdated anyway.

4. Rename and clean up parameters in Python scripts

Renames legacy ran* / eNB* prefixed attributes to generic names across
all CI Python classes. Also removes duplicate and unused legacy CLI
argument aliases (--eNBRepository, --eNBBranch, --eNBIPAddress, etc.)
from args_parse.py.

|Old name	 |New name        |
|----------------|----------------|
|ranRepository	 |repository      |
|ranBranch	 |branch          |
|ranCommitID	 |commitID        |
|ranAllowMerge	 |merge           |
|ranTargetBranch |targetBranch    |
|eNBSourceCodePath|workspace      |

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-15 14:57:18 +02:00
Jaroslava Fiedlerova
5f4aff8a1d Merge remote-tracking branch 'origin/ci-iperf-fail-logs' into integration_2026_w20 (!4121)
Collect iperf logs if command fails

The Iperf_Module() checks that the iperf3 command succeeds successfully,
but in case of failure, does not collect the actual logs. Thus, only a
somewhat unhelpful

  Iperf client command failed on X -> Y (return code: ZZZ)

is logged, and it might not be clear why it fails. In this command,
always try to collect the logs, which might contain the actual error.

Fixes: f02c84fc ("CI: enable PCF-based dual-DNN multi-QoS in 25PRB
RFSim")

Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:54:50 +02:00
Jaroslava Fiedlerova
9e4308030b CI: Rename and cleanup parameters inside python
isMergeRequest     --> merge
eNB_SourceCodePath --> workspace
ranBranch          --> branch
ranCommitID        --> commitID
ranTargetBranch    --> targetBranch
ranRepository      --> repository

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:11:00 +02:00
Jaroslava Fiedlerova
228dad1ec2 CI: Remove L2sim proxy build support
Remove the `BuildProxy` method, `Build_Proxy` action, `proxyCommit`
attribute, and all associated XML configuration and unit tests.

The oai-lte-5g-multi-ue-proxy repository has not received any updates
in over two years, and the proxy commit currently used for the build
is outdated as well. Although the proxy image is still required by
RAN-L2-Sim-Test-4G and must remain available on the test machine,
building it as part of the CI pipeline is dropped in this commit.

Instead, the CI will now assume that the corresponding proxy image is
already present as an external dependency, similarly to how CN images
are handled.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:11:00 +02:00
Jaroslava Fiedlerova
d39a40dba2 CI: Switch to full commit IDs for image naming
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:11:00 +02:00
Jaroslava Fiedlerova
181b5d89f8 CI: Switch to full commit IDs for branch naming
Simplify branch handling in CI by removing reliance on shortened commit
hashes and using full commit IDs. This allows to specify branch during
SCM checkout in Jenkins pipeline.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:11:00 +02:00
Jaroslava Fiedlerova
870454d13d CI: Remove merge step from Jenkinsfile-GitLab-Container
The RAN-Container-Parent pipeline retrieves the Jenkinsfile-GitLab-Container
from the develop branch (or from the branch specified for SCM checkout).

The only potential impact of this change concerns modifications to
ci-scripts/checkGitLabMergeRequestLabels.sh. In such cases, updates from the
source branch would not be considered, and the version from develop would be
used instead. This limitation is acceptable, as this script is going to be
removed in the future.

Merge conflict checks are handled in the Local-Repo-Push stage, where the
source branch is merged with the target branch, and the resulting testing
branch is pushed to the internal repository.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-15 14:11:00 +02:00
Robert Schmidt
dd424a2e0c Collect iperf logs if command fails
The Iperf_Module() checks that the iperf3 command succeeds successfully,
but in case of failure, does not collect the actual logs. Thus, only a
somewhat unhelpful

    Iperf client command failed on X -> Y (return code: ZZZ)

is logged, and it might not be clear why it fails. In this commit,
always try to collect the logs, which might contain the actual error,
and only add the diagnostic message at the end of the Iperf report
message.

Fixes: f02c84fcfc ("CI: enable PCF-based dual-DNN multi-QoS in 25PRB
    RFSim")

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-15 13:28:36 +02:00
Robert Schmidt
e83867a505 Merge remote-tracking branch 'origin/rate-matching-memcpy' into integration_2026_w20 (!4083)
nr_rate_matching: replace byte-by-byte repetition loop with memcpy chunks

Currently, in the rate matching, in the low MCS case, we copy byte by
byte which is slow and causes jitter with high number of UEs.

This replaces the loop with chunked memcpy

Here is a benchmark with 128 UEs (done with the tool of !4028 adapted to
work with !3902)

Before:

    === Results: 128 UEs, 273 RBs, MCS 28, 10000 slots (warmup 5) ===
      Avg PDSCH/slot: 1.1 / 128 UEs
      Slot budget: 500 us (mu=1)

      Phase            mean      p50      p90      p99      max
                       (us)     (us)     (us)     (us)     (us)
      ------------------------------------------------------------------
      Scheduler        56.4     47.3     97.9    133.1    274.7
      PHY TX           53.3     47.9     72.9     87.6    320.3
      Total           109.7     97.1    142.6    187.3    435.8
      ------------------------------------------------------------------
      Max total 435.8 us at iter 8528

      Breakdown                            /slot     /call       max   calls
                                            (us)      (us)      (us)
      ---------------------------------------------------------------------------
      Scheduler:
        Total                               60.6      48.5     223.5   12498
          RA scheduling                        -           -           -       0
          UL scheduling                      0.0       0.0       0.1   12498
          DL scheduling (PDCCH+PDSCH)       41.4      33.1     111.4   12498
            RLC data req                     0.8       0.9       6.9    9150
      PHY TX:
        Total                                  -           -           -       0
          DCI generation                       -           -           -       0
          DLSCH encoding                    24.2      24.2     289.2   10000
            segmentation                     0.2       0.2       0.8   11440
            rate matching                   10.5       9.2     275.5   11440
            scrambling                       0.8       0.7      16.5   11440
          DLSCH modulation                   2.9       2.6       9.0   11440
          layer mapping                      6.3       5.5       8.2   11440
          precoding                          6.6       0.5       1.0   138760
          resource mapping                   2.2       0.2       3.0   138760
          phase compensation                 3.3       3.3       3.5   10000
      ---------------------------------------------------------------------------

After:

    === Results: 128 UEs, 273 RBs, MCS 28, 10000 slots (warmup 5) ===
      Avg PDSCH/slot: 1.1 / 128 UEs
      Slot budget: 500 us (mu=1)

      Phase            mean      p50      p90      p99      max
                       (us)     (us)     (us)     (us)     (us)
      ------------------------------------------------------------------
      Scheduler        57.9     48.4    100.4    134.2    462.5
      PHY TX           53.7     48.3     73.3     87.9    131.9
      Total           111.6     99.0    145.9    189.0    512.4
      ------------------------------------------------------------------
      Max total 512.4 us at iter 8367
      WARNING: max total (512 us) exceeds slot budget (500 us)!

      Breakdown                            /slot     /call       max   calls
                                            (us)      (us)      (us)
      ---------------------------------------------------------------------------
      Scheduler:
        Total                               62.3      49.8     226.3   12498
          RA scheduling                        -           -           -       0
          UL scheduling                      0.0       0.0       0.1   12498
          DL scheduling (PDCCH+PDSCH)       42.3      33.8     111.4   12498
            RLC data req                     0.8       0.8       6.6    9150
      PHY TX:
        Total                                  -           -           -       0
          DCI generation                       -           -           -       0
          DLSCH encoding                    24.3      24.3      86.8   10000
            segmentation                     0.2       0.2       0.8   11440
            rate matching                   10.5       9.2      22.0   11440
            scrambling                       0.8       0.7      15.2   11440
          DLSCH modulation                   3.0       2.6       9.6   11440
          layer mapping                      6.4       5.6       8.3   11440
          precoding                          6.6       0.5       1.0   138760
          resource mapping                   2.2       0.2       3.1   138760
          phase compensation                 3.3       3.3       3.5   10000
      ---------------------------------------------------------------------------

Reviewed-By: Francesco Mani <email@francescomani.it>
2026-05-15 08:48:07 +02:00
Robert Schmidt
76b7972bc0 Merge remote-tracking branch 'origin/issue_1077' into integration_2026_w20 (!4119)
Remove PUCCH formar limitation in PRB size function

F3 can be handled same way as F2 according to 9.2.5.2 of 38.213

Closes #1077

Reviewed-By: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-15 08:41:06 +02:00
Alex Jiao
ebd5fc9a5f NR UE: fix UECapabilityInformation encoding buffer size in UL-DCCH message
Replace hardcoded 500-byte stack buffer with MAX_UE_NR_CAPABILITY_SIZE + 16

Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
2026-05-15 10:19:20 +08:00
Robert Schmidt
782ede870e Merge remote-tracking branch 'origin/xn-encdec-handover-messages' into integration_2026_w20 (!4047)
XNAP: add encode/decode, helpers, and unit test for Xn Handover Request

- Add Xn Handover Request message type definitions.
- Implement encoder and decoder for Xn Handover Request. Xn Handover
  Request (3GPP TS 38.423 v16.2.0 §9.1.1.1)
  * Source NG-RAN node UE XnAP ID (M)
  * Cause (M)
  * Target Cell Global ID (M)
  * GUAMI (M)
  * UE Context Information (M)
  * UE History Information (M)
* Add equality check and memory management helpers.
* Extend XNAP library unit tests to cover Handover Request encode/decode
* Add required ASN.1 includes.
* Add utility conversions for:
  * KGNB* handling.
  * BIT_STRING_TO_NR_CELL_IDENTITY (modified to avoid upperbound overflow).

Reviewed-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-14 19:32:44 +02:00
Alex Jiao
9a858c4387 NR UE: remove hardcoded 64KB limit for UE capability file
Replace fixed-size stack buffer with heap allocation sized to the
actual file, allowing large UE capability XER files to be loaded
without hitting the previous 65536-byte limit.

Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
2026-05-14 23:38:37 +08:00
francescomani
9d35b9028c remove format limitation in compute_pucch_prb_size
F3 can be handled same way as F2 according to 9.2.5.2 of 38.213

Signed-off-by: francescomani <email@francescomani.it>
2026-05-14 15:23:07 +02:00
Rakesh BB
8a0be83745 XNAP: add Xn Handover Request enc-dec+unit_test
- Add Xn Handover Request message type definitions.
- Implement encoder and decoder for Xn Handover Request.
  Xn Handover Request (3GPP TS 38.423 v16.2.0 §9.1.1.1)
  - Source NG-RAN node UE XnAP ID (M)
  - Cause (M)
  - Target Cell Global ID (M)
  - GUAMI (M)
  - UE Context Information (M)
  - UE History Information (M)
- Add equality check and memory management helpers.
- Extend XNAP library unit tests to cover Handover Request encode/decode
- Add required ASN.1 includes.
- Add utility conversions for:
  - KGNB* handling.
2026-05-13 16:12:57 +00:00
Rakesh BB
7c29d27487 fix: cast buf bytes to uint64_t before shifting in BIT_STRING_TO_NR_CELL_IDENTITY
Without explicit casts, each buf byte was promoted to a 32-bit signed
int before shifting. For NR Cell Identity (36-bit, per 3GPP TS 38.423
§9.2.2.9), this causes two problems:

  - Shifting buf[0] left by 28 can push bits into or past the sign bit,
    producing undefined/implementation-defined behavior.
  - The 32-bit intermediate result is too narrow to hold a 36-bit value,
    causing silent truncation before the OR-reduction.

Fix by casting each byte to uint64_t prior to the shift, ensuring all
intermediate expressions are evaluated in a 64-bit unsigned domain and
all 36 bits are preserved without overflow.
2026-05-13 15:14:24 +00:00
Shubhika Garg
5fd1f07e7e ci: skip merge commits when validating Signed-off-by
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-05-13 13:58:35 +02:00
Jaroslava Fiedlerova
c0835cca75 CI: remove unused parameters from args_parse.py
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-13 12:17:16 +02:00
Jaroslava Fiedlerova
f39fb4a84b Merge remote-tracking branch 'origin/lte-nr-fixes' into integration_2026_w20 (!4101)
bug: LTE: avoid RRC use-after-free, NR: gNB-DU config update: verify
cell's origin DU

See commits for more details

Reviewed-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-13 11:45:10 +02:00
Jaroslava Fiedlerova
2d9ed6adc4 Merge remote-tracking branch 'origin/pusch_dmrs_bug_fix' into integration_2026_w20 (!4042)
Fix Wt (orthogonal code) multiplication for PUSCH DMRS length 2 and
noise power estimation

This MR:
- Introduce PUSCH DMRS length as a configurable parameter in nr_ulsim.
- Fix bugs related to the use of PUSCH DMRS length 2.
- Fix the noise power estimation.

Note:
- Still cannot validate PUSCH DMRS length 4, since we need more than 5
  layers (DMRS type 1) / 6 layers (DMRS type 2) to test the orthogonal
  spreading over time.
- Yet to implement time domain averaging for PUSCH DMRS length 2 to
  separate orthogonally over time.
- The main bug here is the noise power estimation (removes the duplicate
  division with the number of antennas).

    Reviewed-by: Roberto Louro Magueta <rmagueta@allbesmart.pt>
2026-05-13 11:44:32 +02:00
Jaroslava Fiedlerova
b5c460b17f Merge remote-tracking branch 'origin/gnb_ran_context' into integration_2026_w20 (!4115)
gNB specific RC

This is the framework of #1065 towards remove LTE cross compilation in
NR

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-13 11:43:57 +02:00
Jaroslava Fiedlerova
2214ecd81a Merge remote-tracking branch 'origin/issue_1087' into integration_2026_w20 (!4109)
prepare FAPI PUCCH PDU for MSG4 ACK in advance

Closes #1087

Reviewed-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-05-13 11:43:21 +02:00
Jaroslava Fiedlerova
c555682fdc Merge remote-tracking branch 'origin/fix-typo-config' into integration_2026_w20 (!4114)
nrUE: fix `noise_power_dB` field name in config

Fixes 0b684732 (!2869)

Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-13 11:42:26 +02:00
Jaroslava Fiedlerova
c89fe9737f Merge remote-tracking branch 'origin/fix-ue-t-tracer' into integration_2026_w20 (!4110)
T-tracer: fix UE_PHY_PDSCH_IQ event fields

1. The field was named N_RB_UL and used fp->N_RB_UL in both LTE and NR
   demodulation code, but it should use N_RB_DL for PDSCH
2. The tracer ue.c referenced the wrong buffer field name "pusch_comp"
   instead of "pdsch_comp", causing a crash on startup

Reviewed-by: Cedric Roux <cedric.roux@eurecom.fr>
2026-05-13 11:41:27 +02:00
francescomani
36ed4d9ff1 get gnb ownership of RC to avoid compiling ENB_APP_SRC in NR
Signed-off-by: francescomani <email@francescomani.it>
2026-05-12 18:42:58 +02:00
Guido Casati
d59a0a1a37 fix (NAS UE): decode FGC NAS container length as 16-bit IE
Decode the FGC NAS message container length from the encoded 2-octet
field before validating and copying the payload. This matches TS
24.501, where the NAS message container is a type 6 TLV and its length
is carried in octets 2-3.

Changes:
- add `<arpa/inet.h>` for `ntohs`
- change `ielen` from `uint8_t` to `uint16_t` in
  `decode_fgc_nas_message_container()`
- read the encoded length with `memcpy`, convert it from network byte
  order, and advance the decode pointer by `sizeof(uint16_t)`

Refs:
- TS 124 501 clause 9.11.3.33 "NAS message container"

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-12 16:02:15 +02:00
Shubhika Garg
04a9145fe1 nrue: fix noise_power_dB field in config file
- Fixes 0b684732ee ("YAML as a drop-in replacement for libconfig")

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-05-12 10:52:03 +02:00
Guido Casati
cc8124837a fix (NAS UE): derive outgoing KSI from integrity context
Use the stored NAS KSI only when a valid integrity context is present,
instead of treating idle mode alone as proof that no KSI is available.
This keeps outgoing initial NAS messages aligned with the actual NAS
security state tracked by the UE (including when in IDLE state).

Changes:
- update `set_fgs_ksi()` in `openair3/NAS/NR_UE/nr_nas_msg.c` to return
  `*nas->ksi & 0x07` only when `security_container`,
  `integrity_context`, and `nas->ksi` are present
- remove the unconditional `FGS_IDLE` check and the hardcoded `0x0`
  return path
- keep `NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE` as the fallback when no
  valid integrity context is available

Refs:
- TS 24.501 (Service Request and ngKSI/security context handling)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 17:55:52 +02:00
Guido Casati
3a3629f88a fix (NAS UE): expose kgnb refresh API in NAS header
Move the `nas_itti_kgnb_refresh_req()` declaration to the NAS UE header
so call sites use a single shared prototype.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 17:51:30 +02:00
Guido Casati
db3a2b42cd fix (NAS UE): account for proper 5G-S-TMSI size in Service Request builder
Correct the encoded-size contribution returned by `fill_fgstmsi()` so
Service Request buffer sizing includes the full 5GS mobile identity field.

Changes:
- update `fill_fgstmsi()` return value from 10 to 11 octets: 2-octet length
  field plus 7-octet 5G-S-TMSI payload

Refs:
- TS 24.501 8.2.16 (Service request)
- TS 24.501 9.11.3.4 (5GS mobile identity)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 17:49:00 +02:00
Guido Casati
8a6f1a6775 fix (NAS UE): harden Service Request decode length checks
Add defensive initialization and minimum-length validation in Service
Request decoding.

Changes:
- zero-initialize `fgs_service_request_msg_t` at decode entry
- return error when input length is shorter than the first mandatory octet
- add guard before decoding 5GS mobile identity length field to prevent
  out-of-bounds access

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 17:48:37 +02:00
Guido Casati
12c6d8d3e4 fix (NAS UE): account for Service Request encoder length
Fix remaining length tracking in Service Request encoding so IE
encoding uses the proper buffer budget per step.

Changes:
- decrement `len` by fixed per-field sizes instead of cumulative `encoded`

Refs:
- TS 24.501 8.2.16 (Service request)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 17:47:35 +02:00
Guido Casati
220ef3cdeb fix (RRC UE): zero-initialize initial NAS buffer in RRC setup complete
Initialize the local NAS message container before it is used in the
RRCSetupComplete generation path to avoid undefined state from stack data.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-11 16:58:44 +02:00
Alex Jiao
7026366b5d T-tracer: fix UE_PHY_PDSCH_IQ event fields
1. The field was named N_RB_UL and used fp->N_RB_UL in both LTE and NR
  demodulation code, but it should use N_RB_DL for PDSCH
2. The tracer ue.c referenced the wrong buffer field name "pusch_comp"
  instead of "pdsch_comp", causing a crash on startup

Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
2026-05-11 11:15:35 +08:00
francescomani
b29e7bc2c6 prepare FAPI PUCCH PDU for MSG4 ACK in advance
Signed-off-by: francescomani <email@francescomani.it>
2026-05-10 10:33:11 +02:00
Rakesh Mundlamuri
4e6e0e529d Minor fixes and harmonize maximum number of segments for DL and UL
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-09 12:24:03 +05:30
Rakesh Mundlamuri
5fcf815c56 Fix time domain averaging to average over number of layers
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-07 22:20:52 +05:30
Rakesh Mundlamuri
e557ffdc57 Fix Wt (orthogonal code) multiplication for PUSCH DMRS length 2
Also fixes noise power computation removing the duplicate division over
number of antennas

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-07 22:20:50 +05:30
Robert Schmidt
30bd2c183b gNB-DU config update: Verify cell's origin DU
Verify that a given cell's assoc_id (i.e., it's "host DU") is the
assoc_id of the currently treated DU to avoid treating a config update
of another (potentially malicious) DU.

Closes: #1081
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-05 12:26:04 +02:00
Robert Schmidt
e17823649a Avoid LTE RRC use-after-free
rrc_eNB_free_UE() frees all memory, so set any house-keeping before.

Closes: #1082
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-05 12:19:07 +02:00
Rakesh Mundlamuri
116c893d45 Introduce PUSCH DMRS length as a configurable parameter
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-03 19:04:04 +05:30
Maxime
683d1b594f nr_rate_matching: replace byte-by-byte repetition loop with memcpy chunks 2026-04-29 12:33:55 -04:00
232 changed files with 32361 additions and 3787 deletions

View File

@@ -5,8 +5,8 @@ project (OpenAirInterface LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(OAI_VERSION 2.4.0)
option(CUDA_ENABLE "Enable CUDA accelerated channel simulation" OFF)
if(CUDA_ENABLE)
option(ENABLE_CHANNEL_SIM_CUDA "Enable CUDA accelerated channel simulation" OFF)
if(ENABLE_CHANNEL_SIM_CUDA)
find_package(CUDA REQUIRED)
find_package(CUDAToolkit REQUIRED)
message(STATUS "CUDA explicitly enabled, building with GPU acceleration support.")
@@ -34,7 +34,7 @@ if(CUDA_ENABLE)
message(STATUS "CUDA Explicit Copy path enabled (default ATS was overridden).")
endif()
add_compile_definitions(ENABLE_CUDA)
add_compile_definitions(CHANNEL_SIM_CUDA)
endif()
#########################################################
@@ -155,14 +155,17 @@ add_boolean_option(AVX512 ${AUTODETECT_AVX512} "Whether AVX512 intrinsics is ava
eval_boolean(AUTODETECT_AVX2 DEFINED CPUFLAGS AND CPUFLAGS MATCHES "avx2")
add_boolean_option(AVX2 ${AUTODETECT_AVX2} "Whether AVX2 intrinsics is available on the host processor" ON)
eval_boolean(AUTODETECT_GFNI DEFINED CPUFLAGS AND CPUFLAGS MATCHES "gfni")
add_boolean_option(GFNI ${AUTODETECT_GFNI} "Whether GFNI intrinsics is available on the host processor" ON)
message(STATUS "CPU architecture is ${CMAKE_SYSTEM_PROCESSOR}")
if (CROSS_COMPILE)
message(STATUS "setting as aarch64")
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -lgcc -lrt")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
# The following intrinsics are assumed to be available on any x86 system
# (avx, f16c, fma, gnfi, mmx, pclmul, sse, sse2, sse3, xop)
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_AVX_NATIVE -DSIMDE_X86_AVX_NATIVE -DSIMDE_X86_F16C_NATIVE -DSIMDE_X86_FMA_NATIVE -DSIMDE_X86_GFNI_NATIVE -DSIMDE_X86_MMX_NATIVE -DSIMDE_X86_PCLMUL_NATIVE -DSIMDE_X86_SSE2_NATIVE -DSIMDE_X86_SSE3_NATIVE -DSIMDE_X86_SSE_NATIVE -DSIMDE_X86_XOP_HAVE_COM_ -DSIMDE_X86_XOP_NATIVE")
# (avx, f16c, fma, mmx, pclmul, sse, sse2, sse3, xop)
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_AVX_NATIVE -DSIMDE_X86_AVX_NATIVE -DSIMDE_X86_F16C_NATIVE -DSIMDE_X86_FMA_NATIVE -DSIMDE_X86_MMX_NATIVE -DSIMDE_X86_PCLMUL_NATIVE -DSIMDE_X86_SSE2_NATIVE -DSIMDE_X86_SSE3_NATIVE -DSIMDE_X86_SSE_NATIVE -DSIMDE_X86_XOP_HAVE_COM_ -DSIMDE_X86_XOP_NATIVE")
message(STATUS "AVX512 intrinsics are ${AVX512}")
if(${AVX512})
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_AVX512BW_NATIVE -DSIMDE_X86_AVX512F_NATIVE -DSIMDE_X86_AVX512VL_NATIVE -mavx512bw -march=skylake-avx512 -mtune=skylake-avx512")
@@ -173,13 +176,17 @@ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
if(${AVX2})
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_AVX2_NATIVE -DSIMDE_X86_VPCLMULQDQ_NATIVE")
endif()
if (CPUINFO MATCHES "sse4_1")
message(STATUS "GFNI intrinsics are ${GFNI}")
if(${GFNI})
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_GFNI_NATIVE -mgfni")
endif()
if (CPUFLAGS MATCHES "sse4_1")
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_SSE4_1_NATIVE")
endif()
if(CPUINFO MATCHES "sse4_2")
if(CPUFLAGS MATCHES "sse4_2")
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_SSE4_2_NATIVE")
endif()
if(CPUINFO MATCHES "ssse3")
if(CPUFLAGS MATCHES "ssse3")
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -DSIMDE_X86_SSSE3_NATIVE")
endif()
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7l")
@@ -221,9 +228,14 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(commonOpts "${commonOpts} -Wno-packed-bitfield-compat")
endif()
# clang: suppress complaints about unused command line argument (-rdynamic only
# used during linking)
# used during linking) and "const member leaves the object uninitialized"
if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(commonOpts "${commonOpts} -Wno-unused-command-line-argument")
set(commonOpts "${commonOpts} -Wno-unused-command-line-argument -Wno-default-const-init-field-unsafe")
endif()
if (ENABLE_LDPC_CUDA)
# TODO only on specific targets!!
set(commonOpts "${commonOpts} -DLDPC_CUDA")
endif()
set(CMAKE_C_FLAGS
@@ -842,6 +854,7 @@ set(PHY_NRLDPC_CODINGIF
)
add_library(dfts MODULE ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts_neon.c)
target_compile_options(dfts PRIVATE -fno-semantic-interposition)
set(PHY_SRC_COMMON
${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dci_tools_common.c
@@ -1086,15 +1099,30 @@ target_link_libraries(PHY_UE PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs UTIL)
add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON})
target_link_libraries(PHY_NR_COMMON PUBLIC UTIL)
add_library(PHY_NR ${PHY_NR_SRC})
if (ENABLE_LDPC_CUDA)
find_package(CUDA REQUIRED)
if (NOT CUDA_FOUND)
message(FATAL_ERROR "no CUDA found")
endif()
#SET(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_80 -g -ggdb") # A100
#SET(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_90") # GH200
#SET(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_87") # Jetson
#SET(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_89") # L40S
SET(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_121") # GB10
set(CUDA_VERBOS_BUILD_ON)
cuda_add_library(PHY_NR ${PHY_NR_SRC} ${PHY_NR_SRC_CU})
cuda_add_library(PHY_NR_UE ${PHY_NR_UE_SRC} ${PHY_NR_UE_SRC_CU})
else()
add_library(PHY_NR ${PHY_NR_SRC})
add_library(PHY_NR_UE ${PHY_NR_UE_SRC})
endif()
target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock ds)
add_library(PHY_NR_NO_AVX_256 ${PHY_NR_SRC})
target_link_libraries(PHY_NR_NO_AVX_256 nr_phy_common nr_common)
target_compile_definitions(PHY_NR_NO_AVX_256 PUBLIC USE_128BIT)
add_library(PHY_NR_UE ${PHY_NR_UE_SRC})
target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs nr_phy_common nr_common UTIL polar smallblock)
target_link_libraries(PHY_NR_UE asn1_nr_rrc_hdrs nr_phy_common nr_common UTIL polar smallblock)
add_library(PHY_RU ${PHY_SRC_RU})
target_link_libraries(PHY_RU PRIVATE asn1_lte_rrc_hdrs UTIL)
@@ -1271,7 +1299,9 @@ set (MAC_NR_SRC
${NR_GNB_MAC_DIR}/gNB_scheduler.c
${NR_GNB_MAC_DIR}/gNB_scheduler_bch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_dlsch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_dlsch_default_policies.c
${NR_GNB_MAC_DIR}/gNB_scheduler_ulsch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_ulsch_default_policies.c
${NR_GNB_MAC_DIR}/gNB_scheduler_primitives.c
${NR_GNB_MAC_DIR}/gNB_scheduler_phytest.c
${NR_GNB_MAC_DIR}/gNB_scheduler_uci.c
@@ -1381,7 +1411,6 @@ endif()
add_library(L2_LTE_NR
# temporary solution until 4G/5G code completely untangled (as evidenced by deletion of the following file)
${MAC_DIR}/dummy_functions.c
${ENB_APP_SRC}
)
target_link_libraries(L2_LTE_NR PRIVATE f1ap s1ap nr_rrc)
@@ -2007,12 +2036,19 @@ add_executable(ldpctest
)
target_link_libraries(ldpctest PRIVATE
-Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON -Wl,--end-group
m pthread dl ${T_LIB}
m pthread dl ${T_LIB} ${CUDA_LIBRARIES}
# link 'check_crc' to make it resolved in the LDPC coding libraries
# 'check_crc' is not used in ldpctest so it is not linked in the executable by default
# --whole-archive links 'check_crc' in the executable even though it is note used, see 'man ld'
-Wl,--whole-archive crc_byte -Wl,--no-whole-archive
)
if (ENABLE_LDPC_CUDA)
# ldpctest.c uses cudaHostAlloc(), so include dir/link libr to make available
# TODO don't write it directly into ldpctest, but separate compilation unit?
find_package(CUDA REQUIRED)
target_link_libraries(ldpctest PRIVATE ${CUDA_LIBRARIES})
target_include_directories(ldpctest PRIVATE ${CUDA_INCLUDE_DIRS})
endif()
add_library(physim_common OBJECT ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_unitary_common.c)
target_link_libraries(physim_common PRIVATE UTIL)
@@ -2099,13 +2135,13 @@ target_link_libraries(nr_srssim PRIVATE
m pthread ITTI dl nr_ue_phy_meas physim_common softmodem_common
)
if(CUDA_ENABLE)
if(ENABLE_CHANNEL_SIM_CUDA)
if (TARGET oai_cuda_lib)
target_link_libraries(nr_dlsim PRIVATE oai_cuda_lib)
target_link_libraries(nr_ulsim PRIVATE oai_cuda_lib)
target_compile_definitions(nr_dlsim PRIVATE ENABLE_CUDA)
target_compile_definitions(nr_ulsim PRIVATE ENABLE_CUDA)
target_compile_definitions(nr_dlsim PRIVATE CHANNEL_SIM_CUDA)
target_compile_definitions(nr_ulsim PRIVATE CHANNEL_SIM_CUDA)
endif()
endif()

View File

@@ -8,8 +8,9 @@
<a href="https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-CSSL--v1.0-blue" alt="License"></a>
<a href="https://releases.ubuntu.com/22.04/"><img src="https://img.shields.io/badge/OS-Ubuntu22-Green" alt="Supported OS Ubuntu 22"></a>
<a href="https://releases.ubuntu.com/24.04/"><img src="https://img.shields.io/badge/OS-Ubuntu24-Green" alt="Supported OS Ubuntu 24"></a>
<a href="https://releases.ubuntu.com/26.04/"><img src="https://img.shields.io/badge/OS-Ubuntu26-Green" alt="Supported OS Ubuntu 26"></a>
<a href="https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux"><img src="https://img.shields.io/badge/OS-RHEL9-Green" alt="Supported OS RHEL9"></a>
<a href="https://getfedora.org/en/workstation/"><img src="https://img.shields.io/badge/OS-Fedore41-Green" alt="Supported OS Fedora 43"></a>
<a href="https://getfedora.org/en/workstation/"><img src="https://img.shields.io/badge/OS-Fedore44-Green" alt="Supported OS Fedora 44"></a>
</p>
<p align="center">

View File

@@ -20,12 +20,13 @@ if (params.LockResources != null && params.LockResources.trim().length() > 0)
// Global Parameters. Normally they should be populated when the master job
// triggers the slave job with parameters
def sourceRepository = params.eNB_Repository ?: env.GIT_URL
def sourceBranch = params.eNB_Branch ?: env.GIT_BRANCH
def targetRepository = params.Target_Repository ?: sourceRepository
def targetBranch = params.eNB_TargetBranch ?: 'develop'
def commitID = params.eNB_CommitID ?: env.GIT_COMMIT
def is_MR = params.eNB_mergeRequest ?: false
def INTERNAL_REPO = "asterix:/home/git/openairinterface5g.git"
def sourceBranch = params.sourceBranch ?: env.GIT_BRANCH
def targetBranch = params.targetBranch ?: 'develop'
def commitID = params.sourceCommit ?: env.GIT_COMMIT
def testRepository = params.customRepository ?: INTERNAL_REPO
def testBranch = params.customBranch ?: "${sourceBranch}-${commitID}"
def mergeWithTarget = params.mergeWithTarget ?: false
def flexricOption = ""
def runWithOC = false
@@ -50,7 +51,7 @@ pipeline {
JOB_TIMESTAMP = sh(returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"').trim()
}
// update the build name and description
buildName "${params.eNB_MR}"
buildName "${params.requestNumber}"
buildDescription "Branch : ${sourceBranch}"
}
}
@@ -64,12 +65,12 @@ pipeline {
echo "no LockResources given"
allParametersPresent = false
}
if (params.eNB_SourceCodePath == null) {
echo "no eNB_SourceCodePath given"
if (params.workspace == null) {
echo "no workspace given"
allParametersPresent = false
}
if (params.OC_Credentials != null) {
echo "This pipeline is configured to run with Opensift Cluster."
echo "This pipeline is configured to run with Openshift Cluster."
runWithOC = true
if (params.OC_ProjectName == null) {
echo "no OC_ProjectName given"
@@ -84,21 +85,11 @@ pipeline {
}
echo "CI executor node : ${pythonExecutor}"
echo "Source Repository : ${sourceRepository}"
echo "Source Branch : ${sourceBranch}"
echo "Target Repository : ${targetRepository}"
echo "Target Branch : ${targetBranch}"
echo "Commit ID : ${commitID}"
echo "Testing Repository : ${testRepository}"
echo "Testing Branch : ${testBranch}"
if (allParametersPresent) {
echo '\u2705 \u001B[94m All parameters are present\u001B[0m'
if (is_MR) {
sh "git fetch"
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${sourceBranch} --src-commit ${commitID} --target-branch ${targetBranch} --target-commit latest"
} else {
sh "git fetch"
sh "git checkout -f ${commitID}"
}
} else {
echo "\u274C Some parameters are missing"
sh "./ci-scripts/fail.sh"
@@ -121,28 +112,21 @@ pipeline {
}
}
sh """
python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \
--ranBranch=${sourceBranch} --ranCommitID=${commitID} \
--ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \
${flexricOption} ${mainPythonAllXmlFiles}
python3 main.py --mode=InitiateHtml --repository=${testRepository} \
--branch=${testBranch} --commitID=${commitID} \
${mainPythonAllXmlFiles}
"""
if (runWithOC) {
withCredentials([usernamePassword(credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password')]) {
sh """
python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \
--ranBranch=${sourceBranch} --ranCommitID=${commitID} \
--ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \
${flexricOption} ${mainPythonAllXmlFiles}
"""
for (xmlFile in myXmlTestSuite) {
if (fileExists(xmlFile)) {
try {
timeout (time: 60, unit: 'MINUTES') {
sh """
python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \
--ranBranch=${sourceBranch} --ranCommitID=${commitID} \
--ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \
--eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \
python3 main.py --mode=TesteNB --repository=${testRepository} \
--branch=${testBranch} \
--ranAllowMerge=${mergeWithTarget} --targetBranch=${targetBranch} \
--workspace=${params.workspace} --XMLTestFile=${xmlFile} \
--OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} \
${flexricOption}
"""
@@ -160,10 +144,10 @@ pipeline {
try {
timeout (time: 60, unit: 'MINUTES') {
sh """
python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \
--ranBranch=${sourceBranch} --ranCommitID=${commitID} \
--ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \
--eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \
python3 main.py --mode=TesteNB --repository=${testRepository} \
--branch=${testBranch} \
--ranAllowMerge=${mergeWithTarget} --targetBranch=${targetBranch} \
--workspace=${params.workspace} --XMLTestFile=${xmlFile} \
${flexricOption}
"""
}

View File

@@ -87,9 +87,6 @@ pipeline {
// since a bit, in push events, gitlabUserEmail is not populated
gitCommitAuthorEmailAddr = env.gitlabUserEmail
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
// GitLab-Jenkins plugin integration is lacking to perform the merge by itself
// Doing it manually --> it may have merge conflicts
// Validate MR commits: checks for missing Signed-off-by and merge commits
def mrValidationLog = "signedCommit_${env.BUILD_NUMBER}.log"
def mrValidationExitCode = sh(
@@ -104,7 +101,6 @@ pipeline {
if (mrValidationExitCode >= 2) {
error("${mrValidationMessage}")
}
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
} else {
echo "Git Branch is ${GIT_BRANCH}"
echo "Git Commit is ${GIT_COMMIT}"
@@ -115,29 +111,19 @@ pipeline {
}
}
}
post {
failure {
script {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Merge Conflicts -- Cannot perform CI"
addGitLabMRComment comment: message
currentBuild.result = 'FAILURE'
}
}
}
}
stage ("Local-Repo-Push") {
steps {
script {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
triggerSlaveJob ('RAN-Local-Repo-Push', 'Local-Repo-Push')
}
triggerSlaveJob ('RAN-Local-Repo-Push', 'Local-Repo-Push')
}
}
post {
failure {
script {
echo "Push to local repository KO"
failingStages += '\n * RAN-Local-Repo-Push'
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Cannot perform CI - merge validation failed (merge conflict, git operation failure, or internal CI error)."
addGitLabMRComment comment: message
currentBuild.result = 'FAILURE'
}
}
}
@@ -894,12 +880,19 @@ def triggerSlaveJob (jobName, gitlabStatusName) {
def localStatus = build job: jobName,
parameters: [
string(name: 'eNB_Repository', value: String.valueOf(GIT_URL)),
string(name: 'targetRepo', value: String.valueOf(GIT_URL)),
string(name: 'SourceRepo', value: String.valueOf(env.gitlabSourceRepoHttpUrl)),
string(name: 'sourceRepo', value: String.valueOf(env.gitlabSourceRepoHttpUrl)),
string(name: 'eNB_Branch', value: String.valueOf(env.gitlabSourceBranch)),
string(name: 'sourceBranch', value: String.valueOf(env.gitlabSourceBranch)),
string(name: 'eNB_CommitID', value: String.valueOf(env.gitlabMergeRequestLastCommit)),
string(name: 'sourceCommit', value: String.valueOf(env.gitlabMergeRequestLastCommit)),
string(name: 'eNB_MR', value: String.valueOf(MR_NUMBER)),
string(name: 'requestNumber', value: String.valueOf(MR_NUMBER)),
booleanParam(name: 'eNB_mergeRequest', value: "MERGE".equals(env.gitlabActionType)),
string(name: 'eNB_TargetBranch', value: String.valueOf(env.gitlabTargetBranch))
booleanParam(name: 'mergeWithTarget', value: "MERGE".equals(env.gitlabActionType)),
string(name: 'eNB_TargetBranch', value: String.valueOf(env.gitlabTargetBranch)),
string(name: 'targetBranch', value: String.valueOf(env.gitlabTargetBranch))
], propagate: false
def localResult = localStatus.getResult()
echo "${jobName} Slave Job status is ${localResult}"
@@ -914,14 +907,13 @@ def triggerSlaveJob (jobName, gitlabStatusName) {
def triggerCN5GSlaveJob (jobName, gitlabStatusName) {
if ("MERGE".equals(env.gitlabActionType)) {
shortenShaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%h" --abbrev=8 ' + env.gitlabMergeRequestLastCommit
shortenShaOne = shortenShaOne.trim()
branchName = env.gitlabSourceBranch.replaceAll("/", "-").trim()
fullRanTag = OAI_Registry + '/oai-gnb:' + env.gitlabSourceBranch + '-' + shortenShaOne
shaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%H" ' + env.gitlabMergeRequestLastCommit
shaOne = shaOne.trim()
fullRanTag = OAI_Registry + '/oai-gnb:' + env.gitlabSourceBranch + '-' + shaOne
} else {
shortenShaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%h" --abbrev=8 ' + env.GIT_COMMIT
shortenShaOne = shortenShaOne.trim()
fullRanTag = OAI_Registry + '/oai-gnb:develop-' + shortenShaOne
shaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%H" ' + env.GIT_COMMIT
shaOne = shaOne.trim()
fullRanTag = OAI_Registry + '/oai-gnb:develop-' + shaOne
}
// Workaround for the "cancelled" GitLab pipeline notification
// The slave job is triggered with the propagate false so the following commands are executed

View File

@@ -27,21 +27,26 @@ pipeline {
steps {
script {
def missingParams = []
if (!params.SourceRepo?.trim()) { missingParams << 'SourceRepo' }
if (!params.eNB_Branch?.trim()) { missingParams << 'eNB_Branch' }
if (!params.eNB_CommitID?.trim()) { missingParams << 'eNB_CommitID' }
if (!params.eNB_TargetBranch?.trim()) { missingParams << 'eNB_TargetBranch' }
if (!params.eNB_Repository?.trim()) { missingParams << 'eNB_Repository' }
if (!params.sourceRepo?.trim()) { missingParams << 'sourceRepo' }
if (!params.sourceBranch?.trim()) { missingParams << 'sourceBranch' }
if (!params.sourceCommit?.trim()) { missingParams << 'sourceCommit' }
if (params.mergeWithTarget) {
if (!params.targetRepo?.trim()) { missingParams << 'targetRepo' }
if (!params.targetBranch?.trim()) { missingParams << 'targetBranch' }
}
if (missingParams) {
error "Missing required parameters: ${missingParams.join(', ')}"
}
echo "Source Repo : ${params.SourceRepo}"
echo "Source Branch : ${params.eNB_Branch}"
echo "Source Commit : ${params.eNB_CommitID}"
echo "Target Branch : ${params.eNB_TargetBranch}"
echo "Target Repo : ${params.eNB_Repository}"
echo "Source Repo : ${params.sourceRepo}"
echo "Source Branch : ${params.sourceBranch}"
echo "Source Commit : ${params.sourceCommit}"
echo "Merge : ${params.mergeWithTarget}"
if (params.mergeWithTarget) {
echo "Target Branch : ${params.targetBranch}"
echo "Target Repo : ${params.targetRepo}"
}
}
}
}
@@ -51,6 +56,7 @@ pipeline {
git config user.email "jenkins@openairinterface.org"
git config user.name "OAI Jenkins"
git remote remove source || true
git remote remove target || true
git remote remove internal-repo || true
"""
}
@@ -58,31 +64,38 @@ pipeline {
stage('Add source repo & fetch branches') {
steps {
sh """
git remote add source ${params.SourceRepo} || true
git fetch source ${params.eNB_Branch}
git fetch origin ${params.eNB_TargetBranch}
git remote add source ${params.sourceRepo} || true
git fetch source ${params.sourceBranch}
"""
script {
if (params.mergeWithTarget) {
sh """
git remote add target ${params.targetRepo} || true
git fetch target ${params.targetBranch}
"""
}
}
}
}
stage('Create temporary branch for CI') {
steps {
script {
def shortCommit = params.eNB_CommitID.take(8)
env.NEW_BRANCH = "${params.eNB_Branch}-${shortCommit}"
env.NEW_BRANCH = "${params.sourceBranch}-${params.sourceCommit}"
echo "New branch: ${env.NEW_BRANCH}"
}
sh """
# Checkout source commit
git checkout -B ${env.NEW_BRANCH} ${params.eNB_CommitID}
git checkout -B ${env.NEW_BRANCH} ${params.sourceCommit}
"""
}
}
stage('Merge target into source') {
when { expression { return params.mergeWithTarget } }
steps {
sh """
echo "Merging target branch ${params.eNB_TargetBranch} into source branch ${env.NEW_BRANCH}"
if ! git merge --ff origin/${params.eNB_TargetBranch} \
-m "Merge ${params.eNB_TargetBranch} into ${params.eNB_Branch} for CI"; then
echo "Merging target branch ${params.targetBranch} into source branch ${env.NEW_BRANCH}"
if ! git merge --ff target/${params.targetBranch} \
-m "Merge ${params.targetBranch} into ${params.sourceBranch} for CI"; then
echo "Merge conflicts detected. Aborting."
git merge --abort || true
exit 1

View File

@@ -56,7 +56,7 @@ pipeline {
echo "Forced Tag is ${WEEK_TAG}"
}
}
WEEK_SHA = sh returnStdout: true, script: 'git log -n1 --pretty=format:"%h" origin/develop | cut -c 1-8'
WEEK_SHA = sh returnStdout: true, script: 'git log -n1 --pretty=format:"%H" origin/develop'
WEEK_SHA = WEEK_SHA.trim()
withCredentials([

View File

@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
# Python for CI of OAI-eNB + COTS-UE
# Python for CI testing
#
# Required Python Version
# Python 3.x
@@ -44,74 +44,55 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,CLUSTER):
elif re.match(r'^\-\-mode=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-mode=(.+)$', myArgv, re.IGNORECASE)
mode = matchReg.group(1)
elif re.match(r'^\-\-eNBRepository=(.+)$|^\-\-ranRepository(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBRepository=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBRepository=(.+)$', myArgv, re.IGNORECASE)
elif re.match(r'^\-\-repository=(.+)$|^\-\-ranRepository=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-repository=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-repository=(.+)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-ranRepository=(.+)$', myArgv, re.IGNORECASE)
CiTestObj.ranRepository = matchReg.group(1)
RAN.ranRepository=matchReg.group(1)
HTML.ranRepository=matchReg.group(1)
CONTAINERS.ranRepository=matchReg.group(1)
CLUSTER.ranRepository=matchReg.group(1)
elif re.match(r'^\-\-eNB_AllowMerge=(.+)$|^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNB_AllowMerge=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNB_AllowMerge=(.+)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE)
CiTestObj.repository = matchReg.group(1)
RAN.repository=matchReg.group(1)
HTML.repository=matchReg.group(1)
CONTAINERS.repository=matchReg.group(1)
CLUSTER.repository=matchReg.group(1)
elif re.match(r'^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE)
doMerge = matchReg.group(1)
if ((doMerge == 'true') or (doMerge == 'True')):
CiTestObj.ranAllowMerge = True
RAN.ranAllowMerge=True
HTML.ranAllowMerge=True
CONTAINERS.ranAllowMerge=True
CLUSTER.ranAllowMerge=True
elif re.match(r'^\-\-eNBBranch=(.+)$|^\-\-ranBranch=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBBranch=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBBranch=(.+)$', myArgv, re.IGNORECASE)
RAN.merge=True
CONTAINERS.merge=True
CLUSTER.merge=True
elif re.match(r'^\-\-branch=(.+)$|^\-\-ranBranch=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-branch=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-branch=(.+)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-ranBranch=(.+)$', myArgv, re.IGNORECASE)
CiTestObj.ranBranch = matchReg.group(1)
RAN.ranBranch=matchReg.group(1)
HTML.ranBranch=matchReg.group(1)
CONTAINERS.ranBranch=matchReg.group(1)
CLUSTER.ranBranch=matchReg.group(1)
elif re.match(r'^\-\-eNBCommitID=(.*)$|^\-\-ranCommitID=(.*)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBCommitID=(.*)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBCommitID=(.*)$', myArgv, re.IGNORECASE)
CiTestObj.branch = matchReg.group(1)
RAN.branch=matchReg.group(1)
HTML.branch=matchReg.group(1)
CONTAINERS.branch=matchReg.group(1)
CLUSTER.branch=matchReg.group(1)
elif re.match(r'^\-\-commitID=(.*)$|^\-\-ranCommitID=(.*)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-commitID=(.*)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-commitID=(.*)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-ranCommitID=(.*)$', myArgv, re.IGNORECASE)
CiTestObj.ranCommitID = matchReg.group(1)
RAN.ranCommitID=matchReg.group(1)
HTML.ranCommitID=matchReg.group(1)
CONTAINERS.ranCommitID=matchReg.group(1)
CLUSTER.ranCommitID=matchReg.group(1)
elif re.match(r'^\-\-eNBTargetBranch=(.*)$|^\-\-ranTargetBranch=(.*)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBTargetBranch=(.*)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-eNBTargetBranch=(.*)$', myArgv, re.IGNORECASE)
HTML.commitID=matchReg.group(1)
elif re.match(r'^\-\-targetBranch=(.*)$|^\-\-ranTargetBranch=(.*)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-targetBranch=(.*)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-targetBranch=(.*)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-ranTargetBranch=(.*)$', myArgv, re.IGNORECASE)
CiTestObj.ranTargetBranch = matchReg.group(1)
RAN.ranTargetBranch=matchReg.group(1)
HTML.ranTargetBranch=matchReg.group(1)
CONTAINERS.ranTargetBranch=matchReg.group(1)
CLUSTER.ranTargetBranch=matchReg.group(1)
elif re.match(r'^\-\-eNBIPAddress=(.+)$|^\-\-eNB[1-2]IPAddress=(.+)$', myArgv, re.IGNORECASE):
print("parameters --eNB*IPAddress ignored")
elif re.match(r'^\-\-eNBUserName=(.+)$|^\-\-eNB[1-2]UserName=(.+)$', myArgv, re.IGNORECASE):
print("parameters --eNB*UserName ignored")
elif re.match(r'^\-\-eNBPassword=(.+)$|^\-\-eNB[1-2]Password=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB*Password ignored")
elif re.match(r'^\-\-eNBSourceCodePath=(.+)$|^\-\-eNB[1-2]SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE):
RAN.targetBranch=matchReg.group(1)
CONTAINERS.targetBranch=matchReg.group(1)
CLUSTER.targetBranch=matchReg.group(1)
elif re.match(r'^\-\-workspace=(.+)$|^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE):
if re.match(r'^\-\-workspace=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-workspace=(.+)$', myArgv, re.IGNORECASE)
else:
matchReg = re.match(r'^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE)
RAN.eNBSourceCodePath=matchReg.group(1)
CONTAINERS.eNBSourceCodePath=matchReg.group(1)
CLUSTER.eNBSourceCodePath=matchReg.group(1)
elif re.match(r'^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB1SourceCodePath ignored")
elif re.match(r'^\-\-eNB2SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
print("parameter --eNB2SourceCodePath ignored")
RAN.workspace=matchReg.group(1)
CONTAINERS.workspace=matchReg.group(1)
CLUSTER.workspace=matchReg.group(1)
elif re.match(r'^\-\-XMLTestFile=(.+)$', myArgv, re.IGNORECASE):
matchReg = re.match(r'^\-\-XMLTestFile=(.+)$', myArgv, re.IGNORECASE)
CiTestObj.testXMLfiles.append(matchReg.group(1))

View File

@@ -50,17 +50,16 @@ def OC_logout(cmd):
class Cluster:
def __init__(self):
self.eNBSourceCodePath = ""
self.workspace = ""
self.OCUserName = ""
self.OCPassword = ""
self.OCProjectName = ""
self.OCUrl = OCUrl
self.OCRegistry = OCRegistry
self.ranRepository = ""
self.ranBranch = ""
self.ranCommitID = ""
self.ranAllowMerge = False
self.ranTargetBranch = ""
self.repository = ""
self.branch = ""
self.merge = False
self.targetBranch = ""
self.cmd = None
def _recreate_entitlements(self):
@@ -101,7 +100,7 @@ class Cluster:
def _start_build(self, name):
# will return "immediately" but build runs in background
# if multiple builds are started at the same time, this can take some time, however
ret = self.cmd.run(f'oc start-build {name} --from-dir={self.eNBSourceCodePath} --exclude=""')
ret = self.cmd.run(f'oc start-build {name} --from-dir={self.workspace} --exclude=""')
regres = re.search(r'build.build.openshift.io/(?P<jobname>[a-zA-Z0-9\-]+) started', ret.stdout)
if ret.returncode != 0 or ret.stdout.count('Uploading finished') != 1 or regres is None:
logging.error(f"error during oc start-build: {ret.stdout}")
@@ -157,7 +156,7 @@ class Cluster:
OC_logout(cmd)
HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_LOGIN_FAIL)
return False
tag = cls_containerize.CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
tag = self.branch
registry = f'{self.OCRegistry}/{CI_OC_RAN_NAMESPACE}'
success, msg = cls_containerize.Containerize.Pull_Image(cmd, images, tag, tag_prefix, registry, None, None)
OC_logout(cmd)
@@ -174,12 +173,12 @@ class Cluster:
return (image, archiveArtifact(self.cmd, ctx, fn))
def BuildClusterImage(self, ctx, node, HTML):
if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
if self.repository == '' or self.branch == '':
HELP.GenericHelp(CONST.Version)
raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {ranBranch} ranCommitID {self.ranCommitID}')
lSourcePath = self.eNBSourceCodePath
raise ValueError(f'Insufficient Parameter: repository {self.repository} branch {self.branch}')
lSourcePath = self.workspace
if node == '' or lSourcePath == '':
raise ValueError('Insufficient Parameter: eNBSourceCodePath missing')
raise ValueError('Insufficient Parameter: workspace missing')
ocUserName = self.OCUserName
ocPassword = self.OCPassword
ocProjectName = self.OCProjectName
@@ -200,22 +199,22 @@ class Cluster:
baseTag = 'develop'
forceBaseImageBuild = False
if self.ranAllowMerge: # merging MR branch into develop -> temporary image
branchName = self.ranBranch.replace('/','-')
imageTag = f'{branchName}-{self.ranCommitID[0:8]}'
if self.ranTargetBranch == 'develop':
if self.merge: # merging MR branch into develop -> temporary image
branchName = self.branch.replace('/','-')
imageTag = f'{branchName}'
if self.targetBranch == 'develop':
ret = self.cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base.rhel9 | grep --colour=never -i INDEX')
result = re.search('index', ret.stdout)
if result is not None:
forceBaseImageBuild = True
baseTag = 'ci-temp'
# if the branch name contains integration_20xx_wyy, let rebuild ran-base
result = re.search('integration_20([0-9]{2})_w([0-9]{2})', self.ranBranch)
result = re.search('integration_20([0-9]{2})_w([0-9]{2})', self.branch)
if not forceBaseImageBuild and result is not None:
forceBaseImageBuild = True
baseTag = 'ci-temp'
else:
imageTag = f'develop-{self.ranCommitID[0:8]}'
imageTag = self.branch
forceBaseImageBuild = True
# logging to OC Cluster and then switch to corresponding project

View File

@@ -32,35 +32,15 @@ from cls_ci_helper import archiveArtifact
IMAGES = ['oai-enb', 'oai-lte-ru', 'oai-lte-ue', 'oai-gnb', 'oai-nr-cuup', 'oai-gnb-aw2s', 'oai-nr-ue', 'oai-enb-asan', 'oai-gnb-asan', 'oai-lte-ue-asan', 'oai-nr-ue-asan', 'oai-nr-cuup-asan', 'oai-gnb-aerial', 'oai-gnb-fhi72']
DEFAULT_REGISTRY = "gracehopper3-oai.sboai.cs.eurecom.fr"
def CreateWorkspace(host, sourcePath, ranRepository, ranCommitID, ranTargetBranch, ranAllowMerge):
if ranCommitID == '':
logging.error('need ranCommitID in CreateWorkspace()')
raise ValueError('Insufficient Parameter in CreateWorkspace(): need ranCommitID')
def CreateWorkspace(host, sourcePath, repository, branch):
script = "scripts/create_workspace.sh"
options = f"{sourcePath} {ranRepository} {ranCommitID}"
if ranAllowMerge:
if ranTargetBranch == '':
ranTargetBranch = 'develop'
options += f" {ranTargetBranch}"
options = f"{sourcePath} {repository} {branch}"
logging.info(f'execute "{script}" with options "{options}" on node {host}')
with cls_cmd.getConnection(host) as c:
ret = c.exec_script(script, 90, options)
logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}')
return ret.returncode == 0
def CreateTag(ranCommitID, ranBranch, ranAllowMerge):
if ranCommitID == 'develop':
return 'develop'
shortCommit = ranCommitID[0:8]
if ranAllowMerge:
# Allowing contributor to have a name/branchName format
branchName = ranBranch.replace('/','-')
tagToUse = f'{branchName}-{shortCommit}'
else:
tagToUse = f'develop-{shortCommit}'
return tagToUse
def AnalyzeBuildLogs(image, lf):
committed = False
tagged = False
@@ -75,7 +55,7 @@ def AnalyzeBuildLogs(image, lf):
committed = committed or lineHasCommit
if re.search(r'error:|Errors|ERROR', line):
errors.append(f"=> {line.strip()}")
status = (committed or tagged) and len(errors) == 0
status = (committed or tagged)
logging.info(f"Analyzing {image}, file {lf}: {status=}, {len(errors)} errors")
for e in errors:
logging.info(e)
@@ -172,14 +152,12 @@ class Containerize():
def __init__(self):
self.ranRepository = ''
self.ranBranch = ''
self.ranAllowMerge = False
self.ranCommitID = ''
self.ranTargetBranch = ''
self.eNBSourceCodePath = ''
self.repository = ''
self.branch = ''
self.merge = False
self.targetBranch = ''
self.workspace = ''
self.imageKind = ''
self.proxyCommit = None
self.yamlPath = ''
self.services = ''
self.deploymentTag = ''
@@ -193,16 +171,27 @@ class Containerize():
#-----------------------------------------------------------
def BuildImage(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
logging.debug('Building on server: ' + node)
cmd = cls_cmd.getConnection(node)
log_files = []
dockerfileprefix = '.ubuntu'
baseImage = 'ran-base'
baseTag = 'develop'
buildImage = 'ran-build'
forceBaseImageBuild = False
imageTag = 'develop'
result = re.search('native_cuda_armv8', self.imageKind)
if result is not None:
baseImage = 'ran-base-cuda'
buildImage = 'ran-build-cuda'
dockerfileprefix = '.cuda.ubuntu'
# we always build the ran-build image with all targets
# Creating a tupple with the imageName, the DockerFile prefix pattern, targetName and sanitized option
imageNames = [('ran-build', 'build', 'ran-build', '')]
imageNames = [(buildImage, 'build', f'{buildImage}', '')]
result = re.search('eNB', self.imageKind)
if result is not None:
imageNames.append(('oai-enb', 'eNB', 'oai-enb', ''))
@@ -242,23 +231,23 @@ class Containerize():
imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', ''))
imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup', ''))
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
result = re.search('native_cuda_armv8', self.imageKind)
if result is not None:
imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', ''))
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
cmd.cd(lSourcePath)
baseImage = 'ran-base'
baseTag = 'develop'
forceBaseImageBuild = False
imageTag = 'develop'
if (self.ranAllowMerge):
if (self.merge):
imageTag = 'ci-temp'
if self.ranTargetBranch == 'develop':
if self.targetBranch == 'develop':
cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base{dockerfileprefix} | grep --colour=never -i INDEX')
result = re.search('index', cmd.getBefore())
if result is not None:
forceBaseImageBuild = True
baseTag = 'ci-temp'
# if the branch name contains integration_20xx_wyy, let rebuild ran-base
result = re.search('integration_20([0-9]{2})_w([0-9]{2})', self.ranBranch)
result = re.search('integration_20([0-9]{2})_w([0-9]{2})', self.branch)
if not forceBaseImageBuild and result is not None:
forceBaseImageBuild = True
baseTag = 'ci-temp'
@@ -276,17 +265,17 @@ class Containerize():
# On when the base image docker file is being modified.
if forceBaseImageBuild:
cmd.run(f"docker image rm {baseImage}:{baseTag}")
logfile = f'{lSourcePath}/cmake_targets/log/ran-base.docker.log'
logfile = f'{lSourcePath}/cmake_targets/log/{baseImage}.docker.log'
option = f" --build-arg UBUNTU_IMAGE={DEFAULT_REGISTRY}/{ubuntuImage}"
cmd.run(f"docker build --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{dockerfileprefix} {option} . &> {logfile}", timeout=1600)
t = ("ran-base", archiveArtifact(cmd, ctx, logfile))
t = (baseImage, archiveArtifact(cmd, ctx, logfile))
log_files.append(t)
# First verify if the base image was properly created.
ret = cmd.run(f"docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}")
allImagesSize = {}
if ret.returncode != 0:
logging.error('\u001B[1m Could not build properly ran-base\u001B[0m')
logging.error(f'\u001B[1m Could not build properly {baseImage}\u001B[0m')
# Recover the name of the failed container?
cmd.run(f"docker ps --quiet --filter \"status=exited\" -n1 | xargs --no-run-if-empty docker rm -f")
cmd.run(f"docker image prune --force")
@@ -299,10 +288,10 @@ class Containerize():
if result is not None:
size = float(result.group("size")) / 1000000
imageSizeStr = f'{size:.1f}'
logging.debug(f'\u001B[1m ran-base size is {imageSizeStr} Mbytes\u001B[0m')
allImagesSize['ran-base'] = f'{imageSizeStr} Mbytes'
logging.debug(f'\u001B[1m {baseImage} size is {imageSizeStr} Mbytes\u001B[0m')
allImagesSize[baseImage] = f'{imageSizeStr} Mbytes'
else:
logging.debug('ran-base size is unknown')
logging.debug(f'{baseImage} size is unknown')
# Build the target image(s)
status = True
@@ -313,11 +302,11 @@ class Containerize():
cmd.run(f'sed -i -e "s#{baseImage}:latest#{baseImage}:{baseTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
# target images should use the proper ran-build image
if image != 'ran-build' and "-asan" in name:
cmd.run(f'sed -i -e "s#ran-build:latest#ran-build-asan:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
cmd.run(f'sed -i -e "s#{buildImage}:latest#{buildImage}-asan:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
elif "fhi72" in name or name == "oai-nr-oru":
cmd.run(f'sed -i -e "s#ran-build-fhi72:latest#ran-build-fhi72:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
elif image != 'ran-build':
cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
cmd.run(f'sed -i -e "s#{buildImage}:latest#{buildImage}:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}')
if image == 'oai-gnb-aerial':
cmd.run('cp -f /opt/nvidia-ipc/nvipc_src.2026.01.07.tar.gz .')
logfile = f'{lSourcePath}/cmake_targets/log/{name}.docker.log'
@@ -376,90 +365,8 @@ class Containerize():
logging.error('\u001B[1m Building OAI Images Failed\u001B[0m')
return status
def BuildProxy(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
logging.debug('Building on server: ' + node)
ssh = cls_cmd.getConnection(node)
oldRanCommidID = self.ranCommitID
oldRanRepository = self.ranRepository
oldRanAllowMerge = self.ranAllowMerge
oldRanTargetBranch = self.ranTargetBranch
self.ranCommitID = self.proxyCommit
self.ranRepository = 'https://github.com/EpiSci/oai-lte-5g-multi-ue-proxy.git'
self.ranAllowMerge = False
self.ranTargetBranch = 'master'
# Let's remove any previous run artifacts if still there
ssh.run('docker image prune --force')
# Remove any previous proxy image
ssh.run('docker image rm oai-lte-multi-ue-proxy:latest')
tag = self.proxyCommit
logging.debug('building L2sim proxy image for tag ' + tag)
# check if the corresponding proxy image with tag exists. If not, build it
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
buildProxy = ret.returncode != 0 # if no image, build new proxy
if buildProxy:
ssh.run(f'rm -Rf {lSourcePath}')
success = CreateWorkspace(node, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
if not success:
raise Exception("could not clone proxy repository")
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')
archiveArtifact(ssh, ctx, fullpath)
ssh.run('docker image prune --force')
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
if ret.returncode != 0:
logging.error('\u001B[1m Build of L2sim proxy failed\u001B[0m')
ssh.close()
HTML.CreateHtmlTestRow('commit ' + tag, 'KO', CONST.ALL_PROCESSES_OK)
return False
else:
logging.debug('L2sim proxy image for tag ' + tag + ' already exists, skipping build')
# retag the build images to that we pick it up later
ssh.run(f'docker image tag proxy:{tag} oai-lte-multi-ue-proxy:latest')
# we assume that the host on which this is built will also run the proxy. The proxy
# currently requires the following command, and the docker-compose up mechanism of
# the CI does not allow to run arbitrary commands. Note that the following actually
# belongs to the deployment, not the build of the proxy...
logging.warning('the following command belongs to deployment, but no mechanism exists to exec it there!')
ssh.run('sudo ifconfig lo: 127.0.0.2 netmask 255.0.0.0 up')
# to prevent accidentally overwriting data that might be used later
self.ranCommitID = oldRanCommidID
self.ranRepository = oldRanRepository
self.ranAllowMerge = oldRanAllowMerge
self.ranTargetBranch = oldRanTargetBranch
ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}')
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()
allImagesSize = {}
if result is not None:
imageSize = float(result.group('size')) / 1000000
logging.debug('\u001B[1m proxy size is ' + ('%.0f' % imageSize) + ' Mbytes\u001B[0m')
allImagesSize['proxy'] = str(round(imageSize,1)) + ' Mbytes'
logging.info('\u001B[1m Building L2sim Proxy Image Pass\u001B[0m')
HTML.CreateHtmlTestRow('commit ' + tag, 'OK', CONST.ALL_PROCESSES_OK)
return True
else:
logging.error('proxy size is unknown')
allImagesSize['proxy'] = 'unknown'
logging.error('\u001B[1m Build of L2sim proxy failed\u001B[0m')
HTML.CreateHtmlTestRow('commit ' + tag, 'KO', CONST.ALL_PROCESSES_OK)
return False
def BuildRunTests(self, ctx, node, dockerfile, runtime_opt, ctest_opt, HTML):
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
logging.debug('Building on server: ' + node)
cmd = cls_cmd.getConnection(node)
cmd.cd(lSourcePath)
@@ -467,8 +374,8 @@ class Containerize():
# check that ran-base image exists as we expect it
baseImage = 'ran-base'
baseTag = 'develop'
if self.ranAllowMerge:
if self.ranTargetBranch == 'develop':
if self.merge:
if self.targetBranch == 'develop':
cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base.ubuntu | grep --colour=never -i INDEX')
result = re.search('index', cmd.getBefore())
if result is not None:
@@ -511,7 +418,7 @@ class Containerize():
return False
def Push_Image_to_Local_Registry(self, node, HTML, tag_prefix=""):
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
logging.debug('Pushing images to server: ' + node)
ssh = cls_cmd.getConnection(node)
imagePrefix = DEFAULT_REGISTRY
@@ -524,10 +431,10 @@ class Containerize():
return False
orgTag = 'develop'
if self.ranAllowMerge:
if self.merge:
orgTag = 'ci-temp'
for image in IMAGES:
tagToUse = tag_prefix + CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
tagToUse = tag_prefix + self.branch
imageTag = f"{image}:{tagToUse}"
ret = ssh.run(f'docker image tag {image}:{orgTag} {imagePrefix}/{imageTag}')
if ret.returncode != 0:
@@ -540,7 +447,7 @@ class Containerize():
HTML.CreateHtmlTestRow(msg, 'KO', CONST.ALL_PROCESSES_OK)
return False
# Creating a develop tag on the local private registry
if not self.ranAllowMerge:
if not self.merge:
devTag = f"{tag_prefix}develop"
ssh.run(f'docker image tag {image}:{orgTag} {imagePrefix}/{image}:{devTag}')
ssh.run(f'docker push {imagePrefix}/{image}:{devTag}')
@@ -588,7 +495,7 @@ class Containerize():
def Pull_Image_from_Registry(self, HTML, node, images, tag=None, tag_prefix="", registry=DEFAULT_REGISTRY, username="oaicicd", password="oaicicd"):
logging.debug(f'\u001B[1m Pulling image(s) on server: {node}\u001B[0m')
if not tag:
tag = CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
tag = self.branch
with cls_cmd.getConnection(node) as cmd:
success, msg = Containerize.Pull_Image(cmd, images, tag, tag_prefix, registry, username, password)
param = f"on node {node}"
@@ -601,7 +508,7 @@ class Containerize():
def Clean_Test_Server_Images(self, HTML, node, images, tag=None):
logging.debug(f'\u001B[1m Cleaning image(s) from server: {node}\u001B[0m')
if not tag:
tag = CreateTag(self.ranCommitID, self.ranBranch, self.ranAllowMerge)
tag = self.branch
status = True
with cls_cmd.getConnection(node) as myCmd:
@@ -620,17 +527,17 @@ class Containerize():
return status
def Create_Workspace(self, node, HTML):
lSourcePath = self.eNBSourceCodePath
success = CreateWorkspace(node, lSourcePath, self.ranRepository, self.ranCommitID, self.ranTargetBranch, self.ranAllowMerge)
sourcePath = self.workspace
success = CreateWorkspace(node, sourcePath, self.repository, self.branch)
if success:
HTML.CreateHtmlTestRowQueue('N/A', 'OK', [f"created workspace {lSourcePath}"])
HTML.CreateHtmlTestRowQueue('N/A', 'OK', [f"created workspace {sourcePath} on node {node}"])
else:
HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["cannot create workspace"])
return success
def DeployObject(self, ctx, node, HTML):
num_attempts = self.num_attempts
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
yaml = self.yamlPath.strip('/')
wd = f'{lSourcePath}/{yaml}'
wd_yaml = f'{wd}/docker-compose.y*ml'
@@ -674,7 +581,7 @@ class Containerize():
return deployed
def StopObject(self, ctx, node, HTML):
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
if not self.services:
raise ValueError(f'no services provided')
logging.info(f'\u001B[1m Stopping objects "{self.services}" from server: {node}\u001B[0m')
@@ -703,7 +610,7 @@ class Containerize():
return success
def UndeployObject(self, ctx, node, HTML, to_analyze):
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
logging.info(f'\u001B[1m Undeploying all objects from server {node}\u001B[0m')
yaml = self.yamlPath.strip('/')
wd = f'{lSourcePath}/{yaml}'
@@ -741,7 +648,7 @@ class Containerize():
def AnalyzeRTStatsObject(self, HTML, node, ctx, thresholds, service=None):
logging.info(f'Analyzing realtime stats from server: {node}')
yaml = self.yamlPath.strip('/')
wd = f'{self.eNBSourceCodePath}/{yaml}'
wd = f'{self.workspace}/{yaml}'
wd_yaml = f'{wd}/docker-compose.y*ml'
with cls_cmd.getConnection(node) as cmd:

View File

@@ -33,11 +33,9 @@ class HTMLManagement():
self.htmlHeaderCreated = False
self.htmlFooterCreated = False
self.ranRepository = ''
self.ranBranch = ''
self.ranCommitID = ''
self.ranAllowMerge = False
self.ranTargetBranch = ''
self.repository = ''
self.branch = ''
self.commitID = ''
self.nbTestXMLfiles = 0
self.htmlTabRefs = []
@@ -96,47 +94,18 @@ class HTMLManagement():
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' <tr>\n')
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-cloud-upload"></span> GIT Repository </td>\n')
self.htmlFile.write(' <td><a href="' + self.ranRepository + '">' + self.ranRepository + '</a></td>\n')
self.htmlFile.write(' <td><a href="' + self.repository + '">' + self.repository + '</a></td>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' <tr>\n')
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-wrench"></span> Job Trigger </td>\n')
if (self.ranAllowMerge):
self.htmlFile.write(' <td>Merge-Request</td>\n')
else:
self.htmlFile.write(' <td>Push to Branch</td>\n')
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-log-out"></span> Test Branch </td>\n')
self.htmlFile.write(' <td>' + self.branch + '</td>\n')
self.htmlFile.write(' </tr>\n')
commit_message = subprocess.check_output("git log -n1 --pretty=format:\"%s\" " + self.commitID, shell=True, universal_newlines=True)
commit_message = commit_message.strip()
self.htmlFile.write(' <tr>\n')
if (self.ranAllowMerge):
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-log-out"></span> Source Branch </td>\n')
else:
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tree-deciduous"></span> Branch</td>\n')
self.htmlFile.write(' <td>' + self.ranBranch + '</td>\n')
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-comment"></span> Commit Message </td>\n')
self.htmlFile.write(' <td>' + commit_message + '</td>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' <tr>\n')
if (self.ranAllowMerge):
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tag"></span> Source Commit ID </td>\n')
else:
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tag"></span> Commit ID </td>\n')
self.htmlFile.write(' <td>' + self.ranCommitID + '</td>\n')
self.htmlFile.write(' </tr>\n')
if self.ranAllowMerge != '' and self.ranCommitID != 'develop':
commit_message = subprocess.check_output("git log -n1 --pretty=format:\"%s\" " + self.ranCommitID, shell=True, universal_newlines=True)
commit_message = commit_message.strip()
self.htmlFile.write(' <tr>\n')
if (self.ranAllowMerge):
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-comment"></span> Source Commit Message </td>\n')
else:
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-comment"></span> Commit Message </td>\n')
self.htmlFile.write(' <td>' + commit_message + '</td>\n')
self.htmlFile.write(' </tr>\n')
if (self.ranAllowMerge):
self.htmlFile.write(' <tr>\n')
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-log-in"></span> Target Branch </td>\n')
if (self.ranTargetBranch == ''):
self.htmlFile.write(' <td>develop</td>\n')
else:
self.htmlFile.write(' <td>' + self.ranTargetBranch + '</td>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' </table>\n')
self.htmlFile.write(' <br>\n')

View File

@@ -282,12 +282,8 @@ def Deploy_Physim(ctx, HTML, node, workdir, script, options):
class OaiCiTest():
def __init__(self):
self.ranRepository = ''
self.ranBranch = ''
self.ranCommitID = ''
self.ranAllowMerge = False
self.ranTargetBranch = ''
self.repository = ''
self.branch = ''
self.testXMLfiles = []
self.ping_args = ''
self.ping_packetloss_threshold = ''
@@ -503,8 +499,6 @@ class OaiCiTest():
logging.warning(f'Iperf3 server on port {port} detected and terminated')
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t)
client_ret = cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -c {svrIP} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t, reportNonZero=False)
if client_ret.returncode != 0:
return (False, f'{ue_header}\nIperf client command failed on {bindIP} -> {svrIP}:{port} (return code: {client_ret.returncode})')
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)
@@ -513,6 +507,10 @@ class OaiCiTest():
else:
status, msg = Iperf_analyzeV3TCPJson(dest_filename, self.iperf_tcp_rate_target)
# add some diagnostic messages if the actual iperf command returned non-zero
if client_ret.returncode != 0:
msg = f'{msg}\nIperf client command failed on {bindIP} -> {svrIP}:{port} (return code: {client_ret.returncode})'
return (status, f'{ue_header}\n{msg}')
def Iperf(self, ctx, node, HTML, infra_file="ci_infra.yaml"):

View File

@@ -207,3 +207,40 @@ channelmod:
forgetfact: 0
offset: 0
ds_tdl: 0
vrtsim:
role: server
# Channel mode: exactly one of the three modes below should be active.
#
# No channel - plain passthrough, no impairments:
# cirdb: 0
# chanmod: 0
#
# Built-in channel model defined in the channelmod: section above:
# chanmod: 1
# cirdb: 0
#
# External taps emitter publishing over a nanomsg socket.
# Requires a running emit_from_db.py and build with -DOAI_VRTSIM_TAPS_CLIENT=ON:
# cirdb: 0
# chanmod: 0
# taps-socket: "tcp://127.0.0.1:5555"
#
# In-process CIR database: reads precomputed 3GPP TDL taps directly from
# cir_db.bin. No external emitter required. Generate the database offline
# with cir_generator.py. Use cirdb-path to point to the directory containing
# cir_db.bin and cir_db.yaml.
cirdb_file: /cirdb/cir_db.bin
cirdb_yaml: /cirdb/cir_db.yaml
#
# Per-UE channel configuration for multi-UE operation
ue_config:
- antennas: "2x2" # NxM where N=UE-TX, M=gNB-TX; M must equal RU nb_tx
model_id: 0 # 0=TDL-A 1=TDL-B 2=TDL-C 3=TDL-D 4=TDL-E
ds_ns: 1.0 # RMS delay spread in ns
speed_mps: 1.5 # UE speed in m/s
# aoa_deg: 0.0 # angle of arrival in degrees; TDL-D/E only
- antennas: "1x2"
model_id: 0
ds_ns: 1.0
speed_mps: 1.5
# aoa_deg: 0.0

View File

@@ -30,7 +30,7 @@ channelmod:
- model_name: rfsimu_channel_ue0
type: AWGN
ploss_dB: 20
nose_power_dB: -2
noise_power_dB: -2
forgetfact: 0
offset: 0
ds_tdl: 0

View File

@@ -1,126 +0,0 @@
#!/bin/bash
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
function usage {
echo "OAI GitLab merge request applying script"
echo ""
echo "Usage:"
echo "------"
echo ""
echo " doGitLabMerge.sh [OPTIONS] [MANDATORY_OPTIONS]"
echo ""
echo "Mandatory Options:"
echo "------------------"
echo ""
echo " --src-branch #### OR -sb ####"
echo " Specify the source branch of the merge request."
echo ""
echo " --src-commit #### OR -sc ####"
echo " Specify the source commit ID (SHA-1) of the merge request."
echo ""
echo " --target-branch #### OR -tb ####"
echo " Specify the target branch of the merge request (usually develop)."
echo ""
echo " --target-commit #### OR -tc ####"
echo " Specify the target commit ID (SHA-1) of the merge request."
echo ""
echo "Options:"
echo "--------"
echo " --help OR -h"
echo " Print this help message."
echo ""
}
if [ $# -ne 8 ] && [ $# -ne 1 ]
then
echo "Syntax Error: not the correct number of arguments"
echo ""
usage
exit 1
fi
checker=0
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-h|--help)
shift
usage
exit 0
;;
-sb|--src-branch)
SOURCE_BRANCH="$2"
let "checker|=0x1"
shift
shift
;;
-sc|--src-commit)
SOURCE_COMMIT_ID="$2"
let "checker|=0x2"
shift
shift
;;
-tb|--target-branch)
TARGET_BRANCH="$2"
let "checker|=0x4"
shift
shift
;;
-tc|--target-commit)
TARGET_COMMIT_ID="$2"
let "checker|=0x8"
shift
shift
;;
*)
echo "Syntax Error: unknown option: $key"
echo ""
usage
exit 1
esac
done
if [[ $TARGET_COMMIT_ID == "latest" ]]
then
TARGET_COMMIT_ID=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH`
fi
echo "Source Branch is : $SOURCE_BRANCH"
echo "Source Commit ID is : $SOURCE_COMMIT_ID"
echo "Target Branch is : $TARGET_BRANCH"
echo "Target Commit ID is : $TARGET_COMMIT_ID"
if [ $checker -ne 15 ]
then
echo ""
echo "Syntax Error: missing option"
echo ""
usage
exit 1
fi
git config user.email "jenkins@openairinterface.org"
git config user.name "OAI Jenkins"
git checkout -f $SOURCE_COMMIT_ID > checkout.txt 2>&1
STATUS=`grep -E -c "fatal: reference is not a tree" checkout.txt`
rm -f checkout.txt
if [ $STATUS -ne 0 ]
then
echo "fatal: reference is not a tree --> $SOURCE_COMMIT_ID"
STATUS=-1
exit $STATUS
fi
git merge --ff $TARGET_COMMIT_ID -m "Temporary merge for CI"
STATUS=`git status | grep -E -c "You have unmerged paths.|fix conflicts"`
if [ $STATUS -ne 0 ]
then
echo "There are merge conflicts.. Cannot perform further build tasks"
STATUS=-1
fi
exit $STATUS

View File

@@ -26,7 +26,7 @@ RUN cmake -GNinja -DENABLE_PHYSIM_TESTS=ON -DENABLE_TESTS=ON \
-DSANITIZE_UNDEFINED=OFF -DSANITIZE_ADDRESS=OFF \
-DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS=-Werror \
-DPHYSIM_CHECK_FILES="ThresholdsCuda.cmake" \
-DCUDA_ENABLE=ON \
-DENABLE_CHANNEL_SIM_CUDA=ON \
-DUSE_UNIFIED_MEMORY=ON \
-DUSE_ATS_MEMORY=OFF \
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/oai-ran/cmake_targets/ran_build/build \

View File

@@ -26,7 +26,7 @@ RUN cmake -GNinja -DENABLE_PHYSIM_TESTS=ON -DENABLE_TESTS=ON \
-DSANITIZE_UNDEFINED=OFF -DSANITIZE_ADDRESS=OFF \
-DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS=-Werror \
-DPHYSIM_CHECK_FILES="ThresholdsCuda.cmake" \
-DCUDA_ENABLE=ON \
-DENABLE_CHANNEL_SIM_CUDA=ON \
-DUSE_UNIFIED_MEMORY=ON \
-DUSE_ATS_MEMORY=OFF \
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/oai-ran/cmake_targets/ran_build/build \

View File

@@ -22,4 +22,4 @@ WORKDIR /oai-ran
COPY . .
WORKDIR /oai-ran/build
RUN cmake -GNinja -DENABLE_TESTS=ON -DCUDA_ENABLE=ON .. && ninja tests
RUN cmake -GNinja -DENABLE_TESTS=ON -DENABLE_CHANNEL_SIM_CUDA=ON .. && ninja tests

View File

@@ -28,16 +28,15 @@ def GenericHelp(vers):
print(' images are available locally, will not remove any images and will run inside the current repo directory')
def GitSrvHelp(repository,branch,commit,mergeallow,targetbranch):
print(' --ranRepository=[OAI RAN Repository URL] -- ' + repository)
print(' --ranBranch=[OAI RAN Repository Branch] -- ' + branch)
print(' --ranCommitID=[OAI RAN Repository Commit SHA-1] -- ' + commit)
print(' --repository=[OAI RAN Repository URL] -- ' + repository)
print(' --branch=[OAI RAN Repository Branch] -- ' + branch)
print(' --commitID=[OAI RAN Repository Commit SHA-1] -- ' + commit)
print(' --ranAllowMerge=[Allow Merge Request (with target branch) (true or false)] -- ' + mergeallow)
print(' --ranTargetBranch=[Target Branch in case of a Merge Request] -- ' + targetbranch)
print(' --targetBranch=[Target Branch in case of a Merge Request] -- ' + targetbranch)
def eNBSrvHelp(sourcepath):
print(' --eNBSourceCodePath=[eNB\'s Source Code Path] -- ' + sourcepath)
def SrvHelp(sourcepath):
print(' --workspace=[directory for workspaces on remote hosts] -- ' + sourcepath)
def XmlHelp(filename):
print(' --XMLTestFile=[XML Test File to be run] -- ' + filename)
print(' Note: multiple xml files can be specified (--XMLFile=File1 ... --XMLTestFile=FileN) when HTML headers are created ("InitiateHtml" mode)')

View File

@@ -65,21 +65,16 @@ def ExecuteActionWithParam(action, ctx, node):
global HTML
global CONTAINERS
global CLUSTER
if action == 'Build_eNB' or action == 'Build_Image' or action == 'Build_Proxy' or action == "Build_Cluster_Image" or action == "Build_Run_Tests":
if action == 'Build_eNB' or action == 'Build_Image' or action == "Build_Cluster_Image" or action == "Build_Run_Tests":
RAN.Build_eNB_args=test.findtext('Build_eNB_args')
CONTAINERS.imageKind=test.findtext('kind')
proxy_commit = test.findtext('proxy_commit')
dockerfile = test.findtext('dockerfile') or ''
runtime_opt = test.findtext('runtime-opt') or ''
ctest_opt = test.findtext('ctest-opt') or ''
if proxy_commit is not None:
CONTAINERS.proxyCommit = proxy_commit
if action == 'Build_eNB':
success = cls_native.Native.Build(ctx, node, HTML, RAN.eNBSourceCodePath, RAN.Build_eNB_args)
success = cls_native.Native.Build(ctx, node, HTML, RAN.workspace, RAN.Build_eNB_args)
elif action == 'Build_Image':
success = CONTAINERS.BuildImage(ctx, node, HTML)
elif action == 'Build_Proxy':
success = CONTAINERS.BuildProxy(ctx, node, HTML)
elif action == 'Build_Cluster_Image':
success = CLUSTER.BuildClusterImage(ctx, node, HTML)
elif action == 'Build_Run_Tests':
@@ -153,16 +148,16 @@ def ExecuteActionWithParam(action, ctx, node):
elif action == 'Deploy_Run_OC_PhySim':
oc_release = test.findtext('oc_release')
script = "scripts/oc-deploy-physims.sh"
image_tag = cls_containerize.CreateTag(CLUSTER.ranCommitID, CLUSTER.ranBranch, CLUSTER.ranAllowMerge)
options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {CLUSTER.eNBSourceCodePath}"
workdir = CLUSTER.eNBSourceCodePath
image_tag = CLUSTER.branch
options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {CLUSTER.workspace}"
workdir = CLUSTER.workspace
success = cls_oaicitest.Deploy_Physim(ctx, HTML, node, workdir, script, options)
elif action == 'Build_Deploy_PhySim':
ctest_opt = test.findtext('ctest-opt') or ''
script = test.findtext('script')
options = f"{CONTAINERS.eNBSourceCodePath} {ctest_opt}"
workdir = CONTAINERS.eNBSourceCodePath
options = f"{CONTAINERS.workspace} {ctest_opt}"
workdir = CONTAINERS.workspace
success = cls_oaicitest.Deploy_Physim(ctx, HTML, node, workdir, script, options)
elif action == 'DeployCoreNetwork' or action == 'UndeployCoreNetwork':
@@ -174,7 +169,7 @@ def ExecuteActionWithParam(action, ctx, node):
CONTAINERS.yamlPath = test.findtext('yaml_path')
CONTAINERS.services = test.findtext('services')
CONTAINERS.num_attempts = int(test.findtext('num_attempts') or 1)
CONTAINERS.deploymentTag = cls_containerize.CreateTag(CONTAINERS.ranCommitID, CONTAINERS.ranBranch, CONTAINERS.ranAllowMerge)
CONTAINERS.deploymentTag = CONTAINERS.branch
if action == 'Deploy_Object':
success = CONTAINERS.DeployObject(ctx, node, HTML)
elif action == 'Stop_Object':
@@ -195,7 +190,7 @@ def ExecuteActionWithParam(action, ctx, node):
success = CONTAINERS.Create_Workspace(node, HTML)
elif action == 'LicenceAndFormattingCheck':
success = SCA.StaticCodeAnalysis.LicenceAndFormattingCheck(ctx, node, HTML, RAN.eNBSourceCodePath, RAN.ranBranch, RAN.ranAllowMerge, RAN.ranTargetBranch)
success = SCA.StaticCodeAnalysis.LicenceAndFormattingCheck(ctx, node, HTML, RAN.workspace, RAN.branch, RAN.merge, RAN.targetBranch)
elif action == 'Push_Local_Registry':
tag_prefix = test.findtext('tag_prefix') or ""
@@ -219,14 +214,14 @@ def ExecuteActionWithParam(action, ctx, node):
elif action == 'Custom_Command':
command = test.findtext('command')
# Allow referencing repository workspace path in XML via %%workspace%%
command = command.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath)
command = command.replace("%%workspace%%", CONTAINERS.workspace)
success = cls_oaicitest.Custom_Command(HTML, node, command)
elif action == 'Custom_Script':
script = test.findtext('script')
args = test.findtext('args')
# Allow referencing repository workspace path in XML via %%workspace%%
script = script.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath)
script = script.replace("%%workspace%%", CONTAINERS.workspace)
success = cls_oaicitest.Custom_Script(HTML, node, script, args)
elif action == 'Pull_Cluster_Image':
@@ -370,15 +365,15 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
logging.info('\u001B[1m Starting Scenario: ' + CiTestObj.testXMLfiles[0] + '\u001B[0m')
logging.info('\u001B[1m----------------------------------------\u001B[0m')
if re.match('^TesteNB$', mode, re.IGNORECASE):
if RAN.ranRepository == '' or RAN.ranBranch == '' or RAN.eNBSourceCodePath == '':
if RAN.repository == '' or RAN.branch == '' or RAN.workspace == '':
HELP.GenericHelp(CONST.Version)
if RAN.ranRepository == '':
HELP.GitSrvHelp(RAN.ranRepository, RAN.ranBranch, RAN.ranCommitID, RAN.ranAllowMerge, RAN.ranTargetBranch)
if RAN.eNBSourceCodePath == '':
HELP.eNBSrvHelp(RAN.eNBSourceCodePath)
if RAN.repository == '':
HELP.GitSrvHelp(RAN.repository, RAN.branch, RAN.merge, RAN.targetBranch)
if RAN.workspace == '':
HELP.SrvHelp(RAN.workspace)
sys.exit('Insufficient Parameter')
else:
if CiTestObj.ranRepository == '' or CiTestObj.ranBranch == '':
if CiTestObj.repository == '' or CiTestObj.branch == '':
HELP.GenericHelp(CONST.Version)
sys.exit('UE: Insufficient Parameter')

View File

@@ -71,7 +71,7 @@ fi
# Check unsigned commits
# ----------------------------
unsignedCommits=$(
for c in $(git rev-list "$TARGET_BRANCH".."$SOURCE_BRANCH"); do
for c in $(git rev-list "$TARGET_BRANCH".."$SOURCE_BRANCH" --no-merges); do
if ! git log -1 --format=%B "$c" | grep -q "Signed-off-by:"; then
git log -1 --format='%h' "$c"
fi

View File

@@ -32,12 +32,11 @@ class RANManagement():
def __init__(self):
self.ranRepository = ''
self.ranBranch = ''
self.ranAllowMerge = False
self.ranCommitID = ''
self.ranTargetBranch = ''
self.eNBSourceCodePath = ''
self.repository = ''
self.branch = ''
self.merge = False
self.targetBranch = ''
self.workspace = ''
self.Initialize_eNB_args = ''
self.imageKind = ''
self.eNBOptions = ['', '', '']
@@ -57,7 +56,7 @@ class RANManagement():
raise ValueError(f"{node=}")
logging.debug('Starting eNB/gNB on server: ' + node)
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
cmd = cls_cmd.getConnection(node)
# Initialize_eNB_args usually start with -O and followed by the location in repository
@@ -103,7 +102,7 @@ class RANManagement():
def TerminateeNB(self, ctx, node, HTML, to_analyze):
logging.debug('Stopping eNB/gNB on server: ' + node)
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
cmd = cls_cmd.getConnection(node)
ret = cmd.run('ps -aux | grep --color=never -e softmodem | grep -v grep')
result = re.search('-softmodem', ret.stdout)
@@ -138,7 +137,7 @@ class RANManagement():
def AnalyzeRTStats(self, HTML, node, ctx, thresholds):
logging.info(f'Analyzing realtime stats from server: {node}')
lSourcePath = self.eNBSourceCodePath
lSourcePath = self.workspace
logdir = f'{lSourcePath}/cmake_targets'
with cls_cmd.getConnection(node) as cmd:

View File

@@ -30,15 +30,13 @@ docker tag oai-nr-ue oai-ci/oai-nr-ue:develop-${SHORT_COMMIT_SHA}
docker tag oai-gnb oai-ci/oai-gnb:develop-${SHORT_COMMIT_SHA}
docker tag oai-nr-cuup oai-ci/oai-nr-cuup:develop-${SHORT_COMMIT_SHA}
python3 main.py --mode=InitiateHtml --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \
--ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \
--ranTargetBranch=NONE \
python3 main.py --mode=InitiateHtml --repository=NONE --branch=${CURRENT_BRANCH} \
--XMLTestFile=xml_files/${TESTCASE} --local --datefmt="%H:%M:%S"
python3 main.py --mode=TesteNB --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \
--ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \
--ranTargetBranch=NONE \
--eNBSourceCodePath=${REPO_PATH} \
python3 main.py --mode=TesteNB --repository=NONE --branch=${CURRENT_BRANCH} \
--ranAllowMerge=false \
--targetBranch=NONE \
--workspace=${REPO_PATH} \
--XMLTestFile=${TESTCASE} --local --datefmt="%H:%M:%S"
RET=$?

View File

@@ -6,22 +6,16 @@ function die() {
exit 1
}
[ $# -ge 3 -a $# -le 4 ] || die "usage: $0 <directory> <repository> <ref> [<merge-ref>]"
[ $# -eq 3 ] || die "usage: $0 <directory> <repository> <branch>"
set -ex
dir=$1
repo=$2
ref=$3
merge=$4
branch=$3
rm -rf ${dir}
git clone --filter=blob:none -n ${repo} ${dir}
git clone --depth=1 --branch "${branch}" "${repo}" "${dir}"
cd ${dir}
git config user.email "jenkins@openairinterface.org"
git config user.name "OAI Jenkins"
git config advice.detachedHead false
mkdir -p cmake_targets/log
git checkout -f ${ref}
[ -n "${merge}" ] && git fetch origin ${merge} && git merge --ff FETCH_HEAD -m "Temporary merge for CI"
exit 0

View File

@@ -24,7 +24,7 @@ class TestBuild(unittest.TestCase):
self._d = tempfile.mkdtemp()
logging.warning(f"temporary directory: {self._d}")
self.node = 'localhost'
self.cont.eNBSourceCodePath = self._d
self.cont.workspace = self._d
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
def tearDown(self):
@@ -33,10 +33,5 @@ class TestBuild(unittest.TestCase):
cmd.run(f"rm -rf {self._d}")
cmd.run(f'rm -rf {self.ctx.logPath}')
def test_build_proxy(self):
self.cont.proxyCommit = "b64d9bce986b38ca59e8582864ade3fcdd05c0dc"
success = self.cont.BuildProxy(self.ctx, self.node, self.html)
self.assertTrue(success)
if __name__ == '__main__':
unittest.main()

View File

@@ -41,10 +41,9 @@ class TestDeploymentMethods(unittest.TestCase):
self.ci = cls_oaicitest.OaiCiTest()
self.cont = cls_containerize.Containerize()
self.cont.yamlPath = ''
self.cont.ranAllowMerge = True
self.cont.ranBranch = ''
self.cont.ranCommitID = ''
self.cont.eNBSourceCodePath = os.getcwd()
self.cont.merge = True
self.cont.branch = ''
self.cont.workspace = os.getcwd()
self.cont.num_attempts = 3
self.node = 'localhost'
self.ctx = TestCaseCtx.Default(tempfile.mkdtemp())
@@ -136,13 +135,12 @@ class TestDeploymentMethods(unittest.TestCase):
self.assertTrue(undeploy)
def test_create_workspace(self):
self.cont.eNBSourceCodePath = tempfile.mkdtemp()
self.cont.ranRepository = "https://gitlab.eurecom.fr/oai/openairinterface5g.git"
self.cont.ranCommitID = "05f9c975eeecbca1bdff5940affad44465f1301f"
self.cont.ranBranch = "develop"
self.cont.workspace = tempfile.mkdtemp()
self.cont.repository = "https://gitlab.eurecom.fr/oai/openairinterface5g.git"
self.cont.branch = "develop"
ws = self.cont.Create_Workspace(self.node, self.html)
with cls_cmd.LocalCmd() as cmd:
cmd.run(f"rm -rf {self.cont.eNBSourceCodePath}")
cmd.run(f"rm -rf {self.cont.workspace}")
self.assertTrue(ws)
def test_undeploy_loganalysis(self):

View File

@@ -22,7 +22,7 @@ class TestDeploymentMethods(unittest.TestCase):
self.html = cls_oai_html.HTMLManagement()
self.html.testCaseId = "000000"
self.cont = cls_containerize.Containerize()
self.cont.eNBSourceCodePath = os.getcwd()
self.cont.workspace = os.getcwd()
def test_pull_clean_local_reg(self):
# the pull function has the authentication at the internal cluster hardcoded

View File

@@ -9,21 +9,17 @@ rm -f ${file}
cd ../../
python3 main.py \
--mode=InitiateHtml \
--ranRepository=https://gitlab.eurecom.fr/oai/openairinterface5g.git \
--ranBranch=${branch} \
--ranCommitID=${commit} \
--ranAllowMerge=true \
--ranTargetBranch=develop \
--repository=https://gitlab.eurecom.fr/oai/openairinterface5g.git \
--branch=${branch} \
--XMLTestFile=tests/test-runner/test.xml
python3 main.py \
--mode=TesteNB \
--ranRepository=https://gitlab.eurecom.fr/oai/openairinterface5g.git \
--ranBranch=${branch} \
--ranCommitID=${commit} \
--repository=https://gitlab.eurecom.fr/oai/openairinterface5g.git \
--branch=${branch} \
--ranAllowMerge=true \
--ranTargetBranch=develop \
--eNBSourceCodePath=NONE \
--targetBranch=develop \
--workspace=NONE \
--XMLTestFile=tests/test-runner/test.xml
python3 main.py \

View File

@@ -1,5 +1,4 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
- Build_Proxy
- Build_Cluster_Image
- Pull_Cluster_Image
- Build_eNB

View File

@@ -7,7 +7,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>obelix</node>
</testCase>

View File

@@ -7,7 +7,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>gracehopper3-oai</node>
</testCase>

View File

@@ -0,0 +1,28 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>build-cuda-tab</htmlTabRef>
<htmlTabName>Build Container Images for ARM with CUDA</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<node>jetson3-oai</node>
</testCase>
<testCase>
<class>Build_Image</class>
<desc>Build all Images</desc>
<node>jetson3-oai</node>
<kind>native_cuda_armv8</kind>
</testCase>
<testCase>
<class>Push_Local_Registry</class>
<desc>Push Images to Local Registry</desc>
<node>jetson3-oai</node>
<tag_prefix>armv8_cuda_</tag_prefix>
</testCase>
</testCaseList>

View File

@@ -7,7 +7,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>jetson3-oai</node>
</testCase>

View File

@@ -1,15 +0,0 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>l2sim-4glte-5gnr-proxy-build</htmlTabRef>
<htmlTabName>Build L2sim proxy image</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Build_Proxy</class>
<desc>Build L2sim Proxy Image</desc>
<node>localhost</node>
<proxy_commit>b64d9bce986b38ca59e8582864ade3fcdd05c0dc</proxy_commit>
</testCase>
</testCaseList>

View File

@@ -34,7 +34,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -34,7 +34,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -35,7 +35,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>
@@ -54,7 +54,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 1</desc>
<desc>Create new Workspace</desc>
<node>carabe</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>hutch</node>
</testCase>

View File

@@ -34,7 +34,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>starsky</node>
</testCase>

View File

@@ -28,7 +28,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>starsky</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>starsky</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>starsky</node>
</testCase>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>starsky</node>
</testCase>

View File

@@ -31,7 +31,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>gracehopper1-oai</node>
</testCase>

View File

@@ -31,7 +31,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>gracehopper1-oai</node>
</testCase>

View File

@@ -0,0 +1,192 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>JETSON-5G-NRUE-CUDA</htmlTabRef>
<htmlTabName>30 MHz TDD SA JETSON with CUDA</htmlTabName>
<htmlTabIcon>tasks</htmlTabIcon>
<testCase>
<class>DeployCoreNetwork</class>
<desc>Initialize 5G Core</desc>
<cn_id>oc-cn5g-00104</cn_id>
</testCase>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>jetson2-oai</node>
<images>oai-nr-ue</images>
<tag_prefix>armv8_cuda_</tag_prefix>
</testCase>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>gracehopper1-oai</node>
<images>oai-gnb-aerial</images>
<tag_prefix>arm_</tag_prefix>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>jetson2-oai</node>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>gracehopper1-oai</node>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Configure 30 MHz bandwidth on WNC RU</desc>
<node>gracehopper1-oai</node>
<script>scripts/set-wnc-bandwidth.sh</script>
<args>30</args>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy PNF/Nvidia CUBB in a container</desc>
<node>gracehopper1-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_gnb_aerial_30MHz</yaml_path>
<services>nv-cubb</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy VNF in a container</desc>
<node>gracehopper1-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_gnb_aerial_30MHz</yaml_path>
<services>oai-gnb-aerial</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy nrUE (TDD/Band78/40MHz/N310) in a container</desc>
<yaml_path>ci-scripts/yaml_files/sa_b200_jetson_cuda_nrue</yaml_path>
<node>jetson2-oai</node>
<num_attempts>3</num_attempts>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach OAIUE</desc>
<id>oai_ue_jetson2</id>
</testCase>
<testCase>
<class>Custom_Command</class>
<desc>Configure IP route</desc>
<node>jetson2-oai</node>
<command>sudo ip route add 172.21.6.105 via 13.1.1.100 dev oaitun_ue1</command>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping: 20pings in 20sec</desc>
<id>oai_ue_jetson2</id>
<svr_id>oc-cn5g-00104</svr_id>
<ping_args>-c 20</ping_args>
<ping_packetloss_threshold>1</ping_packetloss_threshold>
<ping_rttavg_threshold>25</ping_rttavg_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/10Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 10M -t 30 -i 1</iperf_args>
<id>oai_ue_jetson2</id>
<svr_id>oc-cn5g-00104</svr_id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/10Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 10M -t 30 -i 1 -R</iperf_args>
<id>oai_ue_jetson2</id>
<svr_id>oc-cn5g-00104</svr_id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/30Mbps/UDP)(30 sec)</desc>
<may_fail>true</may_fail>
<iperf_args>-u -b 30M -t 30 -i 1</iperf_args>
<id>oai_ue_jetson2</id>
<svr_id>oc-cn5g-00104</svr_id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/30Mbps/UDP)(30 sec)</desc>
<may_fail>true</may_fail>
<iperf_args>-u -b 30M -t 30 -i 1 -R</iperf_args>
<id>oai_ue_jetson2</id>
<svr_id>oc-cn5g-00104</svr_id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy nr UE</desc>
<node>jetson2-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_b200_jetson_cuda_nrue</yaml_path>
<analysis>
<services>oai-nr-ue</services>
</analysis>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy gNB</desc>
<node>gracehopper1-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_gnb_aerial_30MHz</yaml_path>
<analysis>
<services>oai-gnb-aerial=RetxCheck=20,100,100,100 oai-gnb-aerial=EndsWithBye</services>
</analysis>
</testCase>
<testCase>
<class>Custom_Script</class>
<always_exec>true</always_exec>
<desc>Configure 100 MHz bandwidth on WNC RU</desc>
<node>gracehopper1-oai</node>
<script>scripts/set-wnc-bandwidth.sh</script>
<args>100</args>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>gracehopper1-oai</node>
<images>oai-gnb-aerial</images>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>jetson2-oai</node>
<images>oai-nr-ue</images>
</testCase>
<testCase>
<class>UndeployCoreNetwork</class>
<always_exec>true</always_exec>
<desc>Terminate 5G Core</desc>
<cn_id>oc-cn5g-00104</cn_id>
</testCase>
</testCaseList>

View File

@@ -27,7 +27,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>ofqot</node>
</testCase>

View File

@@ -33,7 +33,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>cacofonix</node>
</testCase>

View File

@@ -33,7 +33,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>cacofonix</node>
</testCase>

View File

@@ -28,7 +28,7 @@
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<desc>Create new Workspace</desc>
<node>cacofonix</node>
</testCase>

View File

@@ -97,7 +97,7 @@ services:
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.chanmod 1
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.chanmod 1 --vrtsim.num_ues 1
depends_on:
- oai-ext-dn
networks:
@@ -123,7 +123,7 @@ services:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --ue-nb-ant-rx 2 --ue-nb-ant-tx 2
depends_on:
- oai-gnb
networks:

View File

@@ -111,7 +111,7 @@ services:
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.cirdb 1 --vrtsim.cirdb_file /cirdb/cir_db.bin --vrtsim.cirdb_yaml /cirdb/cir_db.yaml --vrtsim.cirdb_model_id 0 --vrtsim.cirdb_ds_ns 1 --vrtsim.cirdb_speed_mps 1.5
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.cirdb 1 --vrtsim.num_ues 1
depends_on:
cir-generator:
condition: service_completed_successfully
@@ -142,7 +142,7 @@ services:
- NET_ADMIN
- NET_RAW
environment:
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client --ue-nb-ant-rx 2 --ue-nb-ant-tx 2
depends_on:
- oai-gnb
networks:

View File

@@ -111,7 +111,7 @@ services:
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.num_ues 2 --vrtsim.cirdb 1 --vrtsim.cirdb_file /cirdb/cir_db.bin --vrtsim.cirdb_yaml /cirdb/cir_db.yaml --vrtsim.ue_config.[0].antennas 1x2 --vrtsim.ue_config.[0].model_id 0 --vrtsim.ue_config.[0].ds_ns 10.0 --vrtsim.ue_config.[0].speed_mps 1.5 --vrtsim.ue_config.[1].antennas 2x2 --vrtsim.ue_config.[1].model_id 1 --vrtsim.ue_config.[1].ds_ns 10.0 --vrtsim.ue_config.[1].speed_mps 1.5 --gNBs.[0].min_rxtxtime 8
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.cirdb 1 --vrtsim.num_ues 2 --gNBs.[0].min_rxtxtime 8
depends_on:
cir-generator:
condition: service_completed_successfully
@@ -142,7 +142,7 @@ services:
- NET_ADMIN
- NET_RAW
environment:
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3319680000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client --vrtsim.ue_id 0 --ue-nb-ant-tx 1 --ue-nb-ant-rx 2
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3319680000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client --vrtsim.ue_id 0 --ue-nb-ant-tx 2 --ue-nb-ant-rx 2
depends_on:
- oai-gnb
networks:
@@ -171,7 +171,7 @@ services:
- NET_ADMIN
- NET_RAW
environment:
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3319680000 --ssb 516 --uicc0.imsi 208990100001101 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client --vrtsim.ue_id 1 --ue-nb-ant-tx 2 --ue-nb-ant-rx 2
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3319680000 --ssb 516 --uicc0.imsi 208990100001101 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client --vrtsim.ue_id 1 --ue-nb-ant-tx 1 --ue-nb-ant-rx 2
depends_on:
- oai-gnb
networks:

View File

@@ -0,0 +1,40 @@
# SPDX-License-Identifier: MIT
services:
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
network_mode: host
runtime: nvidia
container_name: oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
- SYS_NICE # for performance
- IPC_LOCK # for memory locking
devices:
- /dev/net/tun:/dev/net/tun
- /dev/bus/usb/:/dev/bus/usb/
ulimits:
core: -1 # for core dumps
environment:
TZ: Europe/Paris
USE_B2XX: 'yes'
USE_ADDITIONAL_OPTIONS: --usrp-args type=b200 -C 3350010000 -r 78 --numerology 1 --band 78 --ssb 345 --ue-fo-compensation --uicc0.imsi 001040000000001 --uicc0.pdu_sessions.[0].dnn oai.ipv4 --thread-pool 5,6,7,8,9 --ue-txgain 15 --ue-rxgain 90 --loader.ldpc.shlibversion _cuda -A 60
cpuset: "3-11"
volumes:
- ../../conf_files/ue.sa.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]

View File

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

View File

@@ -88,3 +88,85 @@ function(run_asn1c ASN1C_GRAMMAR ASN1C_PREFIX)
COMMENT "Generating ${ASN1C_COMMENT} from ${GRAMMAR_FILE}"
)
endfunction()
define_property(TEST PROPERTY TEST_DESCRIPTION
BRIEF_DOCS "A human-readable description of this test"
FULL_DOCS "A human-readable description of this test")
define_property(TEST PROPERTY CHECK_COUNT
BRIEF_DOCS "helper property to enumerate checks in environment"
FULL_DOCS "the property counts the number of threshold checks, used to enumerate environment variables given to analyze-timing.sh")
function(add_physim_test test_name test_description test_exec)
# catch all the arguments past the last expected arqument and store them in the options_list
if (NOT TARGET ${test_exec})
message(FATAL_ERROR "test executable ${test_exec} is not an executable")
endif()
set(test_invocation $<TARGET_FILE:${test_exec}> ${ARGN})
add_test(
NAME ${test_name}
COMMAND ${CMAKE_COMMAND}
"-DTEST_CMD=${test_invocation}"
"-DCHECK_SCRIPT=${CMAKE_SOURCE_DIR}/openair1/SIMULATION/tests/analyze-timing.sh"
-P ${CMAKE_SOURCE_DIR}/openair1/SIMULATION/tests/RunTimedTest.cmake
)
set_tests_properties(${test_name} PROPERTIES
LABELS "${test_exec}"
TEST_DESCRIPTION "${test_description}"
# pass test description also through environment variable: for cmake < 3.30,
# in JSON export, we cannot recover the description otherwise
# see also https://gitlab.kitware.com/cmake/cmake/-/issues/21490
ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR};TEST_DESCRIPTION=${test_description}"
)
set_tests_properties(${test_name} PROPERTIES CHECK_COUNT 0)
add_dependencies(tests ${test_exec})
endfunction()
function(check_threshold testname threshold condition)
# check that threshold and condition don't have a colon (;), because that
# would interfere with cmake's list management
string(FIND "${threshold}" ";" pos)
if (pos GREATER -1)
message(FATAL_ERROR "colon not allowed in threshold, but have \"${threshold}\"")
endif()
string(FIND "${condition}" ";" pos)
if (pos GREATER -1)
message(FATAL_ERROR "colon not allowed in condition, but have \"${condition}\"")
endif()
set(THRCOND "${threshold}\;${condition}")
get_test_property(${testname} CHECK_COUNT count)
#message(STATUS "add check ${count} ${THRCOND}")
if (${count} GREATER 10)
message(FATAL_ERROR "only maximum of 10 checks per test allowed")
endif()
# add a new environment variable CHECK_X with this threshold+condition, then
# increase test property regarding check count
set_property(TEST ${testname} APPEND PROPERTY ENVIRONMENT "CHECK_${count}=${THRCOND}")
MATH(EXPR count "${count}+1")
set_tests_properties(${testname} PROPERTIES CHECK_COUNT ${count})
endfunction()
function(check_threshold_range testname threshold)
cmake_parse_arguments(RANGE "" "LOWER;UPPER" "" ${ARGN})
if (NOT RANGE_LOWER AND NOT RANGE_UPPER)
message(FATAL_ERROR "need at least one LOWER or one UPPER threshold")
endif()
if (RANGE_LOWER)
check_threshold(${testname} ${threshold} "> ${RANGE_LOWER}")
endif()
if (RANGE_UPPER)
check_threshold(${testname} ${threshold} "< ${RANGE_UPPER}")
endif()
endfunction()
function(check_threshold_variance testname threshold)
cmake_parse_arguments(VARIANCE "" "AVG;ABS_VAR" "" ${ARGN})
if (NOT VARIANCE_AVG AND NOT VARIANCE_ABS_VAR)
message(FATAL_ERROR "need both AVG and ABS_VAR")
endif()
MATH(EXPR upper "${VARIANCE_AVG}+${VARIANCE_ABS_VAR}")
MATH(EXPR lower "${VARIANCE_AVG}-${VARIANCE_ABS_VAR}")
check_threshold_range(${testname} ${threshold} LOWER ${lower} UPPER ${upper})
endfunction()

View File

@@ -83,11 +83,13 @@ get_distribution_release() {
check_supported_distribution() {
local distribution=$(get_distribution_release)
case "$distribution" in
"ubuntu26.04") return 0 ;;
"ubuntu24.04") return 0 ;;
"ubuntu22.04") return 0 ;;
"debian11") return 0 ;;
"debian12") return 0 ;;
"fedora43") return 0 ;;
"fedora44") return 0 ;;
"rhel9.0") return 0 ;;
"rhel9.1") return 0 ;;
"rhel9.2") return 0 ;;
@@ -96,6 +98,7 @@ check_supported_distribution() {
"rhel9.5") return 0 ;;
"rhel9.6") return 0 ;;
"rhel9.7") return 0 ;;
"rhel9.8") return 0 ;;
"centos9") return 0 ;; # CentOS stream
"rocky9.1") return 0 ;;
"rocky9.2") return 0 ;;
@@ -104,6 +107,7 @@ check_supported_distribution() {
"rocky9.5") return 0 ;;
"rocky9.6") return 0 ;;
"rocky9.7") return 0 ;;
"rocky9.8") return 0 ;;
esac
return 1
}
@@ -141,12 +145,8 @@ install_usrp_uhd_driver_from_source(){
# - 3.15.0.0
git apply $OPENAIR_DIR/cmake_targets/tools/uhd-3.15-tdd-patch.diff
ret=$?;[[ $ret -ne 0 ]] && echo_fatal "Could not apply the TDD patch"
elif [[ "$UHD_VERSION" == "4.8.0.0" || "$UHD_VERSION" == "4.7.0.0" || "$UHD_VERSION" == "4.6.0.0" || "$UHD_VERSION" == "4.5.0.0" ]]; then
# Tested that patch for the following versions:
# - 4.7.0.0
cp $OPENAIR_DIR/cmake_targets/tools/uhd-4.x-tdd-patch.diff $OPENAIR_DIR/cmake_targets/tools/uhd-4.5plus-tdd-patch.diff
sed -i '9,13 s/STATE_OFF/STATE_RX1_OFF/' $OPENAIR_DIR/cmake_targets/tools/uhd-4.5plus-tdd-patch.diff
sed -i '24,28 s/STATE_OFF/STATE_RX2_OFF/' $OPENAIR_DIR/cmake_targets/tools/uhd-4.5plus-tdd-patch.diff
elif [[ "$UHD_VERSION" == "4.10.0.0" || "$UHD_VERSION" == "4.9.0.0" || "$UHD_VERSION" == "4.8.0.0" || "$UHD_VERSION" == "4.7.0.0" || "$UHD_VERSION" == "4.6.0.0" || "$UHD_VERSION" == "4.5.0.0" ]]; then
# Tested patch for versions listed in elif clause
git apply $OPENAIR_DIR/cmake_targets/tools/uhd-4.5plus-tdd-patch.diff
ret=$?;[[ $ret -ne 0 ]] && echo_fatal "Could not apply the TDD patch"
else
@@ -162,8 +162,8 @@ install_usrp_uhd_driver_from_source(){
ret=$?;[[ $ret -ne 0 ]] && echo_fatal "Could not apply the TDD patch"
fi
else
git checkout tags/v4.0.0.0
git apply $OPENAIR_DIR/cmake_targets/tools/uhd-4.x-tdd-patch.diff
git checkout tags/v4.8.0.0
git apply $OPENAIR_DIR/cmake_targets/tools/uhd-4.5plus-tdd-patch.diff
ret=$?;[[ $ret -ne 0 ]] && echo_fatal "Could not apply the TDD patch"
fi
# Printing out the results of the patch to make sure it was properly applied
@@ -199,29 +199,14 @@ check_install_usrp_uhd_driver(){
install_usrp_uhd_driver_from_source
return
fi
# The new USRP repository
# Raphael Defosseux: Adding a loop on adding PPA because in CI the gpg key retrieve may
# timeout due to proxy / network latencies in Eurecom on VM
echo_info "\nAdding PPA repository ettusresearch/uhd\n"
x=0
while [ $x -le 5 ]
do
if $SUDO add-apt-repository ppa:ettusresearch/uhd -y
then
echo_info "add-apt-repository successful\n"
break
else
echo_info "add-apt-repository failed, retrying...\n"
sleep 30
fi
x=$((x + 1))
done
$SUDO add-apt-repository ppa:ettusresearch/uhd -y
$SUDO apt-get update
$SUDO apt-get -y install python3-tk $boost_libs_ubuntu libusb-1.0-0-dev
case "$(get_distribution_release)" in
"ubuntu22.04")
$SUDO apt-get -y install libuhd-dev libuhd4.5.0 uhd-host
;;
"ubuntu22.04") $SUDO apt-get -y install libuhd-dev libuhd4.5.0 uhd-host ;;
"ubuntu24.04") $SUDO apt-get -y install libuhd-dev libuhd4.8.0 uhd-host ;;
"ubuntu26.04") $SUDO apt-get -y install libuhd-dev libuhd4.9.0 uhd-host ;;
esac
elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then
boost_libs_fedora="boost-chrono boost-date-time boost-filesystem boost-program-options boost-thread boost-test boost-regex boost-devel"

View File

@@ -0,0 +1,34 @@
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
index 6de161e87..432af5784 100644
--- a/host/lib/usrp/b200/b200_impl.cpp
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -1410,10 +1410,10 @@ void b200_impl::update_atrs(void)
if (not enb_rx and enb_tx)
fd = txonly;
gpio_atr_3000::sptr atr = perif.atr;
- atr->set_atr_reg(ATR_REG_IDLE, STATE_RX1_OFF);
- atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly);
- atr->set_atr_reg(ATR_REG_TX_ONLY, txonly);
- atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);
+ atr->set_atr_reg(ATR_REG_IDLE, STATE_RX1_OFF | (1<<7));
+ atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly | (1<<7));
+ atr->set_atr_reg(ATR_REG_TX_ONLY, txonly | (1<<7));
+ atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd | (1<<7));
}
if (_radio_perifs.size() > _fe2 and _radio_perifs[_fe2].atr) {
radio_perifs_t& perif = _radio_perifs[_fe2];
@@ -1431,10 +1431,10 @@ void b200_impl::update_atrs(void)
if (not enb_rx and enb_tx)
fd = txonly;
gpio_atr_3000::sptr atr = perif.atr;
- atr->set_atr_reg(ATR_REG_IDLE, STATE_RX2_OFF);
- atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly);
- atr->set_atr_reg(ATR_REG_TX_ONLY, txonly);
- atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);
+ atr->set_atr_reg(ATR_REG_IDLE, STATE_RX2_OFF | (1<<7));
+ atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly | (1<<7));
+ atr->set_atr_reg(ATR_REG_TX_ONLY, txonly | (1<<7));
+ atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd | (1<<7));
}
}

View File

@@ -17,7 +17,6 @@
# define NUMBER_OF_SCH_STATS_MAX 16
# ifndef PHYSIM
# ifndef UE_EXPANSION
# define NUMBER_OF_UE_MAX 40
# define NUMBER_OF_CONNECTED_eNB_MAX 1
@@ -27,10 +26,5 @@
# define NUMBER_OF_CONNECTED_eNB_MAX 1
# define NUMBER_OF_CONNECTED_gNB_MAX 1
# endif
# else
# define NUMBER_OF_UE_MAX 4
# define NUMBER_OF_CONNECTED_eNB_MAX 1
# define NUMBER_OF_CONNECTED_gNB_MAX 1
# endif
#endif /* OPENAIRINTERFACE5G_LIMITS_H_ */

View File

@@ -1454,7 +1454,7 @@ ID = UE_PHY_PDCCH_ENERGY
ID = UE_PHY_PDSCH_IQ
DESC = UE PDSCH received IQ data
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
FORMAT = int,eNB_ID : int,frame : int,subframe : int,nb_rb : int,N_RB_UL : int,symbols_per_tti : buffer,pdsch_comp
FORMAT = int,eNB_ID : int,frame : int,subframe : int,nb_rb : int,N_RB_DL : int,symbols_per_tti : buffer,pdsch_comp
ID = UE_PHY_PDSCH_ENERGY
DESC = UE PDSCH 1 energy and threshold
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE

View File

@@ -229,8 +229,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database,
e->pdsch_iq_ue_xy_plot = w;
widget_add_child(g, line, w, -1);
xy_plot_set_range(g, w, -500, 500, -500, 500);
l = new_iqlog(h, database, "UE_PHY_PDSCH_IQ", "nb_rb",
"N_RB_UL", "symbols_per_tti", "pusch_comp");
l = new_iqlog(h, database, "UE_PHY_PDSCH_IQ", "nb_rb", "N_RB_DL", "symbols_per_tti", "pdsch_comp");
v = new_view_xy(100*12*14,10,g,w,new_color(g,"#000"),XY_FORCED_MODE);
logger_add_view(l, v);
e->pdsch_iq_ue_logger = l;

View File

@@ -71,7 +71,9 @@ static inline const char *rnti_types(nr_rnti_type_t rr)
#define MAX_GSCN_BAND 620 // n78 has the highest GSCN range of 619
#define NR_SYMBOLS_PER_SLOT 14
#define NR_SYMBOLS_PER_SLOT_EXTENDED_CP 12
#define NR_MAX_NB_LAYERS 4 // 8
#define NR_MAX_NB_LAYERS 4
#define MAX_NUM_NR_DLSCH_SEGMENTS (MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * NR_MAX_NB_LAYERS)
#define MAX_NUM_NR_ULSCH_SEGMENTS (MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * NR_MAX_NB_LAYERS)
#define NR_MAX_CSI_PORTS 12
// Since the IQ samples are represented by SQ15 R+I (see https://en.wikipedia.org/wiki/Q_(number_format)) we need to compensate when

View File

@@ -62,7 +62,7 @@ extern "C" {
#endif
#ifndef malloc16
# define malloc16(x) memalign(32,x+32)
#define malloc16(x) memalign(64, x + 64)
#endif
#define free16(y,x) free(y)
#define openair_free(y,x) free((y))
@@ -76,7 +76,7 @@ extern "C" {
} while (0)
static inline void *malloc16_clear( size_t size ) {
void *ptr = memalign(32, size+32);
void *ptr = memalign(64, size + 64);
DevAssert(ptr);
memset( ptr, 0, size );
return ptr;
@@ -110,7 +110,7 @@ static inline void *malloc_or_fail(size_t size)
# define msg(aRGS...) LOG_D(PHY, ##aRGS)
#endif
#ifndef malloc16
# define malloc16(x) memalign(32,x)
#define malloc16(x) memalign(64, x)
#endif
#define free16(y,x) free(y)

View File

@@ -192,7 +192,7 @@ Now, when installing the pre-requisites, especially the `UHD` driver, you can no
```bash
export BUILD_UHD_FROM_SOURCE=True
export UHD_VERSION=3.15.0.0
export UHD_VERSION=4.10.0.0
./build_oai -I -w USRP
```
@@ -206,6 +206,7 @@ See:
* `cmake_targets/tools/uhd-3.15-tdd-patch.diff`
* `cmake_targets/tools/uhd-4.x-tdd-patch.diff`
* `cmake_targets/tools/uhd-4.5plus-tdd-patch.diff`
* `cmake_targets/tools/build_helper` --> function `install_usrp_uhd_driver_from_source`
### Building PHY Simulators

View File

@@ -10,7 +10,9 @@ out the various configuration options that influence its behavior.
The 5G MAC scheduler is a proportional fair (PF) scheduler, "approximating
wide-band CQI" (for lack of a better term, but CQI is typically used for PF)
through the selection of an MCS to use.
through the selection of an MCS to use. For a detailed description of the
scheduler pipeline and how to replace individual policies, see
[Scheduler Architecture](scheduler-architecture.md).
Concretely, the scheduler loops through all UEs and calculates the PF
coefficient using the currently selected MCS, and the historical achieved rate.
@@ -22,9 +24,13 @@ UEs with retransmissions are allocated first; similarly, UEs that have not been
scheduled for some time in UL are scheduled automatically in UL and have
therefore priority over data with "normal" traffic.
The MCS selection is done in `get_mcs_from_bler()` in file
[`gNB_scheduler_primitives.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c).
It consider two thresholds for a "BLER" that is computed from the number of
The MCS selection is done in `nr_dl_mcs_select_default()` / `nr_ul_mcs_select_default()` in files
[`gNB_scheduler_dlsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch_default_policies.c)
and
[`gNB_scheduler_ulsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch_default_policies.c).
The BLER estimation itself is computed separately in `update_bler_stats()` in
[`gNB_scheduler_primitives.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c),
and the MCS policy reads the result. It considers two thresholds for a "BLER" that is computed from the number of
first-round retransmissions over total transmissions in the last window (50ms).
If that ratio is higher than an "upper" threshold (see
`dl/ul_bler_target_upper` in the configuration section below), it is
@@ -33,11 +39,11 @@ lower than a "lower" threshold (see `dl/ul_bler_target_lower`), it is
interpreted as "good channel" and MCS is incremented by 1. This happens each
window.
The actual scheduler implementation can be found in functions `pf_dl()` and
`pf_ul()` in files
[`gNB_scheduler_dlsch.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c)
The actual scheduler implementation can be found in functions `nr_dl_proportional_fair()` and
`nr_ul_proportional_fair()` in files
[`gNB_scheduler_dlsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch_default_policies.c)
(for DL) and
[`gNB_scheduler_ulsch.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c)
[`gNB_scheduler_ulsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch_default_policies.c)
(for UL), respectively.
## PDCCH aggregation level
@@ -120,9 +126,8 @@ Example:
UE RNTI 2460 CU-UE-ID 2 in-sync PH 28 dB PCMAX 24 dBm, average RSRP -74 (8 meas), average SINR 40.0 (32 meas)
UE 2460: CQI 15, RI 2, PMI (14,1)
UE 2460: UL-RI 2 TPMI 0
UE 2460: dlsch_rounds 32917/5113/1504/560, dlsch_errors 211, pucch0_DTX 1385 (SNR 19.8+0.2 dB), BLER 0.19557 MCS (1) 23 CCE fail 3
UE 2460: ulsch_rounds 3756/353/182/179, ulsch_errors 170, ulsch_DTX 285, BLER 0.33021 MCS (1) 27 (Qm 8 dB) NPRB 5 SNR 31.0 (-1.0) dB CCE fail 0
UE 2460: MAC: TX 1530943191 RX 194148 bytes
UE 2460: dlsch_rounds 32917/5113/1504/560, dlsch_errors 211, pucch0_DTX 1385 (SNR 19.8+0.2 dB), BLER 0.19557 MCS (1) 23 CCE fail 3, goodput 120.50 Mbps
UE 2460: ulsch_rounds 3756/353/182/179, ulsch_errors 170, ulsch_DTX 285, BLER 0.33021 MCS (1) 27 (Qm 8 deltaMCS 0 dB) NPRB 5 SNR 31.0 (-1.0) dB CCE fail 0, goodput 12.30 Mbps
UE 2460: LCID 1: TX 651 RX 3031 bytes
UE 2460: LCID 2: TX 0 RX 0 bytes
UE 2460: LCID 4: TX 1526169592 RX 16152 bytes
@@ -209,11 +214,11 @@ The fourth and fifth line show HARQ-related information:
* Both ULSCH/DLSCH `CCE fail`: lists the number of failed CCE attempts. If this
number gets high, it signifies that the scheduler tried to scheduled this UE,
but could not allocate the DCI.
* Both ULSCH/DLSCH `goodput`: smoothed (EWMA) goodput in Mbps, reflecting the
actual MAC-layer throughput achieved by the UE.
In the last lines:
* `MAC` shows the amount of MAC PDU bytes scheduled in transmit (`TX`,
`1530943191`) and receive (`RX`, `194148`) directions
* `LCID X` shows the amount of MAC SDU/RLC PDU data for Logical Channel ID with
ID `X` in transmit and receive directions. LCIDs 1 and 2 are mapped to SRBs 1
and 2. LCIDs 4 and onward are mapped to DRBs 1 onward. If you have an LCID 4,

View File

@@ -0,0 +1,433 @@
# gNB MAC Scheduler Architecture
## Overview
The gNB MAC scheduler runs once per DL slot. It allocates PDSCH resources for the
current slot and PUSCH resources for a future UL slot (selected via K2), sending the
UL DCI grant in the current DL slot. The DL and UL schedulers follow the same pipeline
design: a fixed orchestration loop calls a sequence of **pluggable function pointers**,
each handling one stage of the scheduling decision. Any stage can be replaced
independently without touching the rest of the pipeline.
Both schedulers send their DCIs in the same DL slot and share the same physical
CORESET/CCE resources. The UL scheduler runs first (`nr_schedule_ulsch` in
`gNB_scheduler.c`), then the DL scheduler (`nr_schedule_ue_spec`). CCEs are claimed on
a first-come basis via `get_cce_index` + `fill_pdcch_vrb_map`, so UL DCIs take
priority. If CCE space is tight, DL UEs are more likely to be skipped. The two
schedulers use separate FAPI containers (`UL_dci_req` for UL grants, `DL_req` for DL
grants) but the underlying CCE map is shared.
All scheduler code lives under `openair2/LAYER2/NR_MAC_gNB/`. The DL scheduler is in
[`gNB_scheduler_dlsch.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c),
the UL scheduler in
[`gNB_scheduler_ulsch.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c),
and shared primitives in
[`gNB_scheduler_primitives.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c).
Data structures and typedefs are in
[`nr_mac_gNB.h`](../../openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h).
Default policy implementations are in
[`gNB_scheduler_dlsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch_default_policies.c)
and
[`gNB_scheduler_ulsch_default_policies.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch_default_policies.c).
Function pointers are assigned at startup in
[`main.c`](../../openair2/LAYER2/NR_MAC_gNB/main.c).
## Scheduler Pipeline
Both DL and UL follow the same 7-stage pipeline, plus a DL-only 8th stage.
```
DL UL
┌────────────┐ ┌────────────┐
│ 1.Collect │ │ 1.Collect │ ◄── nr_ulsch_preprocessor
│ Candidates │ │ Candidates │ (iterates over
└─────┬──────┘ └─────┬──────┘ reachable UL slots)
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ 2. RI/PMI │ │ 2. RI/TPMI│ ◄─┐
│ Selection │ │ Selection │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │
┌─────┴──────┐ ┌─────┴──────┐ │
│ 3. Beam │ │ 3. Beam │ │
│ Selection │ │ Selection │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │ nr_ul_schedule
┌─────┴──────┐ ┌─────┴──────┐ │ (per UL slot)
│ 4. TDA │ │ 4. TDA │ │
│ Selection │ │ Selection │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │
┌─────┴──────┐ ┌─────┴──────┐ │
│ 5. MCS │ │ 5. MCS │ │
│ Selection │ │ Selection │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │
┌─────┴──────┐ ┌─────┴──────┐ │
│ 6. RB │ │ 6. RB │ │
│ Allocation │ │ Allocation │ │
│ (per beam) │ │ (per beam) │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │
┌─────┴──────┐ ┌─────┴──────┐ │
│ 7.Dispatch│ │ 7.Dispatch│ ◄─┘
│ (fixed) │ │ (fixed) │
└─────┬──────┘ └────────────┘
┌─────┴──────┐
│ 8. LCID │ (DL only)
│ Allocation │
└────────────┘
```
### UL preprocessor split
The UL pipeline is split across two functions. The preprocessor
(`nr_ulsch_preprocessor`) runs a loop over **multiple candidate UL slots**: for each DL
slot there may be several reachable UL slots (depending on K2), and the scheduler must
pick one. The loop iterates until the DCI budget is exhausted or no TDA can reach
further UL slots.
**Only step 1 (candidate collection) lives in the preprocessor loop.** Candidates depend
on `sched_frame`/`sched_slot` (e.g. inactivity checks), so they are rebuilt each
iteration. The preprocessor also runs a lightweight TDA reachability gate
(`get_num_ul_tda == 0 → break`) to short-circuit the loop when no TDA can reach the
target slot.
**Steps 27 live in `nr_ul_schedule`**, called once per UL slot with the collected
candidates. `k2` is passed as a parameter for TDA selection. This mirrors the DL
structure where all stages run in a single function.
Beam selection (step 3) runs before TDA selection (step 4) so that TDA selection can
use the allocated beam to check the correct VRB map and pick the best TDA per beam.
The preprocessor itself is a pluggable function pointer (`pre_processor_ul`), so a
custom implementation can replace the entire outer strategy — including how many UL
slots to target and what to do when the DCI budget is limited.
### Candidate struct
Each candidate is an `nr_{dl,ul}_candidate_t` struct that flows through the pipeline,
accumulating decisions. Its fields are split into three sections:
1. **Identity / scheduling state** — set by collect, read-only afterwards: `UE` pointer,
`rnti`, HARQ state, pending bytes, BLER estimate, current MCS, BWP geometry, QoS.
2. **CSI observations** — set by collect: `cqi` (DL), `beam_rsrp[]`, `beam_sinr[]` (per-SSB).
3. **gNB decisions** — written by the stage indicated below. Some live in the
embedded `sched_pdsch` / `sched_pusch` sub-struct, others are `alloc_`-prefixed
fields on the candidate itself:
- `sched_p{d,u}sch.nrOfLayers`, `.pm_index` (DL) / `.tpmi` (UL) — set by ri_pmi/tpmi_select
- `sched_p{d,u}sch.time_domain_allocation`, `.tda_info`, `alloc_slbitmap` — set by tda_select
- `alloc_beam_idx`, `alloc_dci_beam_idx` (UL), etc. — set by beam_select
- `sched_p{d,u}sch.mcs` — set by mcs_select
- `sched_p{d,u}sch.rbStart`, `.rbSize` — set by rb_alloc
- `alloc_cce_index`, `alloc_aggregation_level`, `alloc_sched_pdcch` — set by commit_alloc
The policy (`rb_alloc`) writes to `sched_p{d,u}sch.rbStart/rbSize/mcs` via the
`COMMIT_ALLOC` / `COMMIT_UL_ALLOC` macro, which also runs CCE validation and sets
`cand->scheduled = true`. Only candidates with that flag set are dispatched.
---
### Stage 1. Candidate Collection (fixed)
**Functions:** `collect_dl_candidates()`, `collect_ul_candidates()`
Iterates over connected UEs and builds an array of `nr_{dl,ul}_candidate_t` structs.
Populates the identity/state and CSI sections. Not a function pointer — fixed scaffolding
that feeds the pipeline.
### Stage 2. RI / PMI Selection
**Function pointers:** `mac->dl_ri_pmi_select` (`nr_dl_ri_pmi_select_fn`),
`mac->ul_ri_tpmi_select` (`nr_ul_ri_tpmi_select_fn`)
Sets rank and precoder index per candidate. In DL, rank and PMI come from the UE's CSI
report (RI + PMI). In UL, the gNB has full authority and reads rank and TPMI from SRS
feedback.
**DL outputs:** `cand->sched_pdsch.nrOfLayers`, `cand->sched_pdsch.pm_index`.
**UL outputs:** `cand->sched_pusch.nrOfLayers`, `cand->sched_pusch.tpmi`.
### Stage 3. Beam Selection
**Function pointers:** `mac->dl_beam_select` (`nr_dl_beam_select_fn`),
`mac->ul_beam_select` (`nr_ul_beam_select_fn`)
Assigns a beam structure index to each candidate. For UL, two beams are needed — one
for the DCI slot, one for the PUSCH slot — since they may fall in different beam periods.
Candidates that fail beam allocation (no beam available) are marked with `skipped=true`.
Placed before TDA selection so that TDA selection can use the allocated beam to check
the correct VRB map and pick the best TDA per beam.
**Outputs:** `cand->alloc_beam_idx` (PUSCH/PDSCH beam), `cand->alloc_dci_beam_idx` (UL DCI beam).
### Stage 4. TDA Selection
**Function pointers:** `mac->dl_tda_select` (`nr_dl_tda_select_fn`),
`mac->ul_tda_select` (`nr_ul_tda_select_fn`)
Picks the Time Domain Allocation for each candidate. For UL retransmissions, also
validates feasibility: if the TDA differs from the original transmission (e.g. because
targeting a different UL slot), the TBS must be preservable with the new symbol/DMRS
layout. Infeasible retx candidates are marked with `skipped=true`.
**Outputs:** `cand->sched_p{d,u}sch.time_domain_allocation`, `.tda_info`, `cand->alloc_slbitmap`. UL retx
candidates get `cand->retx_rbSize` set.
**Returns:** number of surviving candidates.
### Stage 5. MCS Selection
**Function pointers:** `mac->dl_mcs_select` (`nr_dl_mcs_select_fn`),
`mac->ul_mcs_select` (`nr_ul_mcs_select_fn`)
Runs for **all surviving candidates** — including those that may ultimately not be
scheduled due to CCE failure. Sets `sched_p{d,u}sch.mcs` from BLER stats or SINR, and persists
the decision to `{ul,dl}_bler_stats.mcs` so continuity is maintained across slots even
for unscheduled UEs.
Placed after beam selection so custom implementations can use `alloc_beam_idx` and
`beam_rsrp/sinr` for beam-aware MCS adaptation.
### Stage 6. RB Allocation (per beam)
**Function pointers:** `mac->dl_rb_alloc` (`nr_dl_rb_alloc_fn`),
`mac->ul_rb_alloc` (`nr_ul_rb_alloc_fn`)
The core resource allocation decision and the most likely stage to be replaced. Called
once per beam with candidates filtered to that beam. Decides which UEs to schedule and
how many PRBs each gets. MCS is already set on `sched_p{d,u}sch.mcs` by the previous
stage; the policy may refine it (e.g. downward PHR adjustment).
Each allocation must go through the `COMMIT_ALLOC` / `COMMIT_UL_ALLOC` macro, which
writes `sched_p{d,u}sch.rbStart/rbSize/mcs` onto the candidate, runs CCE validation
(and PUCCH validation on DL), and sets `cand->scheduled = true`. UEs that fail CCE are
skipped transparently.
**Outputs:** `cand->scheduled` flag on accepted candidates.
**Returns:** number of scheduled UEs.
### Stage 7. Dispatch (fixed)
Not a function pointer. Iterates over candidates with `scheduled == true`, reads the `alloc_*` fields
from each candidate, applies CCE results to `sched_ctrl`, marks the VRB map, and calls
`apply_{dl,ul}_{new_transmission,retransmission}` to build the final
`NR_sched_{pusch,pdsch}_t` and trigger FAPI message preparation.
### Stage 8. Per-LCID Byte Allocation (DL only)
**Function pointer:** `mac->dl_lcid_alloc` (`nr_dl_lcid_alloc_fn`)
Called inside `generate_dl_mac_pdu` for initial DL transmissions. Decides how many bytes
each logical channel gets within the available TBS. The candidate flows from the dispatch
stage into `post_process_dlsch``generate_dl_mac_pdu`, giving this stage access to the
full candidate context (per-LCID pending bytes, QoS, priority).
The function pointer receives the MAC instance, the candidate, the TBS available after
MAC CEs, and writes an output array `lcid_alloc[NR_MAX_NUM_LCID]` specifying the byte
budget per LCID. The MAC PDU builder then executes the plan: it iterates over LCs in
config order, requests up to `lcid_alloc[lcid]` bytes from RLC for each, and builds the
MAC subheaders. RLC may return fewer bytes than budgeted; the builder handles this
transparently.
**Inputs:** `candidate->pending_bytes_per_lcid[]`, `candidate->fiveQI`, `candidate->priority`,
`candidate->nssai`, `tbs_available`.
**Outputs:** `lcid_alloc[NR_MAX_NUM_LCID]` — byte budget per LCID.
Not invoked for retransmissions (which reuse the stored transport block) or for phy-test
mode (which fills random data). In these cases the candidate pointer is NULL.
---
## Default Implementations
All function pointers are assigned in
[`main.c`](../../openair2/LAYER2/NR_MAC_gNB/main.c) at startup.
The `COMMIT_ALLOC` / `COMMIT_UL_ALLOC` macros are defined in
[`mac_proto.h`](../../openair2/LAYER2/NR_MAC_gNB/mac_proto.h).
### `nr_dl_ri_pmi_select_default`, `nr_ul_ri_tpmi_select_default`
DL reads rank and PMI from the UE's CSI report (`csi_ri`, `csi_pm_index` on
`sched_ctrl`). Retransmissions reuse rank/PMI from the original HARQ process.
UL reads rank and TPMI from SRS feedback (`srs_feedback` on `sched_ctrl`).
Both write `cand->sched_p{d,u}sch.nrOfLayers` and the respective precoder index.
### `nr_dl_tda_select_default`, `nr_ul_tda_select_default`
Assigns the same TDA to all candidates in a slot (picks the first valid one for the
scheduled frame/slot). For UL retransmissions, calls `check_ul_retx_feasibility` to
verify TBS preservation under the new symbol/DMRS layout.
### `nr_dl_beam_select_default`, `nr_ul_beam_select_default`
Uses OAI's beam management framework (`NR_beam_info_t`). For UL, allocates both a DCI
beam and a PUSCH beam. Candidates that fail beam allocation are compacted out.
### `nr_dl_mcs_select_default`, `nr_ul_mcs_select_default`
BLER-based (when `harq_round_max > 1`): calls `nr_adapt_mcs_from_bler` for candidates
with a fresh BLER estimate; holds current MCS otherwise. SINR-based (when
`harq_round_max == 1`): maps measured PUSCH SINR to MCS via lookup table. In both
cases, persists to `{dl,ul}_bler_stats.mcs`.
### `nr_dl_proportional_fair`, `nr_ul_proportional_fair`
Proportional-fair scheduler with three phases:
- **Phase 1 — Retransmissions:** find the largest free block >= `retx_rbSize`.
- **Phase 2 — Minimal-grant UEs:** in DL, targets UEs with no pending RLC data
(`pending_bytes == 0`) that still need a TA command or beam-switch MAC CE. In UL,
targets inactive UEs (`sched_inactive`) that need scheduling for TA/SR. Both get a
minimum grant (`min_rb`).
- **Phase 3 — New data:** sort by PF weight (`pending_bytes / avg_throughput`), allocate
the largest free block to each UE in order.
The UL policy also checks PHR (Power Headroom) and adjusts MCS/RBs accordingly.
All phases use `COMMIT_ALLOC` / `COMMIT_UL_ALLOC` to validate CCE (and PUCCH on DL).
### `nr_dl_lcid_alloc_default`
Greedy fill: sets each LCID's budget to its full `pending_bytes_per_lcid` value. The
execution loop in `generate_dl_mac_pdu` then fills LCIDs in LC config order until the
TBS is exhausted, reproducing the original first-come-first-served behavior. A custom
implementation could use the candidate's QoS fields (`fiveQI`, `priority`, `nssai`)
to implement weighted fair queuing, strict priority with rate limiting, or slice-aware
allocation across LCIDs.
### `nr_dlsch_preprocessor`, `nr_ulsch_preprocessor`
Top-level orchestrators that run the full pipeline above. In phy-test mode, replaced by
`nr_preprocessor_phytest` / `nr_ul_preprocessor_phytest` which bypass the staged pipeline.
---
## Function Pointer Reference
### DL
| Field on `gNB_MAC_INST` | Typedef | Default | Stage |
|--------------------------|---------------------------|-------------------------------|-------|
| `pre_processor_dl` | `nr_pp_impl_dl` | `nr_dlsch_preprocessor` | Orchestrator |
| `dl_ri_pmi_select` | `nr_dl_ri_pmi_select_fn` | `nr_dl_ri_pmi_select_default` | 2. RI/PMI |
| `dl_beam_select` | `nr_dl_beam_select_fn` | `nr_dl_beam_select_default` | 3. Beam |
| `dl_tda_select` | `nr_dl_tda_select_fn` | `nr_dl_tda_select_default` | 4. TDA |
| `dl_mcs_select` | `nr_dl_mcs_select_fn` | `nr_dl_mcs_select_default` | 5. MCS |
| `dl_rb_alloc` | `nr_dl_rb_alloc_fn` | `nr_dl_proportional_fair` | 6. RB alloc |
| `dl_lcid_alloc` | `nr_dl_lcid_alloc_fn` | `nr_dl_lcid_alloc_default` | 8. LCID alloc |
### UL
| Field on `gNB_MAC_INST` | Typedef | Default | Stage |
|--------------------------|---------------------------|--------------------------------|-------|
| `pre_processor_ul` | `nr_pp_impl_ul` | `nr_ulsch_preprocessor` | Orchestrator |
| `ul_ri_tpmi_select` | `nr_ul_ri_tpmi_select_fn` | `nr_ul_ri_tpmi_select_default` | 2. RI/TPMI |
| `ul_beam_select` | `nr_ul_beam_select_fn` | `nr_ul_beam_select_default` | 3. Beam |
| `ul_tda_select` | `nr_ul_tda_select_fn` | `nr_ul_tda_select_default` | 4. TDA |
| `ul_mcs_select` | `nr_ul_mcs_select_fn` | `nr_ul_mcs_select_default` | 5. MCS |
| `ul_rb_alloc` | `nr_ul_rb_alloc_fn` | `nr_ul_proportional_fair` | 6. RB alloc |
---
## Key Data Structures
All defined in `nr_mac_gNB.h`.
### Candidate (`nr_{dl,ul}_candidate_t`)
Flows through the pipeline accumulating decisions. Three sections:
**Identity / scheduling state** (set by collect, read-only after):
`UE` pointer, `rnti`, `is_retx`, `retx_harq_pid`, `retx_rbSize`, `pending_bytes`,
`pending_bytes_per_lcid[NR_MAX_NUM_LCID]`, `avg_throughput`, `bler`, `current_mcs`,
`max_mcs`, `bwp_start`, `bwp_size`, `fiveQI`, `priority`, `nssai`.
**CSI observations** (set by collect, read-only after):
`cqi` (DL CQI), `beam_rsrp[]`, `beam_sinr[]` (per-SSB L1-RSRP/SINR, `INT16_MIN` = no data).
**gNB decisions** (written by the named stage — some in `sched_p{d,u}sch`, some `alloc_*`):
`sched_p{d,u}sch.nrOfLayers`, `.pm_index`/`.tpmi`, `.time_domain_allocation`, `.tda_info`,
`.mcs`, `.rbStart`, `.rbSize`;
`alloc_slbitmap`, `alloc_beam_idx`, `alloc_dci_beam_idx` (UL),
`alloc_cce_index`, `alloc_aggregation_level`, `alloc_sched_pdcch`.
### Sched Params (`nr_{dl,ul}_sched_params_t`)
Per-beam context passed to `rb_alloc`. Contains the MAC instance, slot info, VRB map,
available RBs, BLER thresholds, TDA info, and serving cell config.
---
## Writing a Custom Scheduler
### Replacing the RB allocation policy
Write a function matching the typedef:
```c
int my_ul_rb_alloc(const nr_ul_sched_params_t *params,
nr_ul_candidate_t *candidates,
int n_candidates)
{
int n_scheduled = 0;
for (int i = 0; i < n_candidates; i++) {
nr_ul_candidate_t *cand = &candidates[i];
// ... decide rbStart, rbSize, mcs ...
// COMMIT_UL_ALLOC writes sched_pusch fields, validates CCE, sets cand->scheduled:
COMMIT_UL_ALLOC(params, cand, rbStart, rbSize, mcs, n_scheduled);
}
return n_scheduled;
}
```
Register it in `openair2/LAYER2/NR_MAC_gNB/main.c`:
```c
RC.nrmac[i]->ul_rb_alloc = my_ul_rb_alloc;
```
### Replacing the per-LCID allocation policy (DL)
Write a function matching the typedef:
```c
void my_lcid_alloc(const gNB_MAC_INST *mac,
const nr_dl_candidate_t *candidate,
int tbs_available,
int lcid_alloc[NR_MAX_NUM_LCID])
{
memset(lcid_alloc, 0, NR_MAX_NUM_LCID * sizeof(int));
// ... use candidate->pending_bytes_per_lcid[], candidate->fiveQI,
// candidate->priority, tbs_available to decide budgets ...
for (int lcid = 0; lcid < NR_MAX_NUM_LCID; lcid++)
lcid_alloc[lcid] = my_budget_for(lcid);
}
```
Register it:
```c
RC.nrmac[i]->dl_lcid_alloc = my_lcid_alloc;
```
### Replacing other stages
Any function pointer can be replaced independently:
```c
RC.nrmac[i]->ul_ri_tpmi_select = my_ri_tpmi_select;
RC.nrmac[i]->ul_mcs_select = my_mcs_select;
RC.nrmac[i]->ul_beam_select = my_beam_select;
```
### Recommendations
- **Always use `COMMIT_ALLOC` / `COMMIT_UL_ALLOC`** to accept an allocation. It writes
`sched_p{d,u}sch` fields onto the candidate, validates CCE (and PUCCH on DL), and sets
`cand->scheduled`.
- **Do not mark the VRB map** — `commit_alloc` / `commit_ul_alloc` handles that inside the macro.
- `sched_p{d,u}sch.mcs` is already set by `mcs_select` when your policy runs. You may
refine it downward (e.g. PHR); pass the final value to `COMMIT_ALLOC` / `COMMIT_UL_ALLOC`.
- Candidates are non-const: the policy is allowed to write `sched_p{d,u}sch` and `alloc_*`
fields. Do not modify identity/state or CSI fields.

View File

@@ -0,0 +1,417 @@
# OAI 5G NR SA tutorial with OAI nrUE with GPU Offload
This tutorial explains how OAI nrUE can use GPU to offload LDPC decoder. In the future we might offload more functions on the GPU.
**Table of Contents**
[[_TOC_]]
## Current Status
The nrUE GPU offload functionality have been tested on below platforms:
|Platform |Version | OS/Linux Kernel |
|---------------|------------|---------------------------------------|
|Jetson AGX Orin|L4T 36.4.7 | U22/5.15.148-tegra (64K page variant) |
|DGX Spark |7.4.0 | U24/6.14.0-1013-nvidia-64k |
The above versions are tested versions, you are free to try on newer versions as well.
You can use another UE platform with GPU as well but it is preferred to use unified memory GPU variant.
### KPIs
The below throughput numbers are similar for TCP/UDP, the gNB was configured for 5 ms TDD pattern (6D3U and Special slot 10D0U) for Nvidia Aerial Testbed.
The UE was in SISO mode.
Testbed Architecture:
UE <--> Over the Air 1.5m to 2m distance <--> WNC RU <--> gNB/DU server
| Platform | Radio | Bandwidth | DL Throughput | UL Throughput |
| ----------- | --------- | --------- | ------------- | ------------- |
| Jetson Orin | B210 | 10 MHz | 12 Mbps | 7.5 Mbps |
| Jetson Orin | B210 | 20 MHz | 20 Mbps | 9.5 Mbps |
| Jetson Orin | B210 | 30 MHz | 61 Mbps | 33 Mbps |
| Jetson Orin | B210 | 40 MHz | 69 Mbps | 46 Mbps |
| DGX Spark | B210 | 40 MHz | 86 Mbps | 46 Mbps |
| DGX Spark | N310/x410 | 100 MHz | 231 Mbps | 118 Mbps |
Stability or long running test with peak throughput for nrUE was performed for 5hrs.
All the numbers are with LDPC GPU offload.
`NOTE`: At the time of writing we only tested the UE in SISO.
We are working supporting multi antenna use case.
## Aerial Testbed Configuration
If you are using aerial testbed with OAI then depending on the bandwidth you want to use, you will have to adapt the value of `ds_offset_dl` in cuphycontroller configuration file.
For our testing environment we adapted the `ds_offset_dl` values as below:
| Bandwidth | ds_offset_dl |
| ----------- | -------------|
| 10 MHz | 14 |
| 20 MHz | 12 |
| 30 MHz | 10 |
| 40 MHz | 9 |
| 100 MHz | 6 |
On the WNC radio we have the below configuration:
```
transmit-power 24
transmit-power-scale 0.0
compress tx static bfp iq-bitwidth 9
compress rx static bfp iq-bitwidth 9
compress prach static bfp iq-bitwidth 9
```
You can adjust the scaling on the RU or you can adjust it in aerial configuration file.
**NOTE**: You might have to adjust these values for your environment, so that the UE can detect the cell and perform random-access.
## Configure the nrUE Hardware Platform
### Configure Jetson AGX Orin
At the time of writing this tutorial AGX Orin only had Jetpack 6.X release which has U22/5.15.148-tegra, for GPU offload on ARM systems it is preferred to use 64K page size kernel.
To change the kernel to 64K page size variant you will have to manually build the kernel. Probably for Jetpack 7.X it will be easier. Before building the kernel make sure AGX Orin has the up to date kernel for your current distribution.
NVIDIA-SMI Version: 540.4.0
CUDA Version: 12.6
CPU: Cortex-A78AE (12)
OS: U22
Kernel: 5.15.148-tegra
NVCC: Build cuda_12.6.r12.6/compiler.34714021_0
UHD: UHD_4.9.0.HEAD-0-g006d7f76 (v4.9.0.0)
#### Update kernel to 64K page size
Login to Jetson AGX Orin
```bash
sudo apt update
sudo apt upgrade
# reboot
```
**CAUTION**: While building the kernel if you make a mistake you have to flash again the Jetson. We are just providing these steps to help you build the kernel with 64K page size. If you have questions or issues building the kernel you can check the [official nvidia documentation](https://docs.nvidia.com/jetson/archives/r36.4.4/DeveloperGuide/SD/Kernel/KernelCustomization.html#sd-kernel-kernelcustomization)
prepare the kernel source code:
```bash
sudo apt install git-core build-essential bc
cd ~/
wget https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v4.4/sources/public_sources.tbz2
# Build Jetson Linux Kernel
tar -xf public_sources.tbz2
cd ~/Linux_for_Tegra/source
tar -xf kernel_src.tbz2
tar -xf kernel_oot_modules_src.tbz2
tar -xf nvidia_kernel_display_driver_source.tbz2
```
the default kernel configuration file used is `kernel/kernel-jammy-src/arch/arm64/configs/defconfig` it is mentioned in `kernel_src_build_env.sh`.
Add `CONFIG_ARM64_64K_PAGES=y` in `kernel/kernel-jammy-src/arch/arm64/configs/defconfig`.
Build the kernel, out-of-tree modules and dtbs:
```bash
make -C kernel
sudo -E make install -C kernel
cp kernel/kernel-jammy-src/arch/arm64/boot/Image
/boot/Image
# Building the NVIDIA Out-of-Tree Modules
cd ~/Linux_for_Tegra/source
make modules
sudo -E make modules_install
sudo nv-update-initrd
make dtbs
cp kernel-devicetree/generic-dts/dtbs/* /boot/dtbs/
```
#### Configure operating system
```bash
jetson_clocks
touch /etc/sysctl.d/rt.conf
## add below content using vim/vi/nano
kernel.sched_rt_runtime_us=-1
kernel.timer_migration=0
sudo cpufreq-set -c 0 -g performance
sudo cpufreq-set -c 5 -g performance
sudo cpufreq-set -c 9 -g performance
```
### Configure DGX Spark
To setup DGX spark you would need to follow the below steps
NVIDIA-SMI Version: 580.95.05
CUDA Version: 13.0
OS: Ubuntu 24
Kernel: 6.14.0-1013-nvidia-64k (You can use a higher version)
NVCC: Build cuda_13.0.r13.0/compiler.36424714_0
UHD: UHD_4.9.0.HEAD-0-g006d7f76 (v4.9.0.0)
Efficient cores (Cortex-A725): 0,1,2,3,4,10,11,12,13,14
Performance cores (Cortex-X925): 5,6,7,8,9,15,16,17,18,19
You can use `sudo cpupower -c 0-19 frequency-info` to check the above core distribution.
```bash
sudo apt update
sudo apt upgrade
#reboot
sudo /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Unattended-Upgrade "0";
# Install 64K kernel corresponding to your running kernel
sudo apt install linux-image-6.14.0-1013-nvidia-64k
# You can also fix the kernel so it is not changed with apt upgrade
sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for DGX OS
GNU\/Linux>DGX OS GNU\/Linux, with Linux 6.14.0-1013-nvidia-64k"/'
/etc/default/grub
#Configure the command line to isolate the cores
cat <<"EOF" | sudo tee /etc/default/grub.d/cmdline.cfg
GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX pci=realloc=off pci=pcie_bus_safe
default_hugepagesz=512M hugepagesz=512M hugepages=24 tsc=reliable
processor.max_cstate=0 audit=0 idle=poll rcu_nocb_poll nosoftlockup
irqaffinity=0-3 isolcpus=managed_irq,domain,4-19
nohz_full=4-19 rcu_nocbs=4-19 earlycon module_blacklist=nouveau
acpi_power_meter.force_cap_on=y numa_balancing=disable init_on_alloc=0
preempt=none ras=off iommu=off mitgations=off kpti=off skew_tick=1 nowatchdog
mce=ignore_ce transparent_hugepage=never"
EOF
touch /etc/sysctl.d/rt.conf
## add below content using vim/vi/nano
kernel.sched_rt_runtime_us=-1
kernel.timer_migration=0
sudo update-grub
sudo reboot
# after reboot you can make a script to set the gpu clock speed to maximum
nvidia-smi -lgc 2000
sudo cpupower frequency-set --governor performance
sudo cpupower idle-set -D 0
```
### Configuration for Ethernet based USRP
If you want to use Ethernet based USRP like N3XX/X3XX/X4XX you would need to run the below commands:
```bash
sudo sysctl -w net.core.wmem_max=62500000
sudo sysctl -w net.core.rmem_max=62500000
sudo sysctl -w net.core.wmem_default=62500000
sudo sysctl -w net.core.rmem_default=62500000
sudo ethtool -G <usrp-interface> tx <maximum-value> rx <maximum-value>
```
*NOTE*: Currently we finalizing container images for OAI-NR-UE with GPU offload, we will provide it soon for container based deployments.
## Using nrUE with B2XX
### Build OAI-nrUE
```bash
mkdir ~/oai-nr-ue
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/oai-nr-ue
cd ~/oai-nr-ue
git checkout ldpc_offload_cuda
cd ~/oai-nr-ue/openairinterface5g/cmake_targets
#Install third-party dependencies
./build_oai -I -w USRP
# Install nrue with USRP support, default nvcc location is /usr/local/bin/nvcc
./build_oai --ninja --build-lib ldpc_cuda --gNB --nrUE -w USRP -C --cmake-opt -DCMAKE_CUDA_COMPILER=<nvcc-location>
```
## Start the nrUE process
Create a UE configuration file based on the PLMN, slicing information, DNN and DNN type you have configured for the UE in the core network.
```bash
cd ~/oai-nr-ue/
# touch nr-ue.conf
```
Sample UE configuration file, you can use the below configuration file and adjust the values as per your testing environment:
```bash
cat nr-ue.conf
uicc0 = {
imsi = "001010000000001";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
}
```
To start the `nr-uesoftmodem` you would need to know the center frequency, bandwidth, band and ssb. This information is printed in the OAI-gNB logs. You can search for the line `[NR_MAC] Command line parameters for OAI UE` for example:
```bash
[NR_MAC] Command line parameters for OAI UE: -C 3350010000 -r 106 --numerology 1 --band 78 --ssb 513
```
Once you have this information you can start the `nr-uesoftmodem` process:
**Command for Jetson AGX Orin**:
**MANDATORY**: Before starting the UE process it is important to configure the gNB configuration file with `min_rxtxtime` as 6 or higher integer value.
```bash
cd ~/oai-nr-ue/openairinterface5g/cmake_targets/
sudo ./nr-uesoftmodem -O ~/oai-nr-ue/nr-ue.conf -C 3350010000 -r 106 --ssb 513 --numerology 1 --band 78 --thread-pool -1 --num-dl-actors 6 --num-ul-actors 2 -E --loader.ldpc.shlibversion _cuda --usrp-tx-thread-config 1 --ue-fo-compensation
```
**Command for DGX Spark**:
**MANDATORY**: Before starting the UE process it is important to configure the gNB configuration file with `min_rxtxtime` as 6 or higher integer value.
```bash
cd ~/oai-nr-ue/openairinterface5g/cmake_targets/
sudo taskset -c "5-9,15-19" ./nr-uesoftmodem -O ~/oai-nr-ue/nr-ue.conf -C 3350010000 -r 106 --ssb 513 --numerology 1 --band 78 --thread-pool -1 -E --loader.ldpc.shlibversion _cuda --ue-fo-compensation
```
In the above command task-set allows running the UE only on the performance cores.
Understand the command which you just used:
The main RF/cell settings are:
- `-O`: load the UE config file.
- `-C 3900000000`: set DL carrier to 3.9 GHz.
- `-r 106`: use 106 PRBs
- `--numerology 1`: numerology 1, meaning 30 kHz subcarrier spacing.
- `--band 77`: NR band n77.
- `--ssb 516`: expect the SSB to start at sub-carrier 516.
- `--ue-fo-compensation`: enable initial frequency-offset compensation, useful OTA when clocks are not perfectly aligned.
- `--usrp-tx-thread-config 1`: enable an extra USRP TX processing thread
- `-E`: enable threequarter_fs, meaning the UE uses three-quarter sampling rate mode. In OAI this reduces sample rate / transport load where supported.
The parallelism/offload settings are:
- `--thread-pool -1`: create a thread-pool worker with no CPU affinity.
- `--num-dl-actors 6`: use 6 DL actor threads, parallel RX/PDSCH processing workers.
- `--num-ul-actors 2`: use 2 UL actor threads, parallel TX/PUSCH/PUCCH preparation and transmit workers.
- `--loader.ldpc.shlibversion_cuda`: load the CUDA-backed LDPC shared library variant.
When the UE starts it starts with highest gains, but sometimes you would need to adjust the tx/rx gains based on the UEs position from the RU/gNB. To manipulate the gains you can use the below options:
- `--ue-rxgain x`: set UE RX gain.
- `--ue-txgain x`: set UE TX gain.
- `-A 50`: apply a command-line sample advance of 50 samples to compensate RF timing offset.
Once the UE connects you will see the tunnel interface `oaitun_ue1` with ip-address in the subnet which is configured at SMF. You can open another terminal and from there you can do traffic test.
## Using nrUE with X3XX/N3XX/x4XX
### Build OAI-nrUE
For Ethernet based USRPs it is recommended to use DPDK mostly when you want to test for 60-100 MHz bandwidth. If you don't want to use DPDK then you can build the `nr-uesoftmodem` using the previous section.
```bash
mkdir ~/oai-nr-ue
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/oai-nr-ue
cd ~/oai-nr-ue
git checkout ldpc_offload_cuda
cd ~/oai-nr-ue/openairinterface5g/cmake_targets
#Install third-party dependencies
./build_oai -I
#Build and Install DPDK
cd ~/
wget https://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz
sudo apt install wget xz-utils libnuma-dev meson libibverbs-dev rdma-core python3-pyelftools
meson setup build -Dplatform=generic
ninja -C build
sudo ninja install -C build
#Build and Install UHD
sudo apt install autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool g++ git inetutils-tools libboost-all-dev libncurses-dev libusb-1.0-0 libusb-1.0-0-dev libusb-dev python3-dev python3-mako python3-numpy python3-requests python3-scipy python3-setuptools python3-ruamel.yaml
git clone https://github.com/EttusResearch/uhd ~/uhd && cd uhd
git checkout v4.9.0.0
cd host && mkdir build && cd build
cmake ../ -GNinja
ninja
sudo ninja install
# Install nrue with USRP support, default nvcc location is /usr/local/bin/nvcc
./build_oai --ninja --build-lib ldpc_cuda --gNB --nrUE -w USRP -C --cmake-opt -DCMAKE_CUDA_COMPILER=<nvcc-location>
```
Create the DPDK interfaces for the UHD, use the below script and modify it for your environment. We are using VLAN 80 for DPDK in our environment:
```bash
vim setup_sriov_usrp.sh
```
```bash
#!/bin/sh
set -xeu
pci_addr()
{
PF_IF=$1
VF_INDEX=$2
SYSFS_PATH="/sys/class/net/${PF_IF}/device/virtfn${VF_INDEX}"
if [ ! -e "$SYSFS_PATH" ]; then
echo "VF $VF_INDEX not found for interface $PF_IF"
exit 1
fi
PCI_ADDR=$(basename "$(readlink "$SYSFS_PATH")")
echo "$PCI_ADDR"
}
IF_NAME=enP2p1s0f1np1 ## interface to reach to the USRP on the UHD host
HOST_MAC_ADD=00:11:22:33:44:67
VLAN=80
MTU=9000
NUM_VFs=1
sudo ethtool -G $IF_NAME rx 8160 tx 8160
sudo sh -c "echo 0 > /sys/class/net/$IF_NAME/device/sriov_numvfs"
sudo sh -c "echo $NUM_VFs > /sys/class/net/$IF_NAME/device/sriov_numvfs"
HOST_PCI_ADDR=$(pci_addr $IF_NAME 0)
# this next 2 lines is for C/U planes
sudo ip link set $IF_NAME vf 0 mac $HOST_MAC_ADD vlan $VLAN spoofchk off mtu $MTU
sleep 1
sudo dpdk-devbind.py --unbind $HOST_PCI_ADDR
sudo modprobe mlx5_core
sudo dpdk-devbind.py --bind mlx5_core $HOST_PCI_ADDR
echo "Successfully configured interface for USRP:
- Mac-address: $HOST_MAC_ADD, VLAN: $VLAN, PCI: $HOST_PCI_ADDR"
exit 0
```
Create `/etc/uhd/uhd.conf` configuration file for allowing UHD to use dpdk for reaching to the USRP:
```txt
[use_dpdk=1]
dpdk_mtu=9000
dpdk_driver=/usr/local/lib/aarch64-linux-gnu/dpdk/pmds-25.0/
dpdk_corelist=5,6,7,8,9
[dpdk_mac=00:11:22:33:44:67]
dpdk_lcore=6
dpdk_ipv4=192.168.80.25/24
```
The ip-address 192.168.80.25/24 is the UHD host ip-address. Make sure you adjust the `dpdk_driver` location.
### Start the nrUE process
Create a UE configuration file based on the PLMN, slicing information, DNN and DNN type you have configured for the UE in the core network. It is similar to previous section.
To start the `nr-uesoftmodem` process:
**Command for DGX Spark**:
```bash
sudo devlink dev reload pci/<HOST_PCI_ADDR> && sudo taskset -c "5-19" ./nr-uesoftmodem --ue-fo-compensation --thread-pool 15,16,17,18,19 --loader.ldpc.shlibversion _cuda -O ~/oai-nr-ue/nr-ue.conf --usrp-args "addr=192.168.80.251,use_dpdk=1" -C 3350010000 -r 273 --numerology 1 --band 78 --ssb 1515 --usrp-tx-thread-config 1
```
DPDK deployment have some issues currently and we need to reload the pci interface before every run. The devlink command `sudo devlink dev reload pci/<HOST_PCI_ADDR>` is doing that.
If you need to adjust the tx/rx gains then you will need to take the parameters from previous section.

View File

@@ -169,7 +169,6 @@ generate DL shared channel (user data)
#### nr_feptx_prec()
tx precoding
<<<<<<< HEAD:doc/SW_archi.md
#### nr_feptx0
do the inverse DFT

View File

@@ -0,0 +1,76 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
#
#---------------------------------------------------------------------
FROM nvidia/cuda:12.2.0-devel-ubuntu22.04 AS ran-base-cuda
ARG NEEDED_GIT_PROXY
ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris
ENV BUILD_UHD_FROM_SOURCE=True
ENV UHD_VERSION=4.8.0.0
#install developers pkg/repo
RUN apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes \
#gcc needed for build_oai
build-essential \
ccache \
psmisc \
git \
#use gcc-12 to avoid problems of default gcc-11 in FlexRIC
gcc-12 \
g++-12 \
xxd \
libpcre2-dev \
python3-dev \
bison \
flex \
m4 \
# python3-pip for conf template generation
python3-pip \
wget \
flatbuffers-compiler \
libflatbuffers-dev \
libnanomsg-dev && \
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 && \
pip3 install --ignore-installed pyyaml
# Add "Tini - A tiny but valid init for containers", https://github.com/krallin/tini
# it will be copied into target containers, to print exit numbers and handle signals properly
ENV TINI_VERSION=v0.19.0
RUN wget https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} -O /tini && chmod +x /tini
ENV YQ_VERSION=v4.2.0
RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${TARGETARCH} -O /usr/local/bin/yq &&\
chmod +x /usr/local/bin/yq
# In some network environments, GIT proxy is required
RUN /bin/bash -c "if [[ -v NEEDED_GIT_PROXY ]]; then git config --global http.proxy $NEEDED_GIT_PROXY; fi"
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
./
WORKDIR /oai-ran/cmake_targets
COPY cmake_targets/build_oai .
WORKDIR /oai-ran
COPY oaienv .
#run build_oai -I to get the builder image
RUN /bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
./build_oai -I -w USRP -w BLADERF --install-optional-packages

View File

@@ -67,7 +67,7 @@ RUN /bin/bash -c "if [[ -v NEEDED_GIT_PROXY ]]; then git config --global http.pr
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
cmake_targets/tools/uhd-4.5plus-tdd-patch.diff \
./
WORKDIR /oai-ran/cmake_targets

View File

@@ -55,7 +55,7 @@ RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linu
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
cmake_targets/tools/uhd-4.5plus-tdd-patch.diff \
./
WORKDIR /oai-ran/cmake_targets

View File

@@ -60,7 +60,7 @@ RUN /bin/bash -c "if [[ -v NEEDED_GIT_PROXY ]]; then git config --global http.pr
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
cmake_targets/tools/uhd-4.5plus-tdd-patch.diff \
./
WORKDIR /oai-ran/cmake_targets

View File

@@ -63,7 +63,7 @@ RUN chmod +x /tini
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
cmake_targets/tools/uhd-4.5plus-tdd-patch.diff \
./
WORKDIR /oai-ran/cmake_targets

View File

@@ -0,0 +1,58 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
#
#---------------------------------------------------------------------
FROM ran-base-cuda:latest AS ran-build-cuda
ARG BUILD_OPTION
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
# build all targets so final targets can be created afterwards
RUN \
# Mount CCache cache direrctory
--mount=type=cache,target=/root/.cache/ccache/ \
# Mount CPM package cache
--mount=type=cache,target=/root/.cache/cpm/ \
/bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
./build_oai -c --ninja \
--eNB --gNB --RU --UE --nrUE \
--build-lib "telnetsrv enbscope uescope nrscope ldpc_cuda" \
-w USRP -t Ethernet -w BLADERF \
--build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
--cmake-opt -DOAI_VRTSIM_TAPS_CLIENT=ON \
--cmake-opt -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-12.2 \
--noavx512 \
--build-tool-opt -k10 \
--cmake-opt -DCMAKE_C_FLAGS="-Werror" --cmake-opt -DCMAKE_CXX_FLAGS="-Werror" $BUILD_OPTION && \
# Mainly to see if the sanitize option was perfectly executed
echo "---- ldd on executables ----" && \
ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \
echo "---- ldd on shared libraries ----" && \
ldd ran_build/build/*.so
## Build FlexRIC for SM Models
RUN \
# Mount CCache cache direrctory
--mount=type=cache,target=/root/.cache/ccache/ \
mkdir -p openair2/E2AP/flexric/build && \
cd openair2/E2AP/flexric/build && \
cmake -GNinja -DCMAKE_BUILD_TYPE=Release \
-DKPM_VERSION=$KPM_VERSION \
-DCMAKE_C_COMPILER=gcc-12 \
-DCMAKE_CXX_COMPILER=g++-12 .. \
-DE2AP_VERSION=$E2AP_VERSION .. && \
ninja && \
ninja install

View File

@@ -0,0 +1,115 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
#
#---------------------------------------------------------------------
ARG UBUNTU_IMAGE=ubuntu:noble
FROM ran-base-cuda:latest AS gnb-base
FROM ran-build-cuda:latest AS gnb-build
#start from scratch for target executable
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04 AS oai-gnb
ARG BUILD_OPTION
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris
RUN apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes \
software-properties-common \
procps \
libsctp1 \
libboost-chrono1.74.0 \
libboost-date-time1.74.0 \
libboost-filesystem1.74.0 \
libboost-regex1.74.0 \
libboost-serialization1.74.0 \
libboost-thread1.74.0 \
libboost-system1.74.0 \
libboost-program-options1.74.0 \
tzdata \
libconfig9 \
openssl \
net-tools \
iperf \
iperf3 \
iproute2 \
iputils-ping \
gdb \
python3 \
python3-six \
python3-requests \
libusb-1.0-0 \
libyaml-cpp-dev \
libnanomsg5 && \
# if the --sanitize option was used to build, additional packages are required
/bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \
libasan8 \
libubsan1; fi' && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
WORKDIR /opt/oai-gnb/bin
COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/nr-softmodem ./
COPY ./docker/scripts/gnb_entrypoint.sh ./entrypoint.sh
COPY ./docker/scripts/check-prach-io.sh ./check-prach-io.sh
COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/liboai_eth_transpro.so \
/oai-ran/cmake_targets/ran_build/build/librfsimulator.so \
/oai-ran/cmake_targets/ran_build/build/liboai_usrpdevif.so \
/oai-ran/cmake_targets/ran_build/build/libcoding.so \
/oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \
/oai-ran/cmake_targets/ran_build/build/libdfts.so \
/oai-ran/cmake_targets/ran_build/build/libldpc*.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_bearer.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_rrc.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_o1.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
COPY --from=gnb-base /usr/local/bin/uhd_find_devices /usr/local/bin
COPY --from=gnb-base /usr/local/lib/libuhd.so.4.8.0 /usr/local/lib
COPY --from=gnb-base /usr/local/lib/uhd/utils/uhd_images_downloader.py /opt/oai-gnb/bin
## Copy E2 SM models
COPY --from=gnb-build /usr/local/lib/flexric /usr/local/lib/flexric
RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai_device.so" && \
ldconfig && \
echo "---- ldd on nr-softmodem ----" && \
ldd /opt/oai-gnb/bin/nr-softmodem && \
echo "---- ldd on shared libraries ----" && \
ldd /usr/local/lib/liboai_eth_transpro.so \
/usr/local/lib/librfsimulator.so \
/usr/local/lib/liboai_usrpdevif.so \
/usr/local/lib/libcoding.so \
/usr/local/lib/libparams_libconfig.so \
/usr/local/lib/libdfts.so \
/usr/local/lib/libldpc*.so \
/usr/local/lib/libtelnetsrv.so \
/usr/local/lib/libtelnetsrv_ci.so \
/usr/local/lib/libuhd.so.4.8.0
WORKDIR /opt/oai-gnb
#EXPOSE 2152/udp # S1U, GTP/UDP
#EXPOSE 22100/tcp # ?
#EXPOSE 36412/udp # S1C, SCTP/UDP
#EXPOSE 36422/udp # X2C, SCTP/UDP
#EXPOSE 50000/udp # IF5 / ORI (control)
#EXPOSE 50001/udp # IF5 / ECPRI (data)
COPY --from=gnb-base /usr/local/bin/yq /usr/local/bin/yq
COPY --from=gnb-base /tini /tini
ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-gnb/bin/entrypoint.sh"]
CMD ["/opt/oai-gnb/bin/nr-softmodem"]

View File

@@ -0,0 +1,107 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
#
#---------------------------------------------------------------------
FROM ran-base-cuda:latest AS nr-ue-base
FROM ran-build-cuda:latest AS nr-ue-build
#start from scratch for target executable
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04 AS oai-nr-ue
ARG BUILD_OPTION
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris
RUN apt-get update && \
apt-get upgrade --yes && \
apt-get install --yes \
arping \
software-properties-common \
procps \
libsctp1 \
libboost-chrono1.74.0 \
libboost-date-time1.74.0 \
libboost-filesystem1.74.0 \
libboost-regex1.74.0 \
libboost-serialization1.74.0 \
libboost-thread1.74.0 \
libboost-system1.74.0 \
libboost-program-options1.74.0 \
tzdata \
libconfig9 \
openssl \
net-tools \
gdb \
python3 \
python3-six \
python3-requests \
libusb-1.0-0 \
iputils-ping \
iproute2 \
iperf3 \
iperf \
libyaml-cpp-dev \
libnanomsg5 && \
# if the --sanitize option was used to build, additional packages are required
/bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \
libasan8 \
libubsan1; fi' && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
WORKDIR /opt/oai-nr-ue/bin
COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem \
./
COPY ./docker/scripts/nr_ue_entrypoint.sh ./entrypoint.sh
WORKDIR /opt/oai-nr-ue/etc
COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/liboai_eth_transpro.so \
/oai-ran/cmake_targets/ran_build/build/librfsimulator.so \
/oai-ran/cmake_targets/ran_build/build/liboai_usrpdevif.so \
/oai-ran/cmake_targets/ran_build/build/libcoding.so \
/oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \
/oai-ran/cmake_targets/ran_build/build/libdfts.so \
/oai-ran/cmake_targets/ran_build/build/libldpc*.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ciUE.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
COPY --from=nr-ue-base /usr/local/bin/uhd_find_devices /usr/local/bin
COPY --from=nr-ue-base /usr/local/lib/libuhd.so.4.8.0 /usr/local/lib
COPY --from=nr-ue-base /usr/local/lib/uhd/utils/uhd_images_downloader.py /opt/oai-nr-ue/bin
RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai_device.so" && \
ldconfig && \
echo "---- ldd on nr-uesoftmodem ----" && \
ldd /opt/oai-nr-ue/bin/nr-uesoftmodem && \
echo "---- ldd on shared libraries ----" && \
ldd /usr/local/lib/liboai_eth_transpro.so \
/usr/local/lib/librfsimulator.so \
/usr/local/lib/liboai_usrpdevif.so \
/usr/local/lib/libcoding.so \
/usr/local/lib/libparams_libconfig.so \
/usr/local/lib/libdfts.so \
/usr/local/lib/libldpc*.so \
/usr/local/lib/libtelnetsrv.so \
/usr/local/lib/libtelnetsrv_ciUE.so \
/usr/local/lib/libtelnetsrv_5Gue.so \
/usr/local/lib/libuhd.so.4.8.0
WORKDIR /opt/oai-nr-ue
COPY --from=nr-ue-base /usr/local/bin/yq /usr/local/bin/yq
COPY --from=nr-ue-base /tini /tini
CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem"]
ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-nr-ue/bin/entrypoint.sh"]

View File

@@ -70,6 +70,7 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "openair1/SCHED_NR/sched_nr.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
RAN_CONTEXT_t RC;
pthread_cond_t nfapi_sync_cond;
pthread_mutex_t nfapi_sync_mutex;
int nfapi_sync_var=-1; //!< protected by mutex \ref nfapi_sync_mutex

View File

@@ -558,8 +558,8 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE,
if (phy_data->dlsch[0].active
&& (phy_data->dlsch[0].rnti_type == TYPE_C_RNTI_ || phy_data->dlsch[0].rnti_type == TYPE_RA_RNTI_)) {
// indicate to tx thread to wait for DLSCH decoding
if (phy_data->dlsch[0].dlsch_config.k1_feedback) { // if feedback is 0 there is no HARQ associated with this DLSCH
const int ack_nack_slot = (proc->nr_slot_rx + phy_data->dlsch[0].dlsch_config.k1_feedback) % fp->slots_per_frame;
if (phy_data->dlsch_config.k1_feedback) { // if feedback is 0 there is no HARQ associated with this DLSCH
const int ack_nack_slot = (proc->nr_slot_rx + phy_data->dlsch_config.k1_feedback) % fp->slots_per_frame;
tx_wait_for_dlsch[ack_nack_slot]++;
}
}

View File

@@ -256,7 +256,7 @@ int main(int argc, char **argv)
init_opt();
int ret_loader = load_nrLDPC_coding_interface(NULL, &nrLDPC_coding_interface);
int ret_loader = load_nrLDPC_coding_interface(NULL, &nrLDPC_coding_interface, 32);
AssertFatal(ret_loader == 0, "error loading LDPC library\n");
// strdup to put the sring in the core file for post mortem identification

View File

@@ -118,6 +118,7 @@ typedef struct {
typedef struct {
uint8_t harq_pid;
uint8_t cw_idx;
uint8_t ack_nack;
uint32_t pdu_length;
uint8_t* pdu;
@@ -466,6 +467,17 @@ typedef struct {
typedef enum{vrb_to_prb_mapping_non_interleaved = 0, vrb_to_prb_mapping_interleaved = 1} vrb_to_prb_mapping_t;
typedef struct {
uint8_t mcs;
bool new_data_indicator;
uint8_t rv;
uint16_t targetCodeRate;
uint8_t qamModOrder;
uint32_t TBS;
uint8_t ldpcBaseGraph;
uint8_t Nl;
} fapi_nr_dl_cw_info_t;
typedef struct {
uint16_t BWPSize;
uint16_t BWPStart;
@@ -482,15 +494,8 @@ typedef struct {
uint8_t prb_bundling_size_ind;
uint8_t rate_matching_ind;
uint8_t zp_csi_rs_trigger;
uint8_t mcs;
bool new_data_indicator;
uint8_t rv;
uint16_t targetCodeRate;
uint8_t qamModOrder;
uint32_t TBS;
uint8_t tb2_mcs;
bool tb2_new_data_indicator;
uint8_t tb2_rv;
uint8_t n_codewords;
fapi_nr_dl_cw_info_t cw_info[2];
uint8_t harq_process_nbr;
vrb_to_prb_mapping_t vrb_to_prb_mapping;
uint8_t dai;
@@ -523,7 +528,6 @@ typedef struct {
uint16_t dlDataScramblingId;
uint16_t pduBitmap;
uint32_t k1_feedback;
uint8_t ldpcBaseGraph;
uint8_t numCsiRsForRateMatching;
fapi_nr_dl_config_csirs_pdu_rel15_t csiRsForRateMatching[NFAPI_MAX_NUM_CSI_RATEMATCH];
} fapi_nr_dl_config_dlsch_pdu_rel15_t;

View File

@@ -16,6 +16,9 @@
#include "openair1/PHY/defs_nr_common.h"
#include "coding_unitary_defs.h"
#include "common/utils/LOG/log.h"
#ifdef LDPC_CUDA
#include <cuda_runtime.h>
#endif
#define MAX_BLOCK_LENGTH 8448
@@ -24,7 +27,14 @@
#endif
#define NR_LDPC_PROFILER_DETAIL
#define NR_LDPC_ENABLE_PARITY_CHECK
ldpc_interface_t ldpc_orig, ldpc_toCompare;
static double modulated_input[MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * 4][68 * 384];
#define MAX_TRIALS 20000
static int8_t Failure_Mask[MAX_TRIALS][(MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * 4)] = {0};
int PARALLEL_PATH = 0;//control decoder path
// 4-bit quantizer
int8_t quantize4bit(double D, double x)
@@ -78,6 +88,9 @@ typedef struct {
n_iter_stats_t dec_iter;
} one_measurement_t;
uint8_t *estimated_output_dev,*estimated_output;
int8_t *channel_output_fixed_dev,*channel_output_fixed;
one_measurement_t test_ldpc(short max_iterations,
int nom_rate,
int denom_rate,
@@ -85,14 +98,16 @@ one_measurement_t test_ldpc(short max_iterations,
unsigned char qbits,
short Kprime,
unsigned int ntrials,
int n_segments)
int n_segments,
int gen_code)
{
static int init_done = 0;
one_measurement_t ret = {0};
reset_meas(&ret.time_optim);
reset_meas(&ret.time_decoder);
// clock initiate
// time_stats_t time,time_optim,tinput,tprep,tparity,toutput, time_decoder;
time_stats_t time, tinput, tprep, tparity, toutput;
time_stats_t time, tinput, tinput_memcpy, tprep, tparity, toutput;
double n_iter_mean = 0;
double n_iter_std = 0;
int n_iter_max = 0;
@@ -100,14 +115,17 @@ one_measurement_t test_ldpc(short max_iterations,
double sigma;
sigma = 1.0 / sqrt(2 * SNR);
cpu_meas_enabled = 1;
uint8_t *test_input[MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * NR_MAX_NB_LAYERS];
uint8_t estimated_output[MAX_NUM_DLSCH_SEGMENTS][Kprime];
memset(estimated_output, 0, sizeof(estimated_output));
uint8_t *channel_input[MAX_NUM_DLSCH_SEGMENTS];
#ifdef LDPC_CUDA
uint8_t **test_input,*test_input_p;
#else
uint8_t *test_input[n_segments * NR_MAX_NB_LAYERS];
#endif
uint8_t *channel_input[n_segments];
uint8_t *channel_input_optim;
// double channel_output[68 * 384];
double modulated_input[MAX_NUM_DLSCH_SEGMENTS][68 * 384] = {0};
int8_t channel_output_fixed[MAX_NUM_DLSCH_SEGMENTS][68 * 384] = {0};
memset(modulated_input,0,sizeof(modulated_input));
short BG = 0, nrows = 0; //,ncols;
int i1, Kb = 0;
int R_ind = 0;
@@ -115,7 +133,8 @@ one_measurement_t test_ldpc(short max_iterations,
int code_rate_vec[8] = {15, 13, 25, 12, 23, 34, 56, 89};
// double code_rate_actual_vec[8] = {0.2, 0.33333, 0.4, 0.5, 0.66667, 0.73333, 0.81481, 0.88};
t_nrLDPC_dec_params decParams[MAX_NUM_DLSCH_SEGMENTS] = {0};
t_nrLDPC_dec_params decParams[n_segments];
memset(decParams,0,sizeof(decParams));
t_nrLDPC_time_stats decoder_profiler = {0};
@@ -123,6 +142,7 @@ one_measurement_t test_ldpc(short max_iterations,
reset_meas(&time);
reset_meas(&tinput);
reset_meas(&tinput_memcpy);
reset_meas(&tprep);
reset_meas(&tparity);
reset_meas(&toutput);
@@ -240,18 +260,26 @@ one_measurement_t test_ldpc(short max_iterations,
printf("To: %d\n", (Kb + nrows - no_punctured_columns) * Zc - removed_bit);
printf("number of undecoded bits: %d\n", (Kb + nrows - no_punctured_columns - 2) * Zc - removed_bit);
// ldpc_toCompare.LDPCinit();
// generate input block
for (int j = 0; j < MAX_NUM_DLSCH_SEGMENTS; j++) {
#ifdef LDPC_CUDA
cudaHostAlloc((void**)&test_input_p,n_segments*sizeof(uint8_t*),cudaHostAllocMapped);
test_input=(uint8_t **)test_input_p;
#endif
for (int j = 0; j < n_segments; j++) {
#ifdef LDPC_CUDA
cudaHostAlloc((void**)&test_input[j],((K + 7) & ~7) / 8,cudaHostAllocMapped);
#else
test_input[j] = malloc16(((K + 7) & ~7) / 8);
memset(test_input[j], 0, ((K + 7) & ~7) / 8);
#endif
channel_input[j] = malloc16(68 * 384);
memset(channel_input[j], 0, 68 * 384);
}
channel_input_optim = malloc16(68 * 384);
memset(channel_input_optim, 0, 68 * 384);
channel_input_optim = malloc16(68 * 384 * sizeof(uint32_t));
// Fill input segments with random values
for (int j = 0; j < MAX_NUM_DLSCH_SEGMENTS; j++) {
for (int j = 0; j < n_segments; j++) {
int i = 0;
for (i = 0; i < ((Kprime + 7) & ~7) / 8; i++)
test_input[j][i] = (uint8_t)rand();
@@ -263,13 +291,21 @@ one_measurement_t test_ldpc(short max_iterations,
}
encoder_implemparams_t impp = {.Zc = Zc, .Kb = Kb, .BG = BG, .K = K};
impp.gen_code = 1;
impp.gen_code = gen_code;
impp.tparity = &tparity;
impp.tinput = &tinput;
impp.tinput_memcpy = &tinput_memcpy;
impp.toutput = &toutput;
if (ntrials == 0)
ldpc_orig.LDPCencoder(test_input, channel_input[0], &impp);
impp.gen_code = 0;
decode_abort_t dec_abort;
init_abort(&dec_abort);
if (init_done == 0) {
ldpc_toCompare.LDPCinit();
init_done = 1;
}
for (int trial = 0; trial < ntrials; trial++) {
unsigned int segment_bler = 0;
//// encoder
@@ -280,37 +316,41 @@ one_measurement_t test_ldpc(short max_iterations,
stop_meas(&time);
impp.n_segments = n_segments;
start_meas(&ret.time_optim);
impp.first_seg = 0;
ldpc_toCompare.LDPCencoder(test_input, channel_input_optim, &impp);
stop_meas(&ret.time_optim);
if (ntrials == 1)
for (int j = 0; j < n_segments; j++)
for (int i = 0; i < K + (nrows - no_punctured_columns) * Zc - removed_bit; i++) {
if (channel_input[j][i] != ((channel_input_optim[i] >> j) & 0x1)) {
printf("differ in seg %d pos %d (%u,%u)\n", j, i, channel_input[j][i], (channel_input_optim[i] >> j) & 0x1);
return ret;
}
if (((channel_input[j][i] != ((channel_input_optim[i] >> j) & 0x1)))) {
printf("differ in seg %d pos %d (%u,%u)\n", j, i, channel_input[j][i], (((uint32_t*)channel_input_optim)[i] >> j) & 0x1);
return ret;
}
}
int bit=0;
for (int j = 0; j < n_segments; j++) {
for (int i = 2 * Zc; i < (Kb + nrows - no_punctured_columns) * Zc - removed_bit; i++) {
#ifdef DEBUG_CODER
if ((i & 0xf) == 0)
printf("\ne %d..%d: ", i, i + 15);
#endif
bit = ((channel_input_optim[i - 2 * Zc] >> j) & 0x1);
if (((channel_input_optim[i - 2 * Zc] >> j) & 0x1) == 0)
if (bit == 0)
modulated_input[j][i] = 1.0; /// sqrt(2); //QPSK
else
modulated_input[j][i] = -1.0; /// sqrt(2);
channel_output_fixed[j][i] =
channel_output_fixed[j*(384*68) + i] =
(int8_t)quantize(sigma / 4.0 / 4.0, modulated_input[j][i] + sigma * gaussdouble(0.0, 1.0), qbits);
// Uncoded BER
uint8_t channel_output_uncoded = channel_output_fixed[j][i] < 0 ? 1 /* QPSK demod */ : 0;
uint8_t channel_output_uncoded = channel_output_fixed[(j*384*68)+i] < 0 ? 1 /* QPSK demod */ : 0;
if (channel_output_uncoded != ((channel_input_optim[i - 2 * Zc] >> j) & 0x1))
ret.errors_bit_uncoded++;
}
@@ -326,25 +366,36 @@ one_measurement_t test_ldpc(short max_iterations,
decParams[j].numMaxIter = max_iterations;
decParams[j].outMode = nrLDPC_outMode_BIT;
decParams[j].Kprime = Kprime;
ldpc_toCompare.LDPCinit();
decParams[j].n_segments = n_segments;
//decParams[j].LastTrial = (trial == ntrials - 1) ? 1 : 0;
}
//Decoder
for (int j = 0; j < n_segments; j++) {
start_meas(&ret.time_decoder);
set_abort(&dec_abort, false);
n_iter = ldpc_toCompare.LDPCdecoder(&decParams[j],
(int8_t *)channel_output_fixed[j],
(int8_t *)estimated_output[j],
&decoder_profiler,
&dec_abort);
{
n_iter = ldpc_toCompare.LDPCdecoder(&decParams[j],
&channel_output_fixed[j*384*68],
&estimated_output[j*Kprime],
&decoder_profiler,
&dec_abort);
}
stop_meas(&ret.time_decoder);
// count errors
if (memcmp(estimated_output[j], test_input[j], ((Kprime + 7) & ~7) / 8) != 0) {
if (memcmp(&estimated_output[j*(Kprime>>3)], test_input[j], ((Kprime + 7) & ~7) / 8) != 0) {
segment_bler++;
if(trial < 200){
Failure_Mask[trial][j] = 1;
}
}
for (int i = 0; i < Kprime; i++) {
unsigned char estoutputbit = (estimated_output[j][i / 8] & (1 << (i & 7))) >> (i & 7);
unsigned char estoutputbit = (estimated_output[j*(Kprime>>3) + (i / 8)] & (1 << (i & 7))) >> (i & 7);
unsigned char inputbit = (test_input[j][i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments
if (estoutputbit != inputbit)
ret.errors_bit++;
@@ -367,17 +418,30 @@ one_measurement_t test_ldpc(short max_iterations,
sqrt(n_iter_std / (double)ntrials / (double)n_segments - pow(n_iter_mean / (double)ntrials / (double)n_segments - 1, 2));
ret.dec_iter.n_iter_max = n_iter_max - 1;
ret.dec_iter.n_iter_mean = n_iter_mean / (double)ntrials / (double)n_segments - 1;
ret.dec_iter.n_iter_std =
sqrt(n_iter_std / (double)ntrials / (double)n_segments - pow(n_iter_mean / (double)ntrials / (double)n_segments - 1, 2));
ret.dec_iter.n_iter_max = n_iter_max - 1;
ret.errors_bit_uncoded = ret.errors_bit_uncoded / (double)((Kb + nrows - no_punctured_columns - 2) * Zc - removed_bit);
for (int j = 0; j < MAX_NUM_DLSCH_SEGMENTS; j++) {
for (int j = 0; j < n_segments; j++) {
#ifdef LDPC_CUDA
cudaFreeHost(test_input[j]);
#else
free(test_input[j]);
#endif
free(channel_input[j]);
}
#ifdef LDPC_CUDA
cudaFreeHost(test_input);
#endif
free(channel_input_optim);
print_meas(&time, "ldpc_encoder", NULL, NULL);
print_meas(&ret.time_optim, "ldpc_encoder_optim", NULL, NULL);
print_meas(&tinput, "ldpc_encoder_optim(input)", NULL, NULL);
print_meas(&tinput_memcpy, "ldpc_encoder_optim(input memcpy)", NULL, NULL);
print_meas(&tprep, "ldpc_encoder_optim(prep)", NULL, NULL);
print_meas(&tparity, "ldpc_encoder_optim(parity)", NULL, NULL);
print_meas(&toutput, "ldpc_encoder_optim(output)", NULL, NULL);
@@ -399,6 +463,9 @@ one_measurement_t test_ldpc(short max_iterations,
}
configmodule_interface_t *uniqCfg = NULL;
#ifdef LDPC_CUDA
extern int pageable,integrated;
#endif
int main(int argc, char *argv[])
{
short Kprime = 8448;
@@ -418,19 +485,18 @@ int main(int argc, char *argv[])
int n_trials = 1;
double SNR_step = 0.1;
int gen_code = 1;
randominit();
int test_uncoded = 0;
n_iter_stats_t dec_iter[400] = {0};
short BG = 0, Zc;
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
exit_fun("[LDPCTEST] Error, configuration module init failed\n");
}
logInit();
while ((c = getopt(argc, argv, "--:O:q:r:s:S:l:G:n:d:i:t:u:hv:")) != -1) {
while ((c = getopt(argc, argv, "--:O:q:r:s:S:l:Gn:d:i:t:u:hv:g:Pn:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
/* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */
if (c == 1 || c == '-' || c == 'O')
@@ -454,12 +520,9 @@ int main(int argc, char *argv[])
Kprime = atoi(optarg);
break;
case 'G':
ldpc_version = "_cuda";
break;
case 'n':
n_trials = atoi(optarg);
AssertFatal(n_trials < MAX_TRIALS, "max trials %d, please increase MAX_TRIALS\n", MAX_TRIALS);
break;
case 's':
@@ -484,6 +547,11 @@ int main(int argc, char *argv[])
case 'v':
ldpc_version = strdup(optarg);
break;
case 'g':
gen_code = atoi(optarg);
AssertFatal(gen_code <= 4, "gen_code %d is not allowed\n", gen_code);
n_trials = 0;
break;
case 'h':
default:
printf("CURRENTLY SUPPORTED CODE RATES: \n");
@@ -494,7 +562,7 @@ int main(int argc, char *argv[])
printf("-r Nominator rate, (1, 2, 22), Default: 1\n");
printf("-d Denominator rate, (3, 5, 25), Default: 1\n");
printf("-l Length of payload bits in a segment (K' in 38.212-5.2.2), [1, 8448], Default: 8448\n");
printf("-G give 1 to run cuda for LDPC, Default: 0\n");
printf("-P give 1 to run cuda stream parallel for LDPC, Default: 0\n");
printf("-n Number of simulation trials, Default: 1\n");
// printf("-M MCS2 for TB 2\n");
printf("-s SNR per information bit (EbNo) in dB, Default: -2\n");
@@ -511,11 +579,29 @@ int main(int argc, char *argv[])
printf("n_trials %d: \n", n_trials);
printf("SNR0 %f: \n", SNR0);
printf("loading %s\n",ldpc_version);
load_LDPClib(ldpc_version, &ldpc_toCompare);
load_LDPClib("_orig", &ldpc_orig);
// find minimum value in all sets of lifting size
Zc = 0;
#ifdef LDPC_CUDA
cudaError_t err = cudaHostAlloc((void**)&estimated_output,sizeof(uint8_t)* n_segments * Kprime,cudaHostAllocMapped);
if (err != cudaSuccess) {
printf("[DEBUG] CUDA Error String: %s\n", cudaGetErrorString(err));
}
AssertFatal(err==cudaSuccess,"estimated_output n_segments %d Kprime %d\n",n_segments,Kprime);
err = cudaHostAlloc((void**)&channel_output_fixed,sizeof(int8_t)* n_segments * 68 * 384,cudaHostAllocMapped);
AssertFatal(err==cudaSuccess,"channel_output_fixed n_segments %d\n",n_segments);
err = cudaHostGetDevicePointer((void**)&estimated_output_dev,estimated_output,0);
AssertFatal(err==cudaSuccess,"estimated_output_dev\n");
err = cudaHostGetDevicePointer((void**)&channel_output_fixed_dev,channel_output_fixed,0);
AssertFatal(err==cudaSuccess,"channel_output_fixed_dev\n");
printf("estimated_output_dev %p, channel_output_fixed_dev %p\n",estimated_output_dev,channel_output_fixed_dev);
#else
estimated_output = malloc(n_segments * Kprime);
channel_output_fixed=malloc(n_segments * 68 * 384);
#endif
char fname[200];
sprintf(fname, "ldpctest_BG_%d_Zc_%d_rate_%d-%d_Kprime_%d_maxit_%d.txt", BG, Zc, nom_rate, denom_rate, Kprime, max_iterations);
@@ -532,7 +618,7 @@ int main(int argc, char *argv[])
SNR_lin = pow(10, SNR / 10.0);
else
SNR_lin = pow(10, SNR / 10.0) * nom_rate / denom_rate;
printf("Linear SNR: %f\n", SNR_lin);
printf("Linear SNR: %d %f, %d/%d, Kprime %d\n", max_iterations, SNR_lin, nom_rate,denom_rate,Kprime);
one_measurement_t res = test_ldpc(max_iterations,
nom_rate,
denom_rate,
@@ -540,13 +626,31 @@ int main(int argc, char *argv[])
qbits,
Kprime, // block length bytes
n_trials,
n_segments);
n_segments,
gen_code);
decoded_errors[i] = res.errors;
dec_iter[i] = res.dec_iter;
dec_iter[i].snr = SNR;
dec_iter[i].ber = (float)res.errors_bit / (float)n_trials / (float)Kprime / (double)n_segments;
dec_iter[i].bler = (float)decoded_errors[i] / (float)n_trials;
printf("Failure Mask = ");
for(int i=0; i<n_trials;i++){
int flag = 0;
for(int j = 0; j < n_segments; j++){
if(Failure_Mask[i][j] == 1){
if(flag == 0){
printf(" %d: ", i);
flag = 1;
}
printf(" %d ", j);
Failure_Mask[i][j] = 0;
}
}
if(flag == 1) printf(",");
}
printf("\n");
printf("SNR %f, BLER %f (%u/%d)\n", SNR, dec_iter[i].bler, decoded_errors[i], n_trials);
printf("SNR %f, BER %f (%u/%d)\n", SNR, dec_iter[i].ber, decoded_errors[i], n_trials);
printf("SNR %f, Uncoded BER %f (%u/%d)\n",
@@ -560,7 +664,9 @@ int main(int argc, char *argv[])
printf("\n");
double cpu_freq = get_cpu_freq_GHz();
printf("CPU Freq %f\n",cpu_freq);
time_stats_t *t_optim = &res.time_optim;
printf("Encoding time mean: %15.3f us\n", (double)t_optim->diff / t_optim->trials / 1000.0 / cpu_freq);
printf("Encoding time std: %15.3f us\n",
sqrt((double)t_optim->diff_square / t_optim->trials / pow(1000, 2) / pow(cpu_freq, 2)
@@ -569,12 +675,12 @@ int main(int argc, char *argv[])
printf("\n");
time_stats_t *t_decoder = &res.time_decoder;
printf("Decoding time mean: %15.3f us\n", (double)t_decoder->diff / t_decoder->trials / 1000.0 / cpu_freq);
printf("Decoding time mean (per segment)");
printf(": %15.3f us\n", (double)t_decoder->diff / t_decoder->trials / 1000.0 / cpu_freq);
printf("Decoding time std: %15.3f us\n",
sqrt((double)t_decoder->diff_square / t_decoder->trials / pow(1000, 2) / pow(cpu_freq, 2)
- pow((double)t_decoder->diff / t_decoder->trials / 1000.0 / cpu_freq, 2)));
printf("Decoding time max: %15.3f us\n", (double)t_decoder->max / 1000.0 / cpu_freq);
fprintf(fd,
"%f %f %f %f %f %f %f %f %f %f %f %f %d \n",
SNR,

View File

@@ -2,3 +2,7 @@
add_subdirectory(nrLDPC_coding_segment)
add_subdirectory(nrLDPC_coding_aal)
add_boolean_option(ENABLE_LDPC_CUDA OFF "Build support for CUDA" OFF)
if (ENABLE_LDPC_CUDA)
add_subdirectory(nrLDPC_coding_cuda)
endif()

View File

@@ -458,8 +458,9 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs,
bool large_input = false;
int j = 0;
for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) {
for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) {
uint32_t data_len = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E;
nrLDPC_TB_decoding_parameters_t *p = &nrLDPC_slot_decoding_parameters->TBs[h];
for (int i = 0; i < p->C; ++i) {
uint32_t data_len = i < p->first_rE2 ? p->E : p->E2;
char *data;
struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool);
AssertFatal(m_head != NULL,
@@ -602,20 +603,19 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops,
bool special_case_tb_mode = !active_dev.is_t2 && (nrLDPC_slot_decoding_parameters->nb_TBs == 1)
&& (nb_segments_decoding(nrLDPC_slot_decoding_parameters) == 1);
for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) {
for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) {
ops[j]->ldpc_dec.basegraph = nrLDPC_slot_decoding_parameters->TBs[h].BG;
ops[j]->ldpc_dec.z_c = nrLDPC_slot_decoding_parameters->TBs[h].Z;
ops[j]->ldpc_dec.q_m = nrLDPC_slot_decoding_parameters->TBs[h].Qm;
ops[j]->ldpc_dec.n_filler = nrLDPC_slot_decoding_parameters->TBs[h].F;
ops[j]->ldpc_dec.n_cb = (nrLDPC_slot_decoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_decoding_parameters->TBs[h].Z)
: (50 * nrLDPC_slot_decoding_parameters->TBs[h].Z);
ops[j]->ldpc_dec.iter_max = nrLDPC_slot_decoding_parameters->TBs[h].max_ldpc_iterations;
ops[j]->ldpc_dec.rv_index = nrLDPC_slot_decoding_parameters->TBs[h].rv_index;
nrLDPC_TB_decoding_parameters_t *p = &nrLDPC_slot_decoding_parameters->TBs[h];
for (int i = 0; i < p->C; ++i) {
ops[j]->ldpc_dec.basegraph = p->BG;
ops[j]->ldpc_dec.z_c = p->Z;
ops[j]->ldpc_dec.q_m = p->Qm;
ops[j]->ldpc_dec.n_filler = p->F;
ops[j]->ldpc_dec.n_cb = (p->BG == 1) ? (66 * p->Z) : (50 * p->Z);
ops[j]->ldpc_dec.iter_max = p->max_ldpc_iterations;
ops[j]->ldpc_dec.rv_index = p->rv_index;
ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE;
if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) {
*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared = false;
if (p->d_to_be_cleared) {
if (active_dev.is_t2)
*nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0;
*p->processedSegments = 0;
} else {
ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE;
if (active_dev.support_internal_harq_memory) {
@@ -625,12 +625,12 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops,
}
if (!active_dev.is_t2)
*nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0;
*p->processedSegments = 0;
if (!special_case_tb_mode) {
ops[j]->ldpc_dec.code_block_mode = 1;
ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E;
if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) {
ops[j]->ldpc_dec.cb_params.e = i < p->first_rE2 ? p->E : p->E2;
if (p->C > 1) {
ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP;
ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK;
}
@@ -646,12 +646,12 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops,
ops[j]->ldpc_dec.tb_params.c = 1;
ops[j]->ldpc_dec.tb_params.r = 0;
ops[j]->ldpc_dec.tb_params.cab = 1;
ops[j]->ldpc_dec.tb_params.ea = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E;
ops[j]->ldpc_dec.tb_params.eb = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E;
ops[j]->ldpc_dec.tb_params.ea = i < p->first_rE2 ? p->E : p->E2;
ops[j]->ldpc_dec.tb_params.eb = i < p->first_rE2 ? p->E : p->E2;
}
// 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;
uint32_t segment_offset = (p->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
@@ -673,6 +673,7 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops,
ops[j]->ldpc_dec.input = inputs[j];
++j;
}
p->d_to_be_cleared = false;
}
}
@@ -729,14 +730,16 @@ static int retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decod
{
int j = 0;
for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) {
for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) {
nrLDPC_TB_decoding_parameters_t *p = &nrLDPC_slot_decoding_parameters->TBs[h];
size_t data_off = 0;
for (int i = 0; i < p->C; ++i) {
struct rte_bbdev_op_data *hard_output = &ops[j]->ldpc_dec.hard_output;
struct rte_mbuf *m = hard_output->data;
uint16_t data_len = rte_pktmbuf_data_len(m) - hard_output->offset;
uint8_t *data = rte_pktmbuf_mtod_offset(m, uint8_t *, hard_output->offset);
memcpy(nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c, data, data_len);
memcpy(p->c + data_off, data, data_len);
uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i;
uint32_t segment_offset = (p->harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i;
uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock;
struct rte_bbdev_op_data *harq_output = &ops[j]->ldpc_dec.harq_combined_output;
if (!active_dev.support_internal_harq_memory) {
@@ -749,6 +752,7 @@ static int retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decod
active_dev.harq_buffers[pruned_segment_offset].offset = harq_output->offset;
active_dev.harq_buffers[pruned_segment_offset].length = harq_output->length;
++j;
data_off += p->K >> 3;
}
}
return 0;
@@ -823,7 +827,7 @@ static int pmd_lcore_ldpc_dec(void *arg)
// Start timer
// We report timing only once in (0,0) since the timers are merged at the end
start_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode);
start_meas(&nrLDPC_slot_decoding_parameters->TBs[0].ts_ldpc_decode);
uint16_t enq = 0, deq = 0;
while (enq < num_segments) {
@@ -840,7 +844,7 @@ static int pmd_lcore_ldpc_dec(void *arg)
// Stop timer
// We report timing only once in (0,0) since the timers are merged at the end
stop_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode);
stop_meas(&nrLDPC_slot_decoding_parameters->TBs[0].ts_ldpc_decode);
if (deq == enq) {
ret = retrieve_ldpc_dec_op(ops_deq, nrLDPC_slot_decoding_parameters);
@@ -849,24 +853,24 @@ static int pmd_lcore_ldpc_dec(void *arg)
/* get the max of iter_count for all dequeued ops */
int j = 0;
for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) {
for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) {
bool *status = &nrLDPC_slot_decoding_parameters->TBs[h].segments[i].decodeSuccess;
nrLDPC_TB_decoding_parameters_t *p = &nrLDPC_slot_decoding_parameters->TBs[h];
for (int i = 0; i < p->C; ++i) {
bool *status = &p->decodeSuccess[i];
tp->iter_count = RTE_MAX(ops_enq[j]->ldpc_dec.iter_count, tp->iter_count);
// Check if CRC is available otherwise rely on ops_enq[j]->status to detect decoding success
// CRC is NOT available if the CRC type is 24_B which is when C is greater than 1
if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) {
if (p->C > 1) {
*status = (ops_enq[j]->status == 0);
} else {
uint8_t *decoded_bytes = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c;
uint8_t crc_type = crcType(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A);
uint32_t len_with_crc = lenWithCrc(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A);
uint8_t *decoded_bytes = p->c;
uint8_t crc_type = crcType(p->C, p->A);
uint32_t len_with_crc = lenWithCrc(p->C, p->A);
*status = check_crc(decoded_bytes, len_with_crc, crc_type);
}
if (*status) {
*nrLDPC_slot_decoding_parameters->TBs[h].processedSegments =
*nrLDPC_slot_decoding_parameters->TBs[h].processedSegments + 1;
*p->processedSegments = *p->processedSegments + 1;
}
++j;
}
@@ -1057,7 +1061,7 @@ static int normalize_dpdk_dev(const char *input, char *output, size_t out_len)
}
// OAI CODE
int32_t nrLDPC_coding_init()
int32_t nrLDPC_coding_init(int max_num_pxsch)
{
pthread_mutex_init(&encode_mutex, NULL);
pthread_mutex_init(&decode_mutex, NULL);
@@ -1283,28 +1287,24 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec
int8_t llr_decimal = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_decimals;
int offset = 0;
for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) {
for (int r = 0; r < nrLDPC_slot_decoding_parameters->TBs[h].C; r++) {
nrLDPC_TB_decoding_parameters_t *p = &nrLDPC_slot_decoding_parameters->TBs[h];
size_t data_off = 0;
for (int r = 0; r < p->C; r++) {
uint32_t E = r < p->first_rE2 ? p->E : p->E2;
if (active_dev.is_t2) {
// For the T2, we simply saturate the LLRs.
uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16)));
memcpy(z_ol,
nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr,
nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t));
memcpy(z_ol, p->llr + data_off, E * sizeof(uint16_t));
simde__m128i *pv_ol128 = (simde__m128i *)z_ol;
simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset];
for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) {
for (int i = 0, j = 0; j < ((E + 15) >> 4); i += 2, j++) {
pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]);
}
} else {
llr_scaling(nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr,
nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E,
&l_ol[offset],
llr_size,
llr_decimal,
nrLDPC_slot_decoding_parameters->TBs[h].nb_layers,
nrLDPC_slot_decoding_parameters->TBs[h].Qm);
llr_scaling(p->llr + data_off, E, &l_ol[offset], llr_size, llr_decimal, p->nb_layers, p->Qm);
}
offset += LDPC_MAX_CB_SIZE;
data_off += E;
}
}

View File

@@ -0,0 +1,71 @@
enable_language(CUDA)
# it would be possible to auto-detect compute capailities: https://stackoverflow.com/a/68223399
#include(FindCUDA/select_compute_arch)
#CUDA_DETECT_INSTALLED_GPUS(INSTALLED_GPU_CCS_1)
# Nvidia recommends to build against all targeted archs: https://forums.developer.nvidia.com/t/261162
# this is for cmake 3.16 (OAI minimum), starting at cmake 3.18 there is another syntax, see below
SET(CUDA_VERBOSE_BUILD ON)
# version using NVLink-C2C in Grace Hopper
add_library(ldpc_cuda MODULE
# main entry
nrLDPC_coding_cuda.c
nrLDPC_decoder_cuda.c
ldpc_encoder_cuda32.c
ldpc_input32.c
nrLDPC_coding_segment_encoder_cuda.c
nrLDPC_coding_segment_decoder_cuda.c
# .cu files
nrLDPC_decoder_BG1_cuda.cu
ldpc_BG1_Zc384_32bit.cu
ldpc_input.cu
ldpc_deinterleave.cu
ldpc_rate_matching.cu
)
set_target_properties(ldpc_cuda PROPERTIES CUDA_ARCHITECTURES "80;87;90")
# we need to find the right include directory, but ti seems that
# enable_language(CUDA) does not make this available(?), so also use the CUDA package
find_package(CUDA REQUIRED)
target_include_directories(ldpc_cuda PRIVATE ${CUDA_INCLUDE_DIRS})
target_link_libraries(ldpc_cuda ldpc_gen_HEADERS log_headers)
set_target_properties(ldpc_cuda PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
set_target_properties(ldpc_cuda PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_dependencies(ldpctest ldpc_cuda)
add_dependencies(nr-softmodem ldpc_cuda)
add_dependencies(nr-uesoftmodem ldpc_cuda)
add_dependencies(nr_ulsim ldpc_cuda)
add_dependencies(nr_ulschsim ldpc_cuda)
add_dependencies(nr_dlsim ldpc_cuda)
add_dependencies(nr_dlschsim ldpc_cuda)
if(ENABLE_PHYSIM_TESTS)
# this is a list of physical simulators designed to test the functioning of
# ldpc_cuda. Of course, one could also modify
# openair1/SIMULATION/tests/CMakeLists.txt to run them with ldpc_cuda
add_physim_test(ldpctest.ldpc_cuda.simple "ldpctest: simple CUDA load check" ldpctest -v _cuda -l8448 -s10 -n300)
add_physim_test(ldpctest.ldpc_cuda.r13 "ldpctest: base graph 1, rate 1/3" ldpctest -v _cuda -r1 -n300 -S64 -s4 -i4)
add_physim_test(ldpctest.ldpc_cuda.r23 "ldpctest: base graph 1, rate 2/3" ldpctest -v _cuda -r2 -n300 -S64 -s4 -i4)
add_physim_test(ldpctest.ldpc_cuda.r89 "ldpctest: base graph 1, rate 8/9" ldpctest -v _cuda -r22 -d25 -n300 -S64 -s4 -i4)
add_physim_test(nr_dlsim.ldpc_cuda.9seg_r13 "nr_dlsim: 9 segments, rate 1/3" nr_dlsim -n100 -s20 -e13 -R273 -b273 -x1 -y1 -z1 -P --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_dlsim.ldpc_cuda.18seg_r23 "nr_dlsim: 18 segments, rate 2/3" nr_dlsim -n100 -s20 -e22 -R273 -b273 -x1 -y1 -z1 -P --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_dlsim.ldpc_cuda.50seg_r89 "nr_dlsim: 50 segments, rate 8/9" nr_dlsim -n100 -s40 -e27 -R273 -b273 -z2 -x2 -y2 -P --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_dlsim.ldpc_cuda.2t2r_mcs25_64qam "nr_dlsim: 2T2R/2 layers, MCS 25, 64QAM, 100 MHz 30 kHz SCS)" nr_dlsim -n100 -R273 -b273 -P -e 25 -s30 -x2 -y2 -z2 --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_dlsim.ldpc_cuda.1t1r_mcs25_256qam "nr_dlsim: 1T1R/1 layer, MCS 25, 256QAM, 100 MHz 30 kHz SCS)" nr_dlsim -n100 -R273 -b273 -P -e 25 -s30 -q 1 --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_dlsim.ldpc_cuda.1t1r_mcs25_256qam_bw "nr_dlsim: 1T1R/1 layer, MCS 25 256QAM, 40 MHz 30 kHz SCS" nr_dlsim -n100 -R106 -b106 -P -e 25 -s30 -q 1 --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_ulsim.ldpc_cuda.9seg_r13 "nr_ulsim: 9 segments, rate 1/3" nr_ulsim -n100 -s20 -m13 -R273 -r273 -W1 -y1 -z1 -P --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_ulsim.ldpc_cuda.18seg_r23 "nr_ulsim: 18 segments, rate 2/3" nr_ulsim -n100 -s20 -m22 -R273 -r273 -W1 -y1 -z1 -P --loader.ldpc.shlibversion _cuda)
add_physim_test(nr_ulsim.ldpc_cuda.50seg_r89 "nr_ulsim: 50 segments, rate 8/9" nr_ulsim -n100 -s40 -m27 -R273 -r273 -W2 -I9 -y2 -z2 -P --loader.ldpc.shlibversion _cuda)
endif()

View File

@@ -0,0 +1,216 @@
#include <stdio.h>
#include <stdint.h>
#include <cuda_runtime.h>
// generated code for Zc=384, byte encoding
__global__ void ldpc_BG1_Zc384_worker(uint32_t *c[4],uint32_t *d[4]) {
uint32_t *c32=c[blockIdx.x];
uint32_t *d32=d[blockIdx.x]+8448-2*384;
int i2 = threadIdx.x;
int i1 = blockIdx.y;
// copy 20 c values to d
if (i1<20) d[blockIdx.x][(i1*384) + i2] = c32[4*384+(2*384*i1)+i2];
if (i2 < 384) {
c32+=i2;
d32+=i2;
switch(i1) {
case 0:
d32[0]=c32[307]^c32[76]^c32[205]^c32[276]^c32[787]^c32[1018]^c32[855]^c32[1586]^c32[1612]^c32[1864]^c32[2673]^c32[2377]^c32[2304]^c32[3360]^c32[3404]^c32[3347]^c32[4021]^c32[3984]^c32[4096]^c32[4824]^c32[4769]^c32[4807]^c32[5707]^c32[5643]^c32[5529]^c32[6475]^c32[6304]^c32[6200]^c32[7229]^c32[7090]^c32[6975]^c32[7968]^c32[7809]^c32[7812]^c32[8557]^c32[8743]^c32[8753]^c32[9233]^c32[9558]^c32[9447]^c32[10341]^c32[10184]^c32[10325]^c32[10969]^c32[10840]^c32[10964]^c32[11735]^c32[11619]^c32[11573]^c32[12394]^c32[12642]^c32[12592]^c32[13170]^c32[13187]^c32[13356]^c32[14066]^c32[14064]^c32[14095]^c32[14772]^c32[14923]^c32[14797]^c32[15690]^c32[15373]^c32[15399]^c32[16474]^c32[16240]^c32[16485];
break;
case 1:
d32[384]=c32[307]^c32[308]^c32[77]^c32[206]^c32[277]^c32[787]^c32[788]^c32[1019]^c32[856]^c32[1586]^c32[1587]^c32[1613]^c32[1865]^c32[2673]^c32[2674]^c32[2378]^c32[2305]^c32[3361]^c32[3405]^c32[3348]^c32[4021]^c32[4022]^c32[3985]^c32[4097]^c32[4824]^c32[4825]^c32[4770]^c32[4808]^c32[5708]^c32[5644]^c32[5530]^c32[6476]^c32[6305]^c32[6201]^c32[7229]^c32[7230]^c32[7091]^c32[6976]^c32[7968]^c32[7969]^c32[7810]^c32[7813]^c32[8557]^c32[8558]^c32[8744]^c32[8754]^c32[9233]^c32[9234]^c32[9559]^c32[9448]^c32[10341]^c32[10342]^c32[10185]^c32[10326]^c32[10970]^c32[10841]^c32[10965]^c32[11735]^c32[11736]^c32[11620]^c32[11574]^c32[12394]^c32[12395]^c32[12643]^c32[12593]^c32[13171]^c32[13188]^c32[13357]^c32[14066]^c32[14067]^c32[14065]^c32[14096]^c32[14772]^c32[14773]^c32[14924]^c32[14798]^c32[15690]^c32[15691]^c32[15374]^c32[15400]^c32[16474]^c32[16475]^c32[16241]^c32[16486];
break;
case 2:
d32[768]=c32[308]^c32[77]^c32[205]^c32[206]^c32[276]^c32[277]^c32[788]^c32[1018]^c32[1019]^c32[855]^c32[856]^c32[1587]^c32[1613]^c32[1864]^c32[1865]^c32[2674]^c32[2378]^c32[2304]^c32[2305]^c32[3361]^c32[3404]^c32[3405]^c32[3347]^c32[3348]^c32[4022]^c32[3985]^c32[4096]^c32[4097]^c32[4825]^c32[4769]^c32[4770]^c32[4807]^c32[4808]^c32[5708]^c32[5643]^c32[5644]^c32[5529]^c32[5530]^c32[6476]^c32[6304]^c32[6305]^c32[6200]^c32[6201]^c32[7230]^c32[7091]^c32[6975]^c32[6976]^c32[7969]^c32[7809]^c32[7810]^c32[7812]^c32[7813]^c32[8558]^c32[8744]^c32[8753]^c32[8754]^c32[9234]^c32[9559]^c32[9447]^c32[9448]^c32[10342]^c32[10184]^c32[10185]^c32[10325]^c32[10326]^c32[10970]^c32[10840]^c32[10841]^c32[10964]^c32[10965]^c32[11736]^c32[11620]^c32[11573]^c32[11574]^c32[12395]^c32[12643]^c32[12592]^c32[12593]^c32[13171]^c32[13187]^c32[13188]^c32[13356]^c32[13357]^c32[14067]^c32[14064]^c32[14065]^c32[14095]^c32[14096]^c32[14773]^c32[14924]^c32[14797]^c32[14798]^c32[15691]^c32[15373]^c32[15374]^c32[15399]^c32[15400]^c32[16475]^c32[16241]^c32[16485]^c32[16486];
break;
case 3:
d32[1152]=c32[308]^c32[77]^c32[206]^c32[276]^c32[277]^c32[788]^c32[1019]^c32[855]^c32[856]^c32[1587]^c32[1613]^c32[1865]^c32[2674]^c32[2378]^c32[2304]^c32[2305]^c32[3361]^c32[3405]^c32[3347]^c32[3348]^c32[4022]^c32[3985]^c32[4097]^c32[4825]^c32[4770]^c32[4807]^c32[4808]^c32[5708]^c32[5644]^c32[5529]^c32[5530]^c32[6476]^c32[6305]^c32[6200]^c32[6201]^c32[7230]^c32[7091]^c32[6976]^c32[7969]^c32[7810]^c32[7812]^c32[7813]^c32[8558]^c32[8744]^c32[8753]^c32[8754]^c32[9234]^c32[9559]^c32[9447]^c32[9448]^c32[10342]^c32[10185]^c32[10325]^c32[10326]^c32[10970]^c32[10841]^c32[10964]^c32[10965]^c32[11736]^c32[11620]^c32[11574]^c32[12395]^c32[12643]^c32[12592]^c32[12593]^c32[13171]^c32[13188]^c32[13356]^c32[13357]^c32[14067]^c32[14065]^c32[14095]^c32[14096]^c32[14773]^c32[14924]^c32[14798]^c32[15691]^c32[15374]^c32[15399]^c32[15400]^c32[16475]^c32[16241]^c32[16485]^c32[16486];
break;
case 4:
d32[1536]=c32[332]^c32[949];
break;
case 5:
d32[1920]=c32[80]^c32[233]^c32[362]^c32[49]^c32[195]^c32[944]^c32[791]^c32[1012]^c32[782]^c32[1743]^c32[1769]^c32[1637]^c32[2446]^c32[2534]^c32[2461]^c32[2419]^c32[3133]^c32[3177]^c32[3120]^c32[4178]^c32[4141]^c32[3869]^c32[4981]^c32[4926]^c32[4964]^c32[5480]^c32[5416]^c32[5686]^c32[6248]^c32[6461]^c32[6357]^c32[7002]^c32[7247]^c32[7132]^c32[7741]^c32[7966]^c32[7969]^c32[8714]^c32[8516]^c32[8526]^c32[9390]^c32[9331]^c32[9220]^c32[9382]^c32[10114]^c32[10341]^c32[10098]^c32[11126]^c32[10997]^c32[11121]^c32[11892]^c32[11776]^c32[11730]^c32[12551]^c32[12415]^c32[12365]^c32[12529]^c32[13327]^c32[13344]^c32[13129]^c32[13839]^c32[13837]^c32[13868]^c32[14929]^c32[14696]^c32[14954]^c32[15463]^c32[15530]^c32[15556]^c32[16247]^c32[16397]^c32[16258]^c32[16179];
break;
case 6:
d32[2304]=c32[278]^c32[4865]^c32[7681]^c32[8799]^c32[10076]^c32[13309]^c32[13842]^c32[15585];
break;
case 7:
d32[2688]=c32[9]^c32[830]^c32[3388]^c32[5709]^c32[6434]^c32[10866];
break;
case 8:
d32[3072]=c32[374]^c32[94]^c32[143]^c32[247]^c32[272]^c32[375]^c32[376]^c32[343]^c32[62]^c32[63]^c32[307]^c32[854]^c32[958]^c32[1085]^c32[804]^c32[805]^c32[922]^c32[1025]^c32[1026]^c32[947]^c32[1653]^c32[1757]^c32[1679]^c32[1783]^c32[1547]^c32[1650]^c32[1651]^c32[2356]^c32[2460]^c32[2444]^c32[2548]^c32[2371]^c32[2474]^c32[2475]^c32[2469]^c32[3427]^c32[3147]^c32[3087]^c32[3190]^c32[3191]^c32[3414]^c32[3133]^c32[3134]^c32[4088]^c32[4192]^c32[4051]^c32[4155]^c32[4163]^c32[3882]^c32[3883]^c32[4891]^c32[4611]^c32[4836]^c32[4939]^c32[4940]^c32[4874]^c32[4977]^c32[4978]^c32[5390]^c32[5494]^c32[5710]^c32[5429]^c32[5430]^c32[5596]^c32[5699]^c32[5700]^c32[6158]^c32[6262]^c32[6371]^c32[6474]^c32[6475]^c32[6267]^c32[6370]^c32[6371]^c32[6912]^c32[7016]^c32[7157]^c32[7261]^c32[7042]^c32[7145]^c32[7146]^c32[8035]^c32[7755]^c32[7876]^c32[7979]^c32[7980]^c32[7879]^c32[7982]^c32[7983]^c32[8624]^c32[8728]^c32[8810]^c32[8530]^c32[8820]^c32[8539]^c32[8540]^c32[9300]^c32[9404]^c32[9241]^c32[9345]^c32[9514]^c32[9233]^c32[9234]^c32[9234]^c32[10024]^c32[10128]^c32[10251]^c32[10354]^c32[10355]^c32[10008]^c32[10111]^c32[10112]^c32[11036]^c32[10756]^c32[10907]^c32[11010]^c32[11011]^c32[11031]^c32[11134]^c32[11135]^c32[11802]^c32[11522]^c32[11686]^c32[11790]^c32[11640]^c32[11743]^c32[11744]^c32[12461]^c32[12565]^c32[12325]^c32[12429]^c32[12659]^c32[12378]^c32[12379]^c32[12327]^c32[13237]^c32[13341]^c32[13254]^c32[13357]^c32[13358]^c32[13423]^c32[13142]^c32[13143]^c32[14133]^c32[13853]^c32[14131]^c32[13850]^c32[13851]^c32[14162]^c32[13881]^c32[13882]^c32[14839]^c32[14943]^c32[14606]^c32[14710]^c32[14864]^c32[14967]^c32[14968]^c32[14816]^c32[15373]^c32[15477]^c32[15440]^c32[15543]^c32[15544]^c32[15466]^c32[15569]^c32[15570]^c32[16157]^c32[16261]^c32[16307]^c32[16411]^c32[16168]^c32[16271]^c32[16272]^c32[16496];
break;
case 9:
d32[3456]=c32[366]^c32[1000]^c32[8001]^c32[8581]^c32[10041]^c32[13359]^c32[13887]^c32[15442];
break;
case 10:
d32[3840]=c32[869]^c32[1875]^c32[3346]^c32[5487]^c32[6527]^c32[11106];
break;
case 11:
d32[4224]=c32[257]^c32[38]^c32[39]^c32[26]^c32[192]^c32[155]^c32[321]^c32[226]^c32[8]^c32[48]^c32[1121]^c32[902]^c32[903]^c32[968]^c32[1134]^c32[805]^c32[971]^c32[870]^c32[1536]^c32[1701]^c32[1702]^c32[1562]^c32[1728]^c32[1814]^c32[1596]^c32[2623]^c32[2404]^c32[2405]^c32[2327]^c32[2493]^c32[2638]^c32[2420]^c32[3310]^c32[3092]^c32[3354]^c32[3136]^c32[3297]^c32[3079]^c32[3971]^c32[4136]^c32[4137]^c32[3934]^c32[4100]^c32[4046]^c32[4212]^c32[4774]^c32[4939]^c32[4940]^c32[4719]^c32[4885]^c32[4757]^c32[4923]^c32[5657]^c32[5439]^c32[5593]^c32[5759]^c32[5479]^c32[5645]^c32[6425]^c32[6207]^c32[6254]^c32[6420]^c32[6150]^c32[6316]^c32[7179]^c32[6960]^c32[6961]^c32[7040]^c32[7206]^c32[6925]^c32[7091]^c32[7918]^c32[7699]^c32[7700]^c32[7759]^c32[7925]^c32[7762]^c32[7928]^c32[8507]^c32[8672]^c32[8673]^c32[8693]^c32[8475]^c32[8703]^c32[8485]^c32[9567]^c32[9348]^c32[9349]^c32[9508]^c32[9290]^c32[9397]^c32[9563]^c32[9224]^c32[10291]^c32[10072]^c32[10073]^c32[10134]^c32[10300]^c32[10275]^c32[10057]^c32[10919]^c32[11085]^c32[10790]^c32[10956]^c32[10914]^c32[11080]^c32[11685]^c32[11850]^c32[11851]^c32[11569]^c32[11735]^c32[11523]^c32[11689]^c32[12344]^c32[12509]^c32[12510]^c32[12592]^c32[12374]^c32[12542]^c32[12324]^c32[12335]^c32[13120]^c32[13286]^c32[13137]^c32[13303]^c32[13306]^c32[13088]^c32[14016]^c32[14181]^c32[14182]^c32[14014]^c32[14180]^c32[14045]^c32[13827]^c32[14722]^c32[14887]^c32[14888]^c32[14873]^c32[14655]^c32[14747]^c32[14913]^c32[15640]^c32[15421]^c32[15422]^c32[15707]^c32[15489]^c32[15733]^c32[15515]^c32[16424]^c32[16205]^c32[16206]^c32[16190]^c32[16356]^c32[16435]^c32[16217]^c32[16316];
break;
case 12:
d32[4608]=c32[77]^c32[954]^c32[7854]^c32[8680]^c32[10034]^c32[13898];
break;
case 13:
d32[4992]=c32[293]^c32[294]^c32[63]^c32[192]^c32[263]^c32[313]^c32[773]^c32[774]^c32[1005]^c32[842]^c32[1572]^c32[1573]^c32[1599]^c32[1851]^c32[2659]^c32[2660]^c32[2364]^c32[2675]^c32[2481]^c32[3347]^c32[3391]^c32[3334]^c32[4007]^c32[4008]^c32[3971]^c32[4083]^c32[4810]^c32[4811]^c32[4756]^c32[4794]^c32[5694]^c32[5630]^c32[5516]^c32[5642]^c32[6462]^c32[6291]^c32[6187]^c32[7215]^c32[7216]^c32[7077]^c32[6962]^c32[7954]^c32[7955]^c32[7796]^c32[7799]^c32[8543]^c32[8544]^c32[8730]^c32[8740]^c32[9219]^c32[9220]^c32[9545]^c32[9434]^c32[10327]^c32[10328]^c32[10171]^c32[10312]^c32[10956]^c32[10827]^c32[10951]^c32[11721]^c32[11722]^c32[11606]^c32[11560]^c32[12380]^c32[12381]^c32[12629]^c32[12579]^c32[13157]^c32[13174]^c32[13343]^c32[14052]^c32[14053]^c32[14051]^c32[14082]^c32[14758]^c32[14759]^c32[14910]^c32[14784]^c32[15676]^c32[15677]^c32[15360]^c32[15386]^c32[15475]^c32[16460]^c32[16461]^c32[16227]^c32[16472];
break;
case 14:
d32[5376]=c32[142]^c32[9464]^c32[11657]^c32[12377]^c32[13403]^c32[16140];
break;
case 15:
d32[5760]=c32[193]^c32[346]^c32[91]^c32[161]^c32[162]^c32[241]^c32[1057]^c32[904]^c32[1124]^c32[1125]^c32[770]^c32[1856]^c32[1882]^c32[1750]^c32[2559]^c32[2647]^c32[2573]^c32[2574]^c32[3246]^c32[3290]^c32[3232]^c32[3233]^c32[3907]^c32[3870]^c32[3982]^c32[4710]^c32[4655]^c32[4692]^c32[4693]^c32[5593]^c32[5529]^c32[5414]^c32[5415]^c32[6361]^c32[6190]^c32[6469]^c32[6470]^c32[7115]^c32[6976]^c32[7245]^c32[7854]^c32[7695]^c32[7697]^c32[7698]^c32[7890]^c32[8827]^c32[8629]^c32[8638]^c32[8639]^c32[9503]^c32[9444]^c32[9332]^c32[9333]^c32[10227]^c32[10070]^c32[10210]^c32[10211]^c32[10302]^c32[10855]^c32[11110]^c32[10849]^c32[10850]^c32[11621]^c32[11889]^c32[11843]^c32[12664]^c32[12528]^c32[12477]^c32[12478]^c32[13056]^c32[13073]^c32[13241]^c32[13242]^c32[13952]^c32[13950]^c32[13980]^c32[13981]^c32[13879]^c32[14658]^c32[14809]^c32[14683]^c32[15576]^c32[15643]^c32[15668]^c32[15669]^c32[16360]^c32[16510]^c32[16370]^c32[16371];
break;
case 16:
d32[6144]=c32[364]^c32[133]^c32[262]^c32[333]^c32[844]^c32[1075]^c32[912]^c32[781]^c32[1643]^c32[1669]^c32[1537]^c32[2346]^c32[2434]^c32[2361]^c32[2642]^c32[3417]^c32[3077]^c32[3404]^c32[4078]^c32[4041]^c32[4153]^c32[4881]^c32[4826]^c32[4864]^c32[5380]^c32[5700]^c32[5586]^c32[6148]^c32[6361]^c32[6257]^c32[7286]^c32[7147]^c32[7032]^c32[8025]^c32[7866]^c32[7869]^c32[8614]^c32[8800]^c32[8810]^c32[8505]^c32[9290]^c32[9231]^c32[9504]^c32[10014]^c32[10241]^c32[9998]^c32[11026]^c32[10897]^c32[11021]^c32[11792]^c32[11676]^c32[11630]^c32[12451]^c32[12315]^c32[12649]^c32[13227]^c32[13244]^c32[13413]^c32[14123]^c32[14121]^c32[14152]^c32[14829]^c32[14596]^c32[14854]^c32[15363]^c32[15430]^c32[15456]^c32[15649]^c32[16147]^c32[16297]^c32[16158];
break;
case 17:
d32[6528]=c32[260]^c32[11055]^c32[12369]^c32[13414]^c32[16503];
break;
case 18:
d32[6912]=c32[898]^c32[9379]^c32[10264]^c32[13956]^c32[14596];
break;
case 19:
d32[7296]=c32[145]^c32[981]^c32[5720]^c32[6386]^c32[7877];
break;
case 20:
d32[7680]=c32[366]^c32[135]^c32[264]^c32[335]^c32[187]^c32[846]^c32[1077]^c32[914]^c32[1645]^c32[1671]^c32[1539]^c32[2348]^c32[2436]^c32[2363]^c32[2510]^c32[3419]^c32[3079]^c32[3406]^c32[4080]^c32[4043]^c32[4155]^c32[4883]^c32[4828]^c32[4866]^c32[5382]^c32[5702]^c32[5588]^c32[6150]^c32[6363]^c32[6259]^c32[7288]^c32[7149]^c32[7034]^c32[7176]^c32[8027]^c32[7868]^c32[7871]^c32[8616]^c32[8802]^c32[8812]^c32[8789]^c32[9292]^c32[9233]^c32[9506]^c32[10016]^c32[10243]^c32[10000]^c32[11028]^c32[10899]^c32[11023]^c32[11794]^c32[11678]^c32[11632]^c32[12453]^c32[12317]^c32[12651]^c32[13229]^c32[13246]^c32[13415]^c32[14125]^c32[14123]^c32[14154]^c32[14831]^c32[14598]^c32[14856]^c32[15365]^c32[15432]^c32[15458]^c32[16149]^c32[16299]^c32[16160];
break;
case 21:
d32[8064]=c32[973]^c32[3942]^c32[12616]^c32[15573]^c32[16225];
break;
case 22:
d32[8448]=c32[30]^c32[9227]^c32[10217]^c32[13078];
break;
case 23:
d32[8832]=c32[792]^c32[1625]^c32[7741]^c32[13851];
break;
case 24:
d32[9216]=c32[157]^c32[310]^c32[55]^c32[126]^c32[298]^c32[1021]^c32[868]^c32[1089]^c32[1820]^c32[1846]^c32[1714]^c32[2523]^c32[2611]^c32[2538]^c32[2462]^c32[3210]^c32[3254]^c32[3197]^c32[3307]^c32[3871]^c32[4218]^c32[3946]^c32[4674]^c32[4619]^c32[4657]^c32[5557]^c32[5493]^c32[5379]^c32[6325]^c32[6154]^c32[6434]^c32[7079]^c32[6940]^c32[7209]^c32[7818]^c32[8043]^c32[8046]^c32[8791]^c32[8593]^c32[8603]^c32[8787]^c32[9467]^c32[9408]^c32[9297]^c32[10191]^c32[10034]^c32[10175]^c32[10819]^c32[11074]^c32[10814]^c32[11585]^c32[11853]^c32[11807]^c32[12628]^c32[12492]^c32[12442]^c32[13404]^c32[13421]^c32[13206]^c32[13916]^c32[13914]^c32[13945]^c32[14622]^c32[14773]^c32[14647]^c32[15540]^c32[15607]^c32[15633]^c32[16324]^c32[16474]^c32[16335];
break;
case 25:
d32[9600]=c32[840]^c32[4625]^c32[5759]^c32[11064];
break;
case 26:
d32[9984]=c32[71]^c32[1617]^c32[3148]^c32[11656];
break;
case 27:
d32[10368]=c32[962]^c32[4802]^c32[6245];
break;
case 28:
d32[10752]=c32[222]^c32[3091]^c32[14836]^c32[16402];
break;
case 29:
d32[11136]=c32[2]^c32[155]^c32[284]^c32[354]^c32[355]^c32[866]^c32[1097]^c32[933]^c32[934]^c32[1020]^c32[1665]^c32[1691]^c32[1559]^c32[2368]^c32[2456]^c32[2382]^c32[2383]^c32[3439]^c32[3099]^c32[3425]^c32[3426]^c32[4100]^c32[4063]^c32[4175]^c32[4903]^c32[4848]^c32[4885]^c32[4886]^c32[5402]^c32[5722]^c32[5607]^c32[5608]^c32[6170]^c32[6383]^c32[6278]^c32[6279]^c32[6924]^c32[7169]^c32[7054]^c32[8047]^c32[7888]^c32[7890]^c32[7891]^c32[8636]^c32[8822]^c32[8831]^c32[8448]^c32[9312]^c32[9253]^c32[9525]^c32[9526]^c32[10036]^c32[10263]^c32[10019]^c32[10020]^c32[11048]^c32[10919]^c32[11042]^c32[11043]^c32[10757]^c32[11814]^c32[11698]^c32[11652]^c32[12473]^c32[12337]^c32[12670]^c32[12671]^c32[13249]^c32[13266]^c32[13434]^c32[13435]^c32[14145]^c32[14143]^c32[14173]^c32[14174]^c32[13971]^c32[14851]^c32[14618]^c32[14876]^c32[15385]^c32[15452]^c32[15477]^c32[15478]^c32[16169]^c32[16319]^c32[16179]^c32[16180];
break;
case 30:
d32[11520]=c32[14]^c32[167]^c32[295]^c32[296]^c32[366]^c32[367]^c32[159]^c32[878]^c32[1108]^c32[1109]^c32[945]^c32[946]^c32[1677]^c32[1703]^c32[1570]^c32[1571]^c32[2380]^c32[2468]^c32[2394]^c32[2395]^c32[3451]^c32[3110]^c32[3111]^c32[3437]^c32[3438]^c32[4112]^c32[4075]^c32[4186]^c32[4187]^c32[4915]^c32[4859]^c32[4860]^c32[4897]^c32[4898]^c32[5414]^c32[5733]^c32[5734]^c32[5619]^c32[5620]^c32[6182]^c32[6394]^c32[6395]^c32[6290]^c32[6291]^c32[6936]^c32[7181]^c32[7065]^c32[7066]^c32[8059]^c32[7899]^c32[7900]^c32[7902]^c32[7903]^c32[7909]^c32[8648]^c32[8450]^c32[8459]^c32[8460]^c32[9324]^c32[9265]^c32[9537]^c32[9538]^c32[10048]^c32[10274]^c32[10275]^c32[10031]^c32[10032]^c32[10244]^c32[11060]^c32[10930]^c32[10931]^c32[11054]^c32[11055]^c32[11826]^c32[11710]^c32[11663]^c32[11664]^c32[12485]^c32[12349]^c32[12298]^c32[12299]^c32[13261]^c32[13277]^c32[13278]^c32[13062]^c32[13063]^c32[14157]^c32[14154]^c32[14155]^c32[14185]^c32[14186]^c32[14863]^c32[14630]^c32[14887]^c32[14888]^c32[15397]^c32[15463]^c32[15464]^c32[15489]^c32[15490]^c32[16181]^c32[16331]^c32[16191]^c32[16192];
break;
case 31:
d32[11904]=c32[181]^c32[180]^c32[334]^c32[333]^c32[79]^c32[78]^c32[150]^c32[148]^c32[149]^c32[1045]^c32[1044]^c32[892]^c32[891]^c32[1113]^c32[1111]^c32[1112]^c32[868]^c32[1844]^c32[1843]^c32[1870]^c32[1869]^c32[1738]^c32[1737]^c32[2547]^c32[2546]^c32[2635]^c32[2634]^c32[2562]^c32[2560]^c32[2561]^c32[3234]^c32[3233]^c32[3278]^c32[3277]^c32[3221]^c32[3219]^c32[3220]^c32[3895]^c32[3894]^c32[3858]^c32[3857]^c32[3970]^c32[3969]^c32[4698]^c32[4697]^c32[4643]^c32[4642]^c32[4681]^c32[4679]^c32[4680]^c32[5581]^c32[5580]^c32[5517]^c32[5516]^c32[5403]^c32[5401]^c32[5402]^c32[5591]^c32[6349]^c32[6348]^c32[6178]^c32[6177]^c32[6458]^c32[6456]^c32[6457]^c32[7103]^c32[7102]^c32[6964]^c32[6963]^c32[7233]^c32[7232]^c32[7842]^c32[7841]^c32[7683]^c32[7682]^c32[7686]^c32[7684]^c32[7685]^c32[8815]^c32[8814]^c32[8617]^c32[8616]^c32[8627]^c32[8625]^c32[8626]^c32[9491]^c32[9490]^c32[9432]^c32[9431]^c32[9321]^c32[9319]^c32[9320]^c32[10215]^c32[10214]^c32[10058]^c32[10057]^c32[10199]^c32[10197]^c32[10198]^c32[10843]^c32[10842]^c32[11098]^c32[11097]^c32[10838]^c32[10836]^c32[10837]^c32[11609]^c32[11608]^c32[11877]^c32[11876]^c32[11831]^c32[11830]^c32[12652]^c32[12651]^c32[12516]^c32[12515]^c32[12466]^c32[12464]^c32[12465]^c32[13428]^c32[13427]^c32[13061]^c32[13060]^c32[13230]^c32[13228]^c32[13229]^c32[13940]^c32[13939]^c32[13938]^c32[13937]^c32[13969]^c32[13967]^c32[13968]^c32[14646]^c32[14645]^c32[14797]^c32[14796]^c32[14671]^c32[14670]^c32[15564]^c32[15563]^c32[15631]^c32[15630]^c32[15657]^c32[15655]^c32[15656]^c32[16348]^c32[16347]^c32[16498]^c32[16497]^c32[16359]^c32[16357]^c32[16358];
break;
case 32:
d32[12288]=c32[211]^c32[364]^c32[108]^c32[109]^c32[179]^c32[180]^c32[102]^c32[1075]^c32[921]^c32[922]^c32[1142]^c32[1143]^c32[1874]^c32[1900]^c32[1767]^c32[1768]^c32[2577]^c32[2665]^c32[2591]^c32[2592]^c32[3264]^c32[3307]^c32[3308]^c32[3250]^c32[3251]^c32[3925]^c32[3888]^c32[3999]^c32[4000]^c32[4728]^c32[4672]^c32[4673]^c32[4710]^c32[4711]^c32[5611]^c32[5546]^c32[5547]^c32[5432]^c32[5433]^c32[6379]^c32[6207]^c32[6208]^c32[6487]^c32[6488]^c32[7133]^c32[6994]^c32[7262]^c32[7263]^c32[7872]^c32[7712]^c32[7713]^c32[7715]^c32[7716]^c32[8461]^c32[8647]^c32[8656]^c32[8657]^c32[9521]^c32[9462]^c32[9350]^c32[9351]^c32[9417]^c32[10245]^c32[10087]^c32[10088]^c32[10228]^c32[10229]^c32[10873]^c32[11127]^c32[11128]^c32[10867]^c32[10868]^c32[10927]^c32[11639]^c32[11523]^c32[11860]^c32[11861]^c32[12298]^c32[12546]^c32[12495]^c32[12496]^c32[13074]^c32[13090]^c32[13091]^c32[13259]^c32[13260]^c32[13970]^c32[13967]^c32[13968]^c32[13998]^c32[13999]^c32[14676]^c32[14827]^c32[14700]^c32[14701]^c32[15594]^c32[15660]^c32[15661]^c32[15686]^c32[15687]^c32[16378]^c32[16144]^c32[16388]^c32[16389];
break;
case 33:
d32[12672]=c32[1091]^c32[1544]^c32[8809]^c32[16233];
break;
case 34:
d32[13056]=c32[230]^c32[5524]^c32[11722]^c32[13368];
break;
case 35:
d32[13440]=c32[189]^c32[342]^c32[87]^c32[158]^c32[1053]^c32[900]^c32[1121]^c32[1088]^c32[1852]^c32[1878]^c32[1746]^c32[2555]^c32[2643]^c32[2570]^c32[3242]^c32[3286]^c32[3229]^c32[3903]^c32[3866]^c32[3978]^c32[4706]^c32[4651]^c32[4689]^c32[4943]^c32[5589]^c32[5525]^c32[5411]^c32[6357]^c32[6186]^c32[6466]^c32[7111]^c32[6972]^c32[7241]^c32[7850]^c32[7691]^c32[7694]^c32[8823]^c32[8625]^c32[8635]^c32[9499]^c32[9440]^c32[9329]^c32[9218]^c32[10223]^c32[10066]^c32[10207]^c32[10851]^c32[11106]^c32[10846]^c32[11617]^c32[11885]^c32[11839]^c32[12660]^c32[12524]^c32[12474]^c32[13436]^c32[13069]^c32[13238]^c32[13948]^c32[13946]^c32[13977]^c32[14654]^c32[14805]^c32[14679]^c32[15572]^c32[15639]^c32[15665]^c32[16356]^c32[16506]^c32[16367];
break;
case 36:
d32[13824]=c32[210]^c32[11065]^c32[11817]^c32[13845];
break;
case 37:
d32[14208]=c32[38]^c32[39]^c32[192]^c32[321]^c32[8]^c32[902]^c32[903]^c32[1134]^c32[971]^c32[1037]^c32[1701]^c32[1702]^c32[1728]^c32[1596]^c32[2404]^c32[2405]^c32[2493]^c32[2420]^c32[3092]^c32[3136]^c32[3079]^c32[4136]^c32[4137]^c32[4100]^c32[4212]^c32[4939]^c32[4940]^c32[4885]^c32[4923]^c32[5439]^c32[5759]^c32[5645]^c32[6207]^c32[6420]^c32[6316]^c32[6960]^c32[6961]^c32[7206]^c32[7091]^c32[7699]^c32[7700]^c32[7925]^c32[7928]^c32[8672]^c32[8673]^c32[8475]^c32[8485]^c32[9348]^c32[9349]^c32[9290]^c32[9563]^c32[10072]^c32[10073]^c32[10300]^c32[10057]^c32[10066]^c32[11085]^c32[10956]^c32[11080]^c32[11850]^c32[11851]^c32[11735]^c32[11689]^c32[12509]^c32[12510]^c32[12374]^c32[12324]^c32[13286]^c32[13303]^c32[13088]^c32[14181]^c32[14182]^c32[14180]^c32[13827]^c32[14887]^c32[14888]^c32[14655]^c32[14913]^c32[15421]^c32[15422]^c32[15489]^c32[15515]^c32[16205]^c32[16206]^c32[16356]^c32[16217];
break;
case 38:
d32[14592]=c32[185]^c32[7089]^c32[7969]^c32[9430];
break;
case 39:
d32[14976]=c32[1026]^c32[2397]^c32[5722]^c32[14889];
break;
case 40:
d32[15360]=c32[175]^c32[6181]^c32[13368];
break;
case 41:
d32[15744]=c32[820]^c32[2618]^c32[7051]^c32[14112];
break;
case 42:
d32[16128]=c32[142]^c32[295]^c32[39]^c32[40]^c32[110]^c32[111]^c32[113]^c32[1006]^c32[852]^c32[853]^c32[1073]^c32[1074]^c32[1805]^c32[1831]^c32[1698]^c32[1699]^c32[2508]^c32[2596]^c32[2522]^c32[2523]^c32[3195]^c32[3238]^c32[3239]^c32[3181]^c32[3182]^c32[3086]^c32[3856]^c32[4203]^c32[3930]^c32[3931]^c32[4659]^c32[4987]^c32[4988]^c32[4641]^c32[4642]^c32[5542]^c32[5477]^c32[5478]^c32[5747]^c32[5748]^c32[6310]^c32[6522]^c32[6523]^c32[6418]^c32[6419]^c32[7064]^c32[6925]^c32[7193]^c32[7194]^c32[7803]^c32[8027]^c32[8028]^c32[8030]^c32[8031]^c32[8776]^c32[8578]^c32[8587]^c32[8588]^c32[9452]^c32[9393]^c32[9281]^c32[9282]^c32[10176]^c32[10018]^c32[10019]^c32[10159]^c32[10160]^c32[10804]^c32[11058]^c32[11059]^c32[10798]^c32[10799]^c32[11570]^c32[11838]^c32[11791]^c32[11792]^c32[12613]^c32[12477]^c32[12426]^c32[12427]^c32[13389]^c32[13405]^c32[13406]^c32[13190]^c32[13191]^c32[13901]^c32[13898]^c32[13899]^c32[13929]^c32[13930]^c32[14607]^c32[14758]^c32[14631]^c32[14632]^c32[15525]^c32[15591]^c32[15592]^c32[15617]^c32[15618]^c32[16309]^c32[16459]^c32[16319]^c32[16320];
break;
case 43:
d32[16512]=c32[92]^c32[245]^c32[374]^c32[60]^c32[61]^c32[956]^c32[803]^c32[1023]^c32[1024]^c32[881]^c32[1755]^c32[1781]^c32[1649]^c32[2458]^c32[2546]^c32[2472]^c32[2473]^c32[3145]^c32[3189]^c32[3131]^c32[3132]^c32[4190]^c32[4153]^c32[3881]^c32[4609]^c32[4938]^c32[4975]^c32[4976]^c32[5492]^c32[5428]^c32[5697]^c32[5698]^c32[6260]^c32[6473]^c32[6368]^c32[6369]^c32[7014]^c32[7259]^c32[7144]^c32[7753]^c32[7978]^c32[7980]^c32[7981]^c32[8726]^c32[8528]^c32[8537]^c32[8538]^c32[9402]^c32[9343]^c32[9231]^c32[9232]^c32[10126]^c32[10353]^c32[10109]^c32[10110]^c32[10754]^c32[11009]^c32[11132]^c32[11133]^c32[11520]^c32[11788]^c32[11742]^c32[12563]^c32[12427]^c32[12376]^c32[12377]^c32[12420]^c32[13339]^c32[13356]^c32[13140]^c32[13141]^c32[13851]^c32[13849]^c32[13879]^c32[13880]^c32[13938]^c32[14941]^c32[14708]^c32[14966]^c32[15475]^c32[15542]^c32[15567]^c32[15568]^c32[16259]^c32[16409]^c32[16269]^c32[16270];
break;
case 44:
d32[16896]=c32[197]^c32[350]^c32[95]^c32[166]^c32[80]^c32[1061]^c32[908]^c32[1129]^c32[1860]^c32[1886]^c32[1754]^c32[2563]^c32[2651]^c32[2578]^c32[3250]^c32[3294]^c32[3237]^c32[3911]^c32[3874]^c32[3986]^c32[4714]^c32[4659]^c32[4697]^c32[5597]^c32[5533]^c32[5419]^c32[5454]^c32[6365]^c32[6194]^c32[6474]^c32[7119]^c32[6980]^c32[7249]^c32[7075]^c32[7858]^c32[7699]^c32[7702]^c32[8831]^c32[8633]^c32[8643]^c32[9507]^c32[9448]^c32[9337]^c32[10231]^c32[10074]^c32[10215]^c32[10859]^c32[11114]^c32[10854]^c32[11625]^c32[11893]^c32[11847]^c32[12668]^c32[12532]^c32[12482]^c32[13060]^c32[13077]^c32[13246]^c32[13956]^c32[13954]^c32[13985]^c32[14662]^c32[14813]^c32[14687]^c32[15580]^c32[15647]^c32[15673]^c32[16364]^c32[16130]^c32[16375];
break;
case 45:
d32[17280]=c32[903]^c32[4757]^c32[7695];
break;
}
}
}
extern "C" int ldpc_BG1_Zc384_cuda32(uint32_t *c[4],uint32_t *d[4],int n_inputs,cudaStream_t *stream,int sidx) {
dim3 numblocks(n_inputs,46);
ldpc_BG1_Zc384_worker<<<numblocks,384,0,stream[sidx]>>>(c,d);
cudaError_t err=cudaPeekAtLastError();
if (err!=cudaSuccess) {
printf("cuda error: %s (c %p, d %p)\n",cudaGetErrorString(err),c,d);
exit(-1);
}
return(0);
}

View File

@@ -0,0 +1,131 @@
// deinterleave_u16.cu
#include <cuda_runtime.h>
#include <stdio.h>
#include <stdint.h>
__global__ void deinterleave_i16_2(int16_t* __restrict__ e,
const int16_t* __restrict__ f,
int E1,
int E2,
int r_firstE2)
{
int g = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
int E = (r<r_firstE2) ? E1 : E2;
int EQm = E/2;
if (g >= EQm) return;
int r_off = r<r_firstE2 ? r*E1 : ((r_firstE2*E1)+(r-r_firstE2)*E2);
const int16_t *in = f +r_off + 2*g;
int16_t* e0 = e + r_off;
int16_t* e1 = e0 + EQm;
e0[g] = in[0];
e1[g] = in[1];
}
__global__ void deinterleave_i16_4(int16_t* __restrict__ e,
const int16_t* __restrict__ f,
int E1,
int E2,
int r_firstE2)
{
int g = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
int E = (r<r_firstE2) ? E1 : E2;
int EQm = E/4;
if (g >= EQm) return;
int r_off = r<r_firstE2 ? r*E1 : ((r_firstE2*E1)+(r-r_firstE2)*E2);
const int16_t *in = f + r_off + 4*g;
int16_t* e0 = e + r_off;
int16_t* e1 = e0 + EQm;
int16_t* e2 = e1 + EQm;
int16_t* e3 = e2 + EQm;
e0[g] = in[0];
e1[g] = in[1];
e2[g] = in[2];
e3[g] = in[3];
}
__global__ void deinterleave_i16_6(int16_t* __restrict__ e,
const int16_t* __restrict__ f,
int E1,
int E2,
int r_firstE2)
{
int g = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
int E = (r<r_firstE2) ? E1 : E2;
int EQm = E/6;
if (g >= EQm) return;
int r_off = r<r_firstE2 ? r*E1 : (r_firstE2*E1)+(r-r_firstE2)*E2;
const int16_t* in = f + r_off + 6*g;
int16_t* e0 = e + r_off;
int16_t* e1 = e0 + EQm;
int16_t* e2 = e1 + EQm;
int16_t* e3 = e2 + EQm;
int16_t* e4 = e3 + EQm;
int16_t* e5 = e4 + EQm;
e0[g] = in[0];
e1[g] = in[1];
e2[g] = in[2];
e3[g] = in[3];
e4[g] = in[4];
e5[g] = in[5];
}
__global__ void deinterleave_i16_8(int16_t* __restrict__ e,
const int16_t* __restrict__ f,
const int E1,
const int E2,
const int r_firstE2)
{
int g = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
int E = (r<r_firstE2) ? E1 : E2;
int EQm = E/8;
if (g >= EQm) return;
int r_off = r<r_firstE2 ? r*E1 : (r_firstE2*E1)+(r-r_firstE2)*E2;
const int16_t *in = (f + r_off + 8*g);
int16_t* e0 = e + r_off;
int16_t* e1 = e0 + EQm;
int16_t* e2 = e1 + EQm;
int16_t* e3 = e2 + EQm;
int16_t* e4 = e3 + EQm;
int16_t* e5 = e4 + EQm;
int16_t* e6 = e5 + EQm;
int16_t* e7 = e6 + EQm;
e0[g] = in[0];
e1[g] = in[1];
e2[g] = in[2];
e3[g] = in[3];
e4[g] = in[4];
e5[g] = in[5];
e6[g] = in[6];
e7[g] = in[7];
}
// Host launcher
extern "C" void launch_deinterleave_i16(int Qm, int E1, int E2, int C, int r_firstE2,int16_t* e, const int16_t* f,cudaStream_t *s,int8_t sidx)
{
const int threads = 256;
dim3 blocks(((E2/Qm) + threads - 1) / threads,C);
switch (Qm) {
case 2: deinterleave_i16_2<<<blocks, threads, 0, s[sidx]>>>(e, f, E1,E2,r_firstE2); break;
case 4: deinterleave_i16_4<<<blocks, threads, 0, s[sidx]>>>(e, f, E1,E2,r_firstE2); break;
case 6: deinterleave_i16_6<<<blocks, threads, 0, s[sidx]>>>(e, f, E1,E2,r_firstE2); break;
case 8: deinterleave_i16_8<<<blocks, threads, 0, s[sidx]>>>(e, f, E1,E2,r_firstE2); break;
default: /* unsupported */ break;
}
cudaError_t err=cudaPeekAtLastError();
if (err!=cudaSuccess) {
printf("cuda error (deinterleave_i16): %s (e %p, f %p, E1 %d, E2 %d, Qm %d, C %d)\n",cudaGetErrorString(err),e,f,E1,E2,Qm,C);
exit(-1);
}
}

View File

@@ -0,0 +1,77 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*!\file ldpc_encode_parity_check.c
* \brief Parity check function used by ldpc encoders
* \author Florian Kaltenberger, Raymond Knopp, Kien le Trung (Eurecom)
* \email openair_tech@eurecom.fr
* \date 27-03-2018
* \version 1.0
* \note
* \warning
*/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "assertions.h"
#include "common/utils/LOG/log.h"
#include <cuda_runtime.h>
int ldpc_BG1_Zc384_cuda32(uint32_t **c,uint32_t **d,int n_inputs, cudaStream_t *stream,int sidx);
void encode_parity_check_part_cuda(uint32_t **c, uint32_t **d, short BG,short Zc,short Kb, int ncols, int n_inputs, cudaStream_t *stream,int sidx)
{
if (BG == 1) {
switch (Zc) {
case 176:
case 192:
case 208:
case 224:
case 240:
case 256:
case 288:
case 320:
case 352:
AssertFatal(1==0,"BG %d Zc %d not supported yet for CUDA\n",BG, Zc);
break;
case 384:
ldpc_BG1_Zc384_cuda32(c, d, n_inputs, stream,sidx);
break;
default:
AssertFatal(false, "BG %d Zc %d is not supported yet\n", BG, Zc);
}
} else if (BG == 2) {
switch (Zc) {
case 72:
case 80:
case 88:
case 96:
case 104:
case 112:
case 120:
case 128:
case 144:
case 160:
case 176:
case 192:
case 208:
case 224:
case 240:
case 256:
case 288:
case 320:
case 352:
case 384:
default:
AssertFatal(false , "BG %d Zc %d is not supported yet\n", BG, Zc);
}
} else
AssertFatal(false, "BG %d is not supported\n", BG);
}

View File

@@ -0,0 +1,194 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file ldpc_encoder_cuda32.c
* \brief Defines the optimized LDPC encoder for NVidia GPUs
* \email openair_tech@eurecom.fr
* \date 11-30-2025
* \version 1.0
* \note
* \warning
*/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "assertions.h"
#include "common/utils/LOG/log.h"
#include "time_meas.h"
#include "openair1/PHY/CODING/nrLDPC_defs.h"
#include "PHY/sse_intrin.h"
#include "openair1/PHY/CODING/nrLDPC_extern.h"
#include <cuda_runtime.h>
//#define DEBUG_LDPC 1
#include "ldpc_encode_parity_check_cuda.c"
uint32_t *c_dev;
uint32_t **c_host;
uint32_t *c_devh[4];
uint32_t *d_dev;
uint32_t **d_host;
uint32_t *d_devh[4];
uint32_t *input_dev;
uint32_t **input_host;
uint32_t *input_devh[128];
int managed = 0, concurrent = 0, uva = 0, pageable = 0, pageable_uses_host = 0, register_host = 0, integrated = 0;
int cuda_support_set = 0;
extern cudaStream_t encoderStreams[4];
int ldpc_input(uint32_t **input,uint32_t *cc[4],int nseg,cudaStream_t *s,int sidx);
void cuda_support_init() {
int dev = 0;
struct cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, dev);
cudaDeviceGetAttribute(&managed, cudaDevAttrManagedMemory, dev);
cudaDeviceGetAttribute(&concurrent, cudaDevAttrConcurrentManagedAccess, dev);
cudaDeviceGetAttribute(&uva, cudaDevAttrUnifiedAddressing, dev);
cudaDeviceGetAttribute(&pageable, cudaDevAttrPageableMemoryAccess, dev);
cudaDeviceGetAttribute(&pageable_uses_host, cudaDevAttrPageableMemoryAccessUsesHostPageTables, dev);
cudaDeviceGetAttribute(&register_host, cudaDevAttrHostRegisterSupported,dev);
cudaDeviceGetAttribute(&integrated, cudaDevAttrIntegrated,dev);
LOG_I(NR_PHY,"Device: %s (cc %d.%d)\n", prop.name, prop.major, prop.minor);
LOG_I(NR_PHY,"Unified Virtual Addressing (UVA): %s\n", uva ? "YES" : "NO");
LOG_I(NR_PHY,"Managed (Unified) Memory: %s\n", managed ? "YES" : "NO");
LOG_I(NR_PHY,"Concurrent managed access: %s\n", concurrent ? "YES" : "NO");
LOG_I(NR_PHY,"Pageable memory access: %s\n", pageable ? "YES" : "NO");
LOG_I(NR_PHY,"Uses host page tables: %s\n", pageable_uses_host ? "YES" : "NO");
LOG_I(NR_PHY,"Host Register supported: %s\n", register_host ? "YES" : "NO");
LOG_I(NR_PHY,"Integrated (shared) Memory %s\n", integrated ? "YES" : "NO");
if (!pageable && !integrated) {
LOG_I(NR_PHY,"Allocating c,d,cc arrays for GPU \n");
cudaError_t err=cudaMalloc((void **)&c_dev,4*sizeof(uint32_t*));
AssertFatal(err == cudaSuccess,"CUDA Error (c_dev): %s\n", cudaGetErrorString(err));
err=cudaHostAlloc((void **)&c_host,4*sizeof(uint32_t*),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (c_host): %s\n", cudaGetErrorString(err));
for (int i=0;i<4;i++) {
err=cudaMalloc((void**)&c_devh[i],2*22*384*sizeof(uint32_t));
AssertFatal(err == cudaSuccess,"CUDA Error (c_devh[%d]): %s\n", i,cudaGetErrorString(err));
err=cudaHostAlloc((void**)&c_host[i],2*22*384*sizeof(uint32_t),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (chost[%d]): %s\n", i,cudaGetErrorString(err));
}
err = cudaMemcpy(c_dev,c_devh,4*sizeof(uint32_t*),cudaMemcpyHostToDevice);
AssertFatal(err == cudaSuccess,"CUDA Error (memcpy c_devh -> c_dev): %s\n", cudaGetErrorString(err));
err=cudaMalloc((void**)&d_dev,4*sizeof(uint32_t*));
AssertFatal(err == cudaSuccess,"CUDA Error: %s\n", cudaGetErrorString(err));
err=cudaHostAlloc((void **)&d_host,4*sizeof(uint32_t*),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (d_host): %s\n", cudaGetErrorString(err));
for (int i=0;i<4;i++) {
err=cudaMalloc((void**)&d_devh[i],68*384*sizeof(uint32_t));
AssertFatal(err == cudaSuccess,"CUDA Error (d_devh[%d]: %s\n", i,cudaGetErrorString(err));
err=cudaHostAlloc((void**)&d_host[i],68*384*sizeof(uint32_t),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (d_host[%d]): %s\n", i,cudaGetErrorString(err));
}
err=cudaMemcpy(d_dev,d_devh,4*sizeof(uint32_t*),cudaMemcpyHostToDevice);
AssertFatal(err == cudaSuccess,"CUDA Error (memcpy d_devh -> d_dev): %s\n", cudaGetErrorString(err));
err=cudaMalloc((void**)&input_dev,128*sizeof(uint8_t*));
AssertFatal(err == cudaSuccess,"CUDA Error: %s\n", cudaGetErrorString(err));
err=cudaHostAlloc((void **)&input_host,128*sizeof(uint8_t*),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (cc_host): %s\n", cudaGetErrorString(err));
for (int i=0;i<128;i++) {
err=cudaMalloc((void**)&input_devh[i],(8448/8)*sizeof(uint8_t));
AssertFatal(err == cudaSuccess,"CUDA Error (input_devh[%d]: %s\n", i,cudaGetErrorString(err));
err=cudaHostAlloc((void**)&input_host[i],(8448/8)*sizeof(uint8_t),cudaHostAllocDefault);
AssertFatal(err == cudaSuccess,"CUDA Error (input_host[%d]): %s\n", i,cudaGetErrorString(err));
}
err=cudaMemcpy(input_dev,input_devh,128*sizeof(uint8_t*),cudaMemcpyHostToDevice);
AssertFatal(err == cudaSuccess,"CUDA Error (memcpy cc_devh -> d_dev): %s\n", cudaGetErrorString(err));
}
else {
LOG_I(NR_PHY,"Allocating c,d,cc arrays for CPU/GPU shared-memory\n");
cudaError_t err=cudaHostAlloc((void **)&c_host,4*sizeof(uint32_t*),cudaHostAllocMapped|cudaHostAllocPortable);
AssertFatal(err == cudaSuccess,"CUDA Error (c_host): %s\n", cudaGetErrorString(err));
err = cudaHostGetDevicePointer((void**)&c_dev, c_host, 0);
AssertFatal(err == cudaSuccess,"CUDA Error (c_dev): %s\n", cudaGetErrorString(err));
LOG_I(NR_PHY,"c_host %p, c_dev %p\n",c_host,c_dev);
for (int i=0;i<4;i++) {
err=cudaHostAlloc((void**)&c_host[i],2*22*384*sizeof(uint32_t),cudaHostAllocMapped);
AssertFatal(err == cudaSuccess,"CUDA Error (c_host[%d]): %s\n", i,cudaGetErrorString(err));
err = cudaHostGetDevicePointer((void**)&c_devh[i], c_host[i], 0);
AssertFatal(err == cudaSuccess,"CUDA Error (c_devh[%d]): %s\n", i,cudaGetErrorString(err));
}
err=cudaMemcpy(c_dev,c_devh,4*sizeof(uint32_t*),cudaMemcpyHostToDevice);
AssertFatal(err == cudaSuccess,"CUDA Error (memcpy c_devh -> c_dev): %s\n", cudaGetErrorString(err));
err=cudaHostAlloc((void **)&d_host,4*sizeof(uint32_t*),cudaHostAllocMapped);
AssertFatal(err == cudaSuccess,"CUDA Error (d_host): %s\n", cudaGetErrorString(err));
err=cudaHostGetDevicePointer((void**)&d_dev, d_host, 0);
AssertFatal(err == cudaSuccess,"CUDA Error cudaHostGetDevicePointer(d_dev): %s\n", cudaGetErrorString(err));
LOG_I(NR_PHY,"d_host %p, d_dev %p\n",d_host,d_dev);
for (int i=0;i<4;i++) {
err=cudaHostAlloc((void**)&d_host[i],68*384*sizeof(uint32_t),cudaHostAllocMapped);
AssertFatal(err == cudaSuccess,"CUDA Error (d_host[%d]): %s\n", i,cudaGetErrorString(err));
err=cudaHostGetDevicePointer((void**)&d_devh[i], d_host[i], 0);
AssertFatal(err == cudaSuccess,"CUDA Error (cudaHostGetDevicePointer) d_devh[%d]: %s\n", i,cudaGetErrorString(err));
LOG_I(NR_PHY,"d_host[%d] %p, d_devh[%d] %p\n",i,d_host[i],i,d_devh[i]);
}
err=cudaMemcpy(d_dev,d_devh,4*sizeof(uint32_t*),cudaMemcpyHostToDevice);
AssertFatal(err == cudaSuccess,"CUDA Error (memcpy d_devh -> d_dev): %s\n", cudaGetErrorString(err));
err=cudaHostAlloc((void **)&input_host,128*sizeof(uint8_t*),cudaHostAllocMapped);
AssertFatal(err == cudaSuccess,"CUDA Error (input_host): %s\n", cudaGetErrorString(err));
err=cudaHostGetDevicePointer((void**)&input_dev, input_host, 0);
AssertFatal(err == cudaSuccess,"CUDA Error cudaHostGetDevicePointer(cc_host): %s\n", cudaGetErrorString(err));
LOG_I(NR_PHY,"input_host %p, input_dev %p\n",input_host,input_dev);
}
cuda_support_set=1;
}
uint32_t **LDPCencoder32(uint8_t **input, encoder_implemparams_t *impp)
{
//set_log(PHY, 4);
int Zc = impp->Zc;
int Kb = impp->Kb;
short block_length = impp->K;
short BG = impp->BG;
int ncols=22;
int encoder_stream=0;
AssertFatal(BG==1,"BG %d is not supported for CUDA version\n",BG);
AssertFatal(Zc==384,"Zc %d is not supported for CUDA version \n", Zc);
if(impp->tinput != NULL) start_meas(impp->tinput);
#ifdef DEBUG_LDPC
LOG_I(PHY,"ldpc_encoder_cuda32: BG %d, Zc %d, Kb %d, block_length %d, segments %d\n",BG,Zc,Kb,block_length,impp->n_segments);
LOG_I(PHY,"ldpc_encoder_cuda32: PDU (seg 0) %x %x %x %x\n",input[0][0],input[0][1],input[0][2],input[0][3]);
#endif
int n_inputs = (impp->n_segments/32)+(((impp->n_segments&31) > 0) ? 1: 0);
// uint32_t cc[4][22*Zc]; //padded input, unpacked, max size
if (!pageable&&!integrated) { // this means we are not on shared memory
for (int r=0;r<impp->n_segments;r++) {
cudaMemcpyAsync(input_devh[r],input[r],block_length>>3,cudaMemcpyHostToDevice,encoderStreams[encoder_stream]);
}
}
ldpc_input(pageable||integrated? (uint32_t**)input : (uint32_t**)input_dev,(uint32_t**)c_dev,impp->n_segments,encoderStreams,encoder_stream);
if(impp->tinput != NULL) stop_meas(impp->tinput);
//parity check part
if(impp->tparity != NULL) start_meas(impp->tparity);
encode_parity_check_part_cuda((uint32_t**)c_dev, (uint32_t**)d_dev, BG, Zc, Kb, ncols,n_inputs,encoderStreams,encoder_stream);
if (!pageable&&!integrated) { // this means we are not on shared memory
for (int r=0; r<n_inputs;r++) cudaMemcpyAsync(d_host[r],d_devh[r],68*384*sizeof(uint32_t),cudaMemcpyDeviceToHost,encoderStreams[encoder_stream]);
}
cudaStreamSynchronize(encoderStreams[encoder_stream]);
if(impp->tparity != NULL) stop_meas(impp->tparity);
return d_host;
}

View File

@@ -0,0 +1,92 @@
#include <stdio.h>
#include <stdint.h>
#include <cuda_runtime.h>
/*
__device__ const uint32_t masks[4] = {0x80,0x8000,0x800000,0x80000000};
__global__ void ldpc_input_worker(uint32_t **input,uint32_t *cc[4],int block_length,int nseg) {
int block_off = blockIdx.x*blockDim.x<<2;
int i2 = threadIdx.x<<2;
uint32_t *out=cc[blockIdx.y] + block_off + i2;
int nseg0 = (blockIdx.y << 5);
int nseg1;
if ((nseg0 + 32) <= nseg) nseg1 = nseg0+32;
else nseg1 = nseg0 + (nseg&31);
int bit_offset = i2+block_off;
int uint32_offset = bit_offset>>5;
uint32_t mask = masks[(bit_offset&31)>>3];
uint32_t mask0 = mask>>(bit_offset&7);bit_offset++;
uint32_t mask1 = mask>>(bit_offset&7);bit_offset++;
uint32_t mask2 = mask>>(bit_offset&7);bit_offset++;
uint32_t mask3 = mask>>(bit_offset&7);
uint32_t tmp,jmod;
uint32_t otmp0,otmp1,otmp2,otmp3;
if (bit_offset < block_length) {
tmp=input[nseg0][uint32_offset];
otmp0 = ((tmp&mask0) > 0);
otmp1 = ((tmp&mask1) > 0);
otmp2 = ((tmp&mask2) > 0);
otmp3 = ((tmp&mask3) > 0);
for (int j=nseg0+1;j<nseg1;j++) {
tmp=input[j][uint32_offset];
jmod = j&31;
otmp0 |= (((tmp&mask0) > 0)<<jmod);
otmp1 |= (((tmp&mask1) > 0)<<jmod);
otmp2 |= (((tmp&mask2) > 0)<<jmod);
otmp3 |= (((tmp&mask3) > 0)<<jmod);
}
out[0]=otmp0;
out[1]=otmp1;
out[2]=otmp2;
out[3]=otmp3;
}
}
*/
__device__ uint32_t masks[32] = {
0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1,
0x8000,0x4000,0x2000,0x1000,0x800,0x400,0x200,0x100,
0x800000,0x400000,0x200000,0x100000,0x80000,0x40000,0x20000,0x10000,
0x80000000,0x40000000,0x20000000,0x10000000,0x8000000,0x4000000,0x2000000,0x1000000};
__global__ void ldpc_input_worker(uint32_t **input,uint32_t *cc[4],int nseg) {
// int block_off = blockIdx.y*blockDim.x;
int i1 = blockIdx.y;
int i2 = threadIdx.x;
int nseg0 = (blockIdx.x << 5);
int nseg1;
if ((nseg0 + 32) <= nseg) nseg1 = nseg0+32;
else nseg1 = nseg0 + (nseg&31);
int bit_offset = i2+(i1*384);
int uint32_offset = bit_offset>>5;
uint32_t mask0 = masks[bit_offset&31];
uint32_t tmp,jmod;
uint32_t otmp0;
if (bit_offset < 8448) {
tmp=input[nseg0][uint32_offset];
otmp0 = ((tmp&mask0) > 0);
for (int j=nseg0+1;j<nseg1;j++) {
tmp=input[j][uint32_offset];
jmod = j&31;
otmp0 |= (((tmp&mask0) > 0)<<jmod);
}
cc[blockIdx.x][(2*i1*384) + i2] = otmp0;
cc[blockIdx.x][(2*i1+1)*384 + i2]=otmp0;
}
}
extern "C" int ldpc_input(uint32_t **input,uint32_t *cc[4],int nseg,cudaStream_t *stream,int sidx) {
int ns = nseg>>5;
if ((nseg&31)>0) ns++;
dim3 numblocks(ns,22);
//printf("input %p\n",input);
ldpc_input_worker<<<numblocks,384,0,stream[sidx]>>>(input,cc,nseg);
cudaError_t err=cudaPeekAtLastError();
if (err!=cudaSuccess) {
printf("cuda error: %s (input %p, cc %p, nseg %d, ns %d)\n",cudaGetErrorString(err),input,cc,nseg,ns);
exit(-1);
}
return(0);
}

View File

@@ -0,0 +1,492 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "assertions.h"
#include "common/utils/LOG/log.h"
#include "openair1/PHY/CODING/nrLDPC_defs.h"
#include "PHY/sse_intrin.h"
#include "openair1/PHY/CODING/nrLDPC_extern.h"
void ldpc_input32(uint8_t **input, uint32_t **c, int n_inputs, int n_segments) {
#ifndef __aarch64__
simde__m256i andmask = simde_mm256_set_epi32(0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80); // every 8 bits -> 8 bytes, pattern repeats.
simde__m256i zero256 = simde_mm256_setzero_si256();
simde__m256i masks[32];
register simde__m256i c256;
for (int i=0;i<32;i++) {
masks[i] = simde_mm256_set1_epi32(1<<i);
}
for (int b=0;b<8448;b+=384) {
for (int i=0; i < 384; i += 8) {
unsigned int i8 = (2*b + i) >> 3;
for (int j0=0;j0<n_inputs;j0++) {
c256 = simde_mm256_and_si256(simde_mm256_cmpeq_epi32(simde_mm256_andnot_si256(simde_mm256_set1_epi32(input[j0<<5][i8]),andmask),zero256),masks[0]);
int j2 = 32;
if (j0==(n_inputs-1)) j2= n_segments-(j0<<5);
for (int j=1; j < j2; j++) {
c256 = simde_mm256_or_si256(simde_mm256_and_si256(simde_mm256_cmpeq_epi32(simde_mm256_andnot_si256(simde_mm256_set1_epi32(input[(j0<<5)+j][i8]),andmask),zero256),masks[j]),c256);
}
((simde__m256i *)c[j0])[i8] = c256;
((simde__m256i *)c[j0])[i8+48] = c256;
}
}
}
#else
int i2=0;
uint32_t cc[4][8448];
const int32_t ucShifta[32][4] = {
{-7,-6,-5,-4}, // 0
{-6,-5,-4,-3}, // 1
{-5,-4,-3,-2}, // 2
{-4,-3,-2,-1}, // 3
{-3,-2,-1,0}, // 4
{-2,-1,0,1}, // 5
{-1,0,1,2}, // 6
{0,1,2,3}, // 7
{1,2,3,4}, // 8
{2,3,4,5}, // 9
{3,4,5,6}, // 10
{4,5,6,7}, // 11
{5,6,7,8}, // 12
{6,7,8,9}, // 13
{7,8,9,10}, // 14
{8,9,10,11}, // 15
{9,10,11,12}, // 16
{10,11,12,13}, // 17
{11,12,13,14}, // 18
{12,13,14,15}, // 19
{13,14,15,16}, // 20
{14,15,16,17}, // 21
{15,16,17,18}, // 22
{16,17,18,19}, // 23
{17,18,19,20}, // 24
{18,19,20,21}, // 25
{19,20,21,22}, // 26
{20,21,22,23}, // 27
{21,22,23,24}, // 28
{22,23,24,25}, // 29
{23,24,25,26}, // 30
{24,25,26,27}}; // 31
const int32_t ucShiftb[32][4] = {
{-3,-2,-1,0}, // 0
{-2,-1,0,1}, // 1
{-1,0,1,2}, // 2
{0,1,2,3}, // 3
{1,2,3,4}, // 4
{2,3,4,5}, // 5
{3,4,5,6}, // 6
{4,5,6,7}, // 7
{5,6,7,8}, // 8
{6,7,8,9}, // 9
{7,8,9,10}, // 10
{8,9,10,11}, // 11
{9,10,11,12}, // 12
{10,11,12,13}, // 13
{11,12,13,14}, // 14
{12,13,14,15}, // 15
{13,14,15,16}, // 16
{14,15,16,17}, // 17
{15,16,17,18}, // 18
{16,17,18,19}, // 19
{17,18,19,20}, // 20
{18,19,20,21}, // 21
{19,20,21,22}, // 22
{20,21,22,23}, // 23
{21,22,23,24}, // 24
{22,23,24,25}, // 25
{23,24,25,26}, // 26
{24,25,26,27}, // 27
{25,26,27,28}, // 28
{26,27,28,29}, // 29
{27,28,29,30}, // 30
{28,29,30,31}}; // 31
const int32_t ucShiftc[32][4] = {
{-15,-14,-13,-12}, // 0
{-14,-13,-12,-11}, // 1
{-13,-12,-11,-10}, // 2
{-12,-11,-10,-9}, // 3
{-11,-10,-9,-8}, // 4
{-10,-9,-8,-7}, // 5
{-9,-8,-7,-6}, // 6
{-8,-7,-6,-5}, // 7
{-7,-6,-5,-4}, // 8
{-6,-5,-4,-3}, // 9
{-5,-4,-3,-2}, // 10
{-4,-3,-2,-1}, // 11
{-3,-2,-1,0}, // 12
{-2,-1,0,1}, // 13
{-1,0,1,2}, // 14
{0,1,2,3}, // 15
{1,2,3,4}, // 16
{2,3,4,5}, // 17
{3,4,5,6}, // 18
{4,5,6,7}, // 19
{5,6,7,8}, // 20
{6,7,8,9}, // 21
{7,8,9,10}, // 22
{8,9,10,11}, // 23
{9,10,11,12}, // 24
{10,11,12,13}, // 25
{11,12,13,14}, // 26
{12,13,14,15}, // 27
{13,14,15,16}, // 28
{14,15,16,17}, // 29
{15,16,17,18}, // 30
{16,17,18,19}}; // 31
const int32_t ucShiftd[32][4] = {
{-11,-10,-9,-8}, // 0
{-10,-9,-8,-7}, // 1
{-9,-8,-7,-6}, // 2
{-8,-7,-6,-5}, // 3
{-7,-6,-5,-4}, // 4
{-6,-5,-4,-3}, // 5
{-5,-4,-3,-2}, // 6
{-4,-3,-2,-1}, // 7
{-3,-2,-1,0}, // 8
{-2,-1,0,1}, // 9
{-1,0,1,2}, // 10
{0,1,2,3}, // 11
{1,2,3,4}, // 12
{2,3,4,5}, // 13
{3,4,5,6}, // 14
{4,5,6,7}, // 15
{5,6,7,8}, // 16
{6,7,8,9}, // 17
{7,8,9,10}, // 18
{8,9,10,11}, // 19
{9,10,11,12}, // 20
{10,11,12,13}, // 21
{11,12,13,14}, // 22
{12,13,14,15}, // 23
{13,14,15,16}, // 24
{14,15,16,17}, // 25
{15,16,17,18}, // 26
{16,17,18,19}, // 27
{17,18,19,20}, // 28
{18,19,20,21}, // 29
{19,20,21,22}, // 30
{20,21,22,23}};// 31
const int32_t ucShifte[32][4] = {
{-23,-22,-21,-20}, // 0
{-22,-21,-20,-19}, // 1
{-21,-20,-19,-18}, // 2
{-20,-19,-18,-17}, // 3
{-19,-18,-17,-16}, // 4
{-18,-17,-16,-15}, // 5
{-17,-16,-15,-14}, // 6
{-16,-15,-14,-13}, // 7
{-15,-14,-13,-12}, // 8
{-14,-13,-12,-11}, // 9
{-13,-12,-11,-10}, // 10
{-12,-11,-10,-9}, // 11
{-11,-10,-9,-8}, // 12
{-10,-9,-8,-7}, // 13
{-9,-8,-7,-6}, // 14
{-8,-7,-6,-5}, // 15
{-7,-6,-5,-4}, // 16
{-6,-5,-4,-3}, // 17
{-5,-4,-3,-2}, // 18
{-4,-3,-2,-1}, // 19
{-3,-2,-1,0}, // 20
{-2,-1,0,1}, // 21
{-1,0,1,2}, // 22
{0,1,2,3}, // 23
{1,2,3,4}, // 24
{2,3,4,5}, // 25
{3,4,5,6}, // 26
{4,5,6,7}, // 27
{5,6,7,8}, // 28
{6,7,8,9}, // 29
{7,8,9,10}, // 30
{8,9,10,11}}; // 31
const int32_t ucShiftf[32][4] = {
{-19,-18,-17,-16}, // 0
{-18,-17,-16,-15}, // 1
{-17,-16,-15,-14}, // 2
{-16,-15,-14,-13}, // 3
{-15,-14,-13,-12}, // 4
{-14,-13,-12,-11}, // 5
{-13,-12,-11,-10}, // 6
{-12,-11,-10,-9}, // 7
{-11,-10,-9,-8}, // 8
{-10,-9,-8,-7}, // 9
{-9,-8,-7,-6}, // 10
{-8,-7,-6,-5}, // 11
{-7,-6,-5,-4}, // 12
{-6,-5,-4,-3}, // 13
{-5,-4,-3,-2}, // 14
{-4,-3,-2,-1}, // 15
{-3,-2,-1,0}, // 16
{-2,-1,0,1}, // 17
{-1,0,1,2}, // 18
{0,1,2,3}, // 19
{1,2,3,4}, // 20
{2,3,4,5}, // 21
{3,4,5,6}, // 22
{4,5,6,7}, // 23
{5,6,7,8}, // 24
{6,7,8,9}, // 25
{7,8,9,10}, // 26
{8,9,10,11}, // 27
{9,10,11,12}, // 28
{10,11,12,13}, // 29
{11,12,13,14}, // 30
{12,13,14,15}}; // 31
const int32_t ucShiftg[32][4] = {
{-31,-30,-29,-28},
{-30,-29,-28,-27},
{-29,-28,-27,-26},
{-28,-27,-26,-25},
{-27,-26,-25,-24},
{-26,-25,-24,-23},
{-25,-24,-23,-22},
{-24,-23,-22,-21},
{-23,-22,-21,-20}, // 0
{-22,-21,-20,-19}, // 1
{-21,-20,-19,-18}, // 2
{-20,-19,-18,-17}, // 3
{-19,-18,-17,-16}, // 4
{-18,-17,-16,-15}, // 5
{-17,-16,-15,-14}, // 6
{-16,-15,-14,-13}, // 7
{-15,-14,-13,-12}, // 8
{-14,-13,-12,-11}, // 9
{-13,-12,-11,-10}, // 10
{-12,-11,-10,-9}, // 11
{-11,-10,-9,-8}, // 12
{-10,-9,-8,-7}, // 13
{-9,-8,-7,-6}, // 14
{-8,-7,-6,-5}, // 15
{-7,-6,-5,-4}, // 16
{-6,-5,-4,-3}, // 17
{-5,-4,-3,-2}, // 18
{-4,-3,-2,-1}, // 19
{-3,-2,-1,0}, // 20
{-2,-1,0,1}, // 21
{-1,0,1,2}, // 22
{0,1,2,3}}; // 23
const int32_t ucShifth[32][4] = {
{-27,-26,-25,-24}, // 0
{-26,-25,-24,-23}, // 1
{-25,-24,-23,-22}, // 2
{-24,-23,-22,-21}, // 3
{-23,-22,-21,-20}, // 4
{-22,-21,-20,-19}, // 5
{-21,-20,-19,-18}, // 6
{-20,-19,-18,-17}, // 7
{-19,-18,-17,-16}, // 8
{-18,-17,-16,-15}, // 9
{-17,-16,-15,-14}, // 10
{-16,-15,-14,-13}, // 11
{-15,-14,-13,-12}, // 12
{-14,-13,-12,-11}, // 13
{-13,-12,-11,-10}, // 14
{-12,-11,-10,-9}, // 15
{-11,-10,-9,-8}, // 16
{-10,-9,-8,-7}, // 17
{-9,-8,-7,-6}, // 18
{-8,-7,-6,-5}, // 19
{-7,-6,-5,-4}, // 20
{-6,-5,-4,-3}, // 21
{-5,-4,-3,-2}, // 22
{-4,-3,-2,-1}, // 23
{-3,-2,-1,0}, // 24
{-2,-1,0,1}, // 25
{-1,0,1,2}, // 26
{0,1,2,3}, // 27
{1,2,3,4}, // 28
{2,3,4,5}, // 29
{3,4,5,6}, // 30
{4,5,6,7}}; // 31
const uint32_t __attribute__ ((aligned (16))) masksa[4] = {0x80,0x40,0x20,0x10};
const uint32_t __attribute__ ((aligned (16))) masksb[4] = {0x8,0x4,0x2,0x1};
const uint32_t __attribute__ ((aligned (16))) masksc[4] = {0x8000,0x4000,0x2000,0x1000};
const uint32_t __attribute__ ((aligned (16))) masksd[4] = {0x800,0x400,0x200,0x100};
const uint32_t __attribute__ ((aligned (16))) maskse[4] = {0x800000,0x400000,0x200000,0x100000};
const uint32_t __attribute__ ((aligned (16))) masksf[4] = {0x80000,0x40000,0x20000,0x10000};
const uint32_t __attribute__ ((aligned (16))) masksg[4] = {0x80000000,0x40000000,0x20000000,0x10000000};
const uint32_t __attribute__ ((aligned (16))) masksh[4] = {0x8000000,0x4000000,0x2000000,0x1000000};
int32x4_t vshifta[32],vshiftb[32],vshiftc[32],vshiftd[32],vshifte[32],vshiftf[32],vshiftg[32],vshifth[32];
uint32x4_t vmasksa = vld1q_u32(masksa);
uint32x4_t vmasksb = vld1q_u32(masksb);
uint32x4_t vmasksc = vld1q_u32(masksc);
uint32x4_t vmasksd = vld1q_u32(masksd);
uint32x4_t vmaskse = vld1q_u32(maskse);
uint32x4_t vmasksf = vld1q_u32(masksf);
uint32x4_t vmasksg = vld1q_u32(masksg);
uint32x4_t vmasksh = vld1q_u32(masksh);
uint32x4_t in;
for (int n=0;n<32;n++) {
vshifta[n] = vld1q_s32(ucShifta[n]);
vshiftb[n] = vld1q_s32(ucShiftb[n]);
vshiftc[n] = vld1q_s32(ucShiftc[n]);
vshiftd[n] = vld1q_s32(ucShiftd[n]);
vshifte[n] = vld1q_s32(ucShifte[n]);
vshiftf[n] = vld1q_s32(ucShiftf[n]);
vshiftg[n] = vld1q_s32(ucShiftg[n]);
vshifth[n] = vld1q_s32(ucShifth[n]);
}
i2=0;
int j0=0,j1=0,j2=0,j3=0;
if (n_segments <= 32) {
j0=n_segments;
}
else if (n_segments <= 64) {
j0=32;
j1=n_segments-32;
}
else if (n_segments <= 96) {
j0=32; j1=32;
j2=n_segments-64;
}
else if (n_segments <= 128) {
j0=32; j1=32; j2=32;
j3=n_segments-96;
}
uint32x4_t *ccp,cc0,cc1,cc2,cc3,cc4,cc5,cc6,cc7;
for (int i=0; i < 264; i++,i2+=8) {
in = vdupq_n_u32(((uint32_t*)input[0])[i]);
cc0 = vshlq_u32(vandq_u32(in,vmasksa),vshifta[0]);
cc1 = vshlq_u32(vandq_u32(in,vmasksb),vshiftb[0]);
cc2 = vshlq_u32(vandq_u32(in,vmasksc),vshiftc[0]);
cc3 = vshlq_u32(vandq_u32(in,vmasksd),vshiftd[0]);
cc4 = vshlq_u32(vandq_u32(in,vmaskse),vshifte[0]);
cc5 = vshlq_u32(vandq_u32(in,vmasksf),vshiftf[0]);
cc6 = vshlq_u32(vandq_u32(in,vmasksg),vshiftg[0]);
cc7 = vshlq_u32(vandq_u32(in,vmasksh),vshifth[0]);
for (int j = 1; j < j0; j++) {
in = vdupq_n_u32(((uint32_t*)input[j])[i]);
cc0 = vorrq_u32(cc0,vshlq_u32(vandq_u32(in,vmasksa),vshifta[j]));
cc1 = vorrq_u32(cc1,vshlq_u32(vandq_u32(in,vmasksb),vshiftb[j]));
cc2 = vorrq_u32(cc2,vshlq_u32(vandq_u32(in,vmasksc),vshiftc[j]));
cc3 = vorrq_u32(cc3,vshlq_u32(vandq_u32(in,vmasksd),vshiftd[j]));
cc4 = vorrq_u32(cc4,vshlq_u32(vandq_u32(in,vmaskse),vshifte[j]));
cc5 = vorrq_u32(cc5,vshlq_u32(vandq_u32(in,vmasksf),vshiftf[j]));
cc6 = vorrq_u32(cc6,vshlq_u32(vandq_u32(in,vmasksg),vshiftg[j]));
cc7 = vorrq_u32(cc7,vshlq_u32(vandq_u32(in,vmasksh),vshifth[j]));
}
ccp=&((uint32x4_t *)cc[0])[i2];
ccp[0] = cc0;
ccp[1] = cc1;
ccp[2] = cc2;
ccp[3] = cc3;
ccp[4] = cc4;
ccp[5] = cc5;
ccp[6] = cc6;
ccp[7] = cc7;
if (j1>0) {
in = vdupq_n_u32(((uint32_t*)input[32])[i]);
cc0 = vshlq_u32(vandq_u32(in,vmasksa),vshifta[0]);
cc1 = vshlq_u32(vandq_u32(in,vmasksb),vshiftb[0]);
cc2 = vshlq_u32(vandq_u32(in,vmasksc),vshiftc[0]);
cc3 = vshlq_u32(vandq_u32(in,vmasksd),vshiftd[0]);
cc4 = vshlq_u32(vandq_u32(in,vmaskse),vshifte[0]);
cc5 = vshlq_u32(vandq_u32(in,vmasksf),vshiftf[0]);
cc6 = vshlq_u32(vandq_u32(in,vmasksg),vshiftg[0]);
cc7 = vshlq_u32(vandq_u32(in,vmasksh),vshifth[0]);
for (int j = 1; j < j1; j++) {
in = vdupq_n_u32(((uint32_t*)input[32+j])[i]);
cc0 = vorrq_u32(cc0,vshlq_u32(vandq_u32(in,vmasksa),vshifta[j]));
cc1 = vorrq_u32(cc1,vshlq_u32(vandq_u32(in,vmasksb),vshiftb[j]));
cc2 = vorrq_u32(cc2,vshlq_u32(vandq_u32(in,vmasksc),vshiftc[j]));
cc3 = vorrq_u32(cc3,vshlq_u32(vandq_u32(in,vmasksd),vshiftd[j]));
cc4 = vorrq_u32(cc4,vshlq_u32(vandq_u32(in,vmaskse),vshifte[j]));
cc5 = vorrq_u32(cc5,vshlq_u32(vandq_u32(in,vmasksf),vshiftf[j]));
cc6 = vorrq_u32(cc6,vshlq_u32(vandq_u32(in,vmasksg),vshiftg[j]));
cc7 = vorrq_u32(cc7,vshlq_u32(vandq_u32(in,vmasksh),vshifth[j]));
}
ccp=&((uint32x4_t *)cc[1])[i2];
ccp[0] = cc0;
ccp[1] = cc1;
ccp[2] = cc2;
ccp[3] = cc3;
ccp[4] = cc4;
ccp[5] = cc5;
ccp[6] = cc6;
ccp[7] = cc7;
}
if (j2>0) {
in = vdupq_n_u32(((uint32_t*)input[64])[i]);
cc0 = vshlq_u32(vandq_u32(in,vmasksa),vshifta[0]);
cc1 = vshlq_u32(vandq_u32(in,vmasksb),vshiftb[0]);
cc2 = vshlq_u32(vandq_u32(in,vmasksc),vshiftc[0]);
cc3 = vshlq_u32(vandq_u32(in,vmasksd),vshiftd[0]);
cc4 = vshlq_u32(vandq_u32(in,vmaskse),vshifte[0]);
cc5 = vshlq_u32(vandq_u32(in,vmasksf),vshiftf[0]);
cc6 = vshlq_u32(vandq_u32(in,vmasksg),vshiftg[0]);
cc7 = vshlq_u32(vandq_u32(in,vmasksh),vshifth[0]);
for (int j = 1; j < j2; j++) {
in = vdupq_n_u32(((uint32_t*)input[64+j])[i]);
cc0 = vorrq_u32(cc0,vshlq_u32(vandq_u32(in,vmasksa),vshifta[j]));
cc1 = vorrq_u32(cc1,vshlq_u32(vandq_u32(in,vmasksb),vshiftb[j]));
cc2 = vorrq_u32(cc2,vshlq_u32(vandq_u32(in,vmasksc),vshiftc[j]));
cc3 = vorrq_u32(cc3,vshlq_u32(vandq_u32(in,vmasksd),vshiftd[j]));
cc4 = vorrq_u32(cc4,vshlq_u32(vandq_u32(in,vmaskse),vshifte[j]));
cc5 = vorrq_u32(cc5,vshlq_u32(vandq_u32(in,vmasksf),vshiftf[j]));
cc6 = vorrq_u32(cc6,vshlq_u32(vandq_u32(in,vmasksg),vshiftg[j]));
cc7 = vorrq_u32(cc7,vshlq_u32(vandq_u32(in,vmasksh),vshifth[j]));
}
ccp=&((uint32x4_t *)cc[2])[i2];
ccp[0] = cc0;
ccp[1] = cc1;
ccp[2] = cc2;
ccp[3] = cc3;
ccp[4] = cc4;
ccp[5] = cc5;
ccp[6] = cc6;
ccp[7] = cc7;
}
if (j3>0) {
in = vdupq_n_u32(((uint32_t*)input[96])[i]);
cc0 = vshlq_u32(vandq_u32(in,vmasksa),vshifta[0]);
cc1 = vshlq_u32(vandq_u32(in,vmasksb),vshiftb[0]);
cc2 = vshlq_u32(vandq_u32(in,vmasksc),vshiftc[0]);
cc3 = vshlq_u32(vandq_u32(in,vmasksd),vshiftd[0]);
cc4 = vshlq_u32(vandq_u32(in,vmaskse),vshifte[0]);
cc5 = vshlq_u32(vandq_u32(in,vmasksf),vshiftf[0]);
cc6 = vshlq_u32(vandq_u32(in,vmasksg),vshiftg[0]);
cc7 = vshlq_u32(vandq_u32(in,vmasksh),vshifth[0]);
for (int j = 1; j < j3; j++) {
in = vdupq_n_u32(((uint32_t*)input[96+j])[i]);
cc0 = vorrq_u32(cc0,vshlq_u32(vandq_u32(in,vmasksa),vshifta[j]));
cc1 = vorrq_u32(cc1,vshlq_u32(vandq_u32(in,vmasksb),vshiftb[j]));
cc2 = vorrq_u32(cc2,vshlq_u32(vandq_u32(in,vmasksc),vshiftc[j]));
cc3 = vorrq_u32(cc3,vshlq_u32(vandq_u32(in,vmasksd),vshiftd[j]));
cc4 = vorrq_u32(cc4,vshlq_u32(vandq_u32(in,vmaskse),vshifte[j]));
cc5 = vorrq_u32(cc5,vshlq_u32(vandq_u32(in,vmasksf),vshiftf[j]));
cc6 = vorrq_u32(cc6,vshlq_u32(vandq_u32(in,vmasksg),vshiftg[j]));
cc7 = vorrq_u32(cc7,vshlq_u32(vandq_u32(in,vmasksh),vshifth[j]));
}
ccp=&((uint32x4_t *)cc[3])[i2];
ccp[0] = cc0;
ccp[1] = cc1;
ccp[2] = cc2;
ccp[3] = cc3;
ccp[4] = cc4;
ccp[5] = cc5;
ccp[6] = cc6;
ccp[7] = cc7;
}
}
for (int s=0;s<n_inputs;s++) {
for (int i1 = 0; i1 < 22; i1++) {
memcpy(&c[s][2 * i1 * 384] , &cc[s][i1 * 384], 384 * sizeof(uint32_t));
memcpy(&c[s][(2 * i1 + 1) * 384], &cc[s][i1 * 384], 384 * sizeof(uint32_t));
}
}
#endif
}

View File

@@ -0,0 +1,300 @@
/*
* 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
*/
#include <stdio.h>
#include "openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.h"
__device__ __forceinline__ int clamp_i16_to_i8(int x)
{
// x is int (promoted)
x = (x < -128) ? -128 : x;
x = (x > 127) ? 127 : x;
return x;
}
__device__ __forceinline__ uint32_t packs_4x16_to_4x8(uint32_t a, uint32_t b)
{
int a0 = (int)(int16_t)(a & 0xFFFFu);
int a1 = (int)(int16_t)(a >> 16);
int b0 = (int)(int16_t)(b & 0xFFFFu);
int b1 = (int)(int16_t)(b >> 16);
uint32_t o0 = (uint8_t)(int8_t)clamp_i16_to_i8(a0);
uint32_t o1 = (uint8_t)(int8_t)clamp_i16_to_i8(a1);
uint32_t o2 = (uint8_t)(int8_t)clamp_i16_to_i8(b0);
uint32_t o3 = (uint8_t)(int8_t)clamp_i16_to_i8(b1);
return (o0) | (o1 << 8) | (o2 << 16) | (o3 << 24);
}
__device__ __forceinline__ uint16_t packs_2x16_to_2x8(uint32_t a)
{
int a0 = (int)(int16_t)(a & 0xFFFFu);
int a1 = (int)(int16_t)(a >> 16);
uint16_t o0 = (uint8_t)(int8_t)clamp_i16_to_i8(a0);
uint16_t o1 = (uint8_t)(int8_t)clamp_i16_to_i8(a1);
return (o0) | (o1 << 8) ;
}
__global__ void rm(int Ncb_4,int ind0_4, int E1, int E2, int r_firstE2, int Foffset_4, int F_4, int clear, int seglen_4, int K_4, int Z_4, uint32_t *d, uint32_t *e, uint32_t *llr_buffer) {
int ind_4 = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
uint32_t *er = e + ((r<r_firstE2) ? r*(E1>>1) : ((r_firstE2*E1)+(r-r_firstE2)*E2)>>1);
uint32_t *dr = d + (2*r*seglen_4);
uint32_t *llr_bufferr = llr_buffer + (r*seglen_4);
#ifdef DEBUG_RM
if (blockIdx.x == 0 && threadIdx.x == 0) printf("r %d, seglen %d\n",r,seglen_4);
#endif
if (ind_4>= Foffset_4 && ind_4 < Foffset_4+F_4) {
llr_bufferr[ind_4-Foffset_4+K_4-F_4] = 0x7f7f7f7f;
#ifdef DEBUG_RM
if (r==0) printf("writing 0x7f7f7f7f to position %d (ind %d, K %d, Foffset %d, F %d)\n",seglen_4*r + ind_4-Foffset_4+K_4-F_4,ind_4,K_4,Foffset_4,F_4);
#endif
return;
}
if (ind_4 >= Ncb_4) return;
if (clear == 1) { dr[2*ind_4] = 0; dr[(2*ind_4)+1]=0; }
int E_4;
if (r<r_firstE2) E_4=(E1/4); else E_4=(E2/4);
int ind1=ind0_4,ind2;
int k=0;
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0) printf("check 1a: ind %d ind1 %d Foffset %d\n",ind,ind1,Foffset);
#endif
if (ind1 < Foffset_4) {
int ind2 = ind1 + min(Foffset_4-ind1,E_4);
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0) printf("check 1b: ind %d ind1 %d ind2 %d\n",ind,ind1,ind2);
#endif
if (ind_4 >= ind1 && ind_4 < ind2) {
dr[2*ind_4] = __vaddss2(dr[2*ind_4],er[2*(ind_4-ind1)]);
dr[(2*ind_4)+1] = __vaddss2(dr[(2*ind_4)+1],er[2*(ind_4-ind1)+1]);
}
#ifdef DEBUG_RM
if (r<=1 && threadIdx.x == 0 && ind_4 >= ind1 && ind_4 < ind2) printf("write 1. r %d : ind %d, ind1 %d, ind2 %d,k %d/E %d, pos %d, er %d %d\n",r,ind_4,ind1,ind2,ind_4-ind1,E_4,4*(ind_4-ind1),(int16_t)(er[2*(ind_4-ind1)]&0xffff),(int16_t)(er[2*(ind_4-ind1)]>>16));
#endif
k=ind2-ind1;
ind1 = ind2;
}
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0) printf("check 2a: ind %d ind1 %d Foffset %d Foffset+F %d\n",ind,ind1,Foffset,Foffset+F);
#endif
if (ind1 >= Foffset_4 && ind1 < Foffset_4 + F_4) ind1 = Foffset_4 + F_4;
ind2 = ind1 + min(Ncb_4-ind1,E_4-k);
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0) printf("check 2b: ind %d ind1 %d ind2 %d\n",ind_4,ind1,ind2);
#endif
if (ind_4 >= ind1 && ind_4 < ind2) {
dr[2*ind_4] = __vaddss2(dr[2*ind_4],er[2*(k+(ind_4-ind1))]);
dr[(2*ind_4)+1] = __vaddss2(dr[(2*ind_4)+1],er[2*(k+(ind_4-ind1))+1]);
}
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0 && ind >= ind1 && ind < ind2) printf("write 2. ind %d, ind1 %d, ind2 %d, k %d/E %d\n",ind,ind1,ind2,k+ind-ind1,E);
#endif
k+=(ind2-ind1);
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0) printf("check k %d E %d\n",k,E);
#endif
while (k < E_4) {
ind2 = min(Foffset_4,E_4-k);
if (ind_4 < ind2) {
dr[2*ind_4] = __vaddss2(dr[2*ind_4],er[2*(k+ind_4)]);
dr[(2*ind_4)+1] = __vaddss2(dr[(2*ind_4)+1],er[2*(k+ind_4)+1]);
}
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0 && ind < ind2 && ind >= ind1) printf("3. ind %d, ind2 %d, k %d/E %d\n",ind,ind2,k+ind,E);
#endif
k+=ind2;
ind1=Foffset_4+F_4;
ind2 = ind1 + min(Ncb_4-ind1,E_4-k);
if (ind_4 >= ind1 && ind_4 < ind2 && k < E_4) {
dr[2*ind_4] = __vaddss2(dr[2*ind_4],er[2*(k+ind_4-ind1)]);
dr[(2*ind_4)+1] = __vaddss2(dr[(2*ind_4)+1],er[2*(k+ind_4-ind1)+1]);
}
#ifdef DEBUG_RM
if (r==0 && threadIdx.x == 0 && ind < ind && ind >= ind1) printf("4. ind %d, ind1 %d, ind2 %d, k %d/E %d\n",ind,ind1,ind2,k+ind-ind1,E);
#endif
k+=(ind2-ind1);
}
// note the offset here is such that when ind < Foffset = Kprime - 2Z, the output is put in position r*seglen + (2Z ... Kprime) and when ind > Foffset+F, it is in position r*seglen + (Kprime+F = K .. 2Z+(66*Z)=seglen
llr_bufferr[2*Z_4 + ind_4] = packs_4x16_to_4x8(dr[(2*ind_4)], dr[(2*ind_4)+1]);
#ifdef DEBUG_RM
if (r<=1 && threadIdx.x == 0) printf("RM segment %d: writing %x to position %d (ind %d)\n",r,llr_bufferr[2*Z_4 + ind_4],r*seglen_4 + 2*Z_4 + ind_4,ind_4);
#endif
}
__global__ void rm2(int Ncb_2,int ind0_2, int E1, int E2, int r_firstE2, int Foffset_2, int F_2, int clear, int seglen_2, int K_2, int Z_2, uint32_t *d, uint32_t *e, uint16_t *llr_buffer) {
int ind_2 = (int)(blockIdx.x * blockDim.x + threadIdx.x);
int r = (int)blockIdx.y;
uint32_t *er = e + ((r<r_firstE2) ? r*(E1>>1) : ((r_firstE2*E1)+(r-r_firstE2)*E2)>>1);
uint32_t *dr = d + (r*seglen_2);
uint16_t *llr_bufferr = llr_buffer + (r*seglen_2);
if (ind_2 >= Ncb_2) return;
#ifdef RM_DEBUG
if (r==0) printf("Looking for ind %d...%d, Ncb %d\n",ind_2*2,1+ind_2*2,Ncb_2*2);
#endif
if (ind_2>= Foffset_2 && ind_2 < Foffset_2+F_2) {
llr_bufferr[ind_2-Foffset_2+K_2-F_2] = 0x7f7f;
#ifdef RM_DEBUG
if (r==0) printf("writing 0x7f7f7f7f to position %d (ind %d, K %d, Foffset %d, F %d)\n",(seglen_2*r + ind_2-Foffset_2+K_2-F_2)*2,ind_2*2,K_2*2,Foffset_2*2,F_2*2);
#endif
return;
}
if (clear == 1) dr[ind_2] = 0;
int E_2;
if (r<r_firstE2) E_2=E1/2; else E_2=E2/2;
int ind1=ind0_2,ind2;
int k=0;
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check 1a: ind %d ind1 %d Foffset %d\n",ind,ind1,Foffset);
if (ind1 < Foffset_2) {
int ind2 = ind1 + min(Foffset_2-ind1,E_2);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check RM1B: ind %d ind1 %d ind2 %d k %d/E %d\n",4*ind,4*ind1,4*ind2,4*k,4*E);
if (ind_2 >= ind1 && ind_2 < ind2) {
dr[ind_2] = __vaddss2(dr[ind_2],er[ind_2-ind1]);
}
#ifdef RM_DEBUG
if (r==0 /*&& threadIdx.x == 0 && blockIdx.x == 0*/ && ind_2 >= ind1 && ind_2 < ind2) printf("RM1A: ind_2 %d, ind1 %d, ind2 %d,k %d/E %d\n",2*ind_2,2*ind1,2*ind2,2*(ind_2-ind1),2*E_2);
#endif
k=ind2-ind1;
ind1 = ind2;
}
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check 2a: ind %d ind1 %d Foffset %d Foffset+F %d\n",ind,ind1,Foffset,Foffset+F);
if (ind1 >= Foffset_2 && ind1 < Foffset_2 + F_2) ind1 = Foffset_2 + F_2;
ind2 = ind1 + min(Ncb_2-ind1,E_2-k);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check RM2B: ind %d ind1 %d ind2 %d k%d/E %d\n",4*ind,4*ind1,4*ind2,4*k,4*E);
if (ind_2 >= ind1 && ind_2 < ind2) {
dr[ind_2] = __vaddss2(dr[ind_2],er[(k+(ind_2-ind1))]);
}
#ifdef RM_DEBUG
if (r==0 /*&& threadIdx.x == 0 && blockIdx.x == 0*/ && ind_2 >= ind1 && ind_2 < ind2) printf("RM2A: ind %d, ind1 %d, ind2 %d, k %d/E %d\n",2*ind_2,2*ind1,2*ind2,2*(k+(ind-ind1)),2*E_2);
#endif
k+=(ind2-ind1);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check k %d E %d\n",k,E);
while (k < E_2) {
ind2 = min(Foffset_2,E_2-k);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check RM3B: ind %d ind1 %d ind2 %d k %d/E %d\n",4*ind,0,4*ind2,4*k,4*E);
if (ind_2 < ind2) {
dr[ind_2] = __vaddss2(dr[ind_2],er[(k+ind_2)]);
}
#ifdef RM_DEBUG
if (r==0 /*&& threadIdx.x == 0 && blockIdx.x == 0*/ && ind_2 < ind2 ) printf("RM3A: ind %d, ind2 %d, k %d/E %d\n",2*ind_2,2*ind2,2*(k+ind_2),2*E_2);
#endif
k+=ind2;
ind1=Foffset_2+F_2;
ind2 = ind1 + min(Ncb_2-ind1,E_2-k);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("check RM4B: ind %d ind1 %d ind2 %d k %d/E %d\n",4*ind,0,4*ind2,4*k,4*E);
if (ind_2 >= ind1 && ind_2 < ind2 && k < E_2) {
dr[ind_2] = __vaddss2(dr[ind_2],er[(k+ind_2-ind1)]);
}
#ifdef RM_DEBUG
if (r==0 /*&& threadIdx.x == 0 && blockIdx.x == 0*/ && ind_2 < ind2 && ind_2 >= ind1) printf("RM4A: ind %d, ind1 %d, ind2 %d, k %d/E %d\n",2*ind_2,2*ind1,2*ind2_2,2*(k+ind_2-ind1),2*E_2);
#endif
k+=(ind2-ind1);
}
// note the offset here is such that when ind < Foffset = Kprime - 2Z, the output is put in position r*seglen + (2Z ... Kprime) and when ind > Foffset+F, it is in potiion r*seglent + (Kprime+F = K .. 2Z+(66*Z)=seglen
llr_bufferr[2*Z_2 + ind_2] = packs_2x16_to_2x8(dr[ind_2]);
// if (r==0 && threadIdx.x == 0 && blockIdx.x == 0) printf("writing %x to position %d (ind %d)\n",llr_bufferr[2*Z + ind_2],r*seglen + 2*Z + ind_2,ind_2);
}
static const uint8_t index_k0[2][4] = {{0, 17, 33, 56}, {0, 13, 25, 43}};
extern "C" int nr_rate_matching_ldpc_rx_cuda(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
int16_t *d,
int16_t *soft_input,
int8_t *llr_buffer,
uint32_t K,
uint8_t C,
uint8_t rvidx,
uint8_t clear,
uint32_t E1,
uint32_t E2,
uint32_t r_firstE2,
uint32_t F,
uint32_t Foffset,
cudaStream_t *s,
int8_t sidx)
{
if (C == 0 || C>132) {
printf("nr_rate_matching: invalid parameter C %d\n", C);
exit(-1);
}
//Bit selection
uint32_t N = (BG == 1) ? (66 * Z) : (50 * Z);
uint32_t Ncb;
if (Tbslbrm == 0)
Ncb = N;
else {
uint32_t Nref = (3 * Tbslbrm / (2 * C)); //R_LBRM = 2/3
Ncb = min(N, Nref);
}
uint32_t ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z;
int nthreads=Z;
#ifdef RM_DEBUG
printf("\nrm (%d,%d): Ncb %d, ind %d, rvidx %d, E1 %d, E2 %d, Foffset %d, F %d, K %d, Z %d, clear %d\n",nblocks.x,nthreads,Ncb, ind, rvidx, E1, E2, Foffset, F,K,Z,clear);
#endif
if ((E1&3) == 0 && (E2&3) == 0) {
dim3 nblocks(((Ncb>>2) + nthreads-1)/nthreads,C);
rm<<<nblocks, nthreads, 0, s[sidx]>>>(Ncb/4,ind/4,E1,E2,r_firstE2,Foffset/4,F/4,clear,68*384/4,K/4,Z/4,(uint32_t*)d,(uint32_t*)soft_input,(uint32_t*)llr_buffer);
}
else {
dim3 nblocks2(((Ncb>>1) + nthreads-1)/nthreads,C);
rm2<<<nblocks2, nthreads, 0, s[sidx]>>>(Ncb/2,ind/2,E1,E2,r_firstE2,Foffset/2,F/2,clear,68*384/2,K/2,Z/2,(uint32_t*)d,(uint32_t*)soft_input,(uint16_t*)llr_buffer);
}
cudaError_t err=cudaPeekAtLastError();
if (err!=cudaSuccess) {
printf("cuda error (nr_rate_matching_ldpc_rx_cuda): %s Ncb %d, ind %d, rvidx %d, E1 %d, E2 %d, Foffset %d, F %d, K %d, Z %d, clear %d\n",cudaGetErrorString(err),Ncb, ind, rvidx, E1, E2, Foffset, F,K,Z,clear);
exit(-1);
}
return(0);
}

View File

@@ -0,0 +1,444 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file nrLDPC_CUDA_BnProcKernel_BG1.h
* \brief Defines the kernels for bit node processing
* \author Qizhi Pan, Raymond Knopp
* \company EURECOM
* \email: qizhi.pan@eurecom.fr, raymond.knopp@eurecom.fr
* \date 2025-12-30
* \version 1.0
* \note
* \warning
*/
#include <cuda_runtime.h>
#include <stdint.h>
#include <stdio.h>
#include "openair1/PHY/CODING/nrLDPC_decoder/nrLDPCdecoder_defs.h"
__device__ __forceinline__ void unpack_and_sign_extend(uint32_t packed, uint32_t *val_lo, uint32_t *val_hi)
{
uint32_t magic_sub = 0x00800080;
uint32_t lo_zext = __byte_perm(packed, 0, 0x5140);
*val_lo = __vsub2(lo_zext ^ magic_sub, magic_sub);
uint32_t hi_zext = __byte_perm(packed, 0, 0x7362);
*val_hi = __vsub2(hi_zext ^ magic_sub, magic_sub);
}
__device__ __forceinline__ uint32_t saturate_and_pack(uint32_t val_lo, uint32_t val_hi)
{
uint32_t lo_clamped = __vmins2(val_lo, 0x007F007F); // min(v, 127)
lo_clamped = __vmaxs2(lo_clamped, 0xFF80FF80); // max(v, -128)
uint32_t hi_clamped = __vmins2(val_hi, 0x007F007F);
hi_clamped = __vmaxs2(hi_clamped, 0xFF80FF80);
return __byte_perm(lo_clamped, hi_clamped, 0x6420);
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Node_R13(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t BnGrpIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t Bn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)BnGrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < BnGrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
uint32_t BricksToBeGet;
if (BnGrpIdx == 1) {
BricksToBeGet = packed_intrinsic;
uint32_t MsgIdx = Bn2MsgStartIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R13[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R13[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
} else {
for (int temp_MsgIdx = 0; temp_MsgIdx < BnGrpIdx; temp_MsgIdx++) {
uint32_t prevIdxWords = (temp_MsgIdx * GrpNum * NR_LDPC_ZMAX) >> 2;
uint32_t prev = bnProcBufPtr[prevIdxWords];
BricksToBeGet = __vsubss4(saturated_llr, prev);
uint32_t MsgIdx = Bn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R13[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R13[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
}
}
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Node_R23(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t BnGrpIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t Bn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)BnGrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < BnGrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
uint32_t BricksToBeGet;
if (BnGrpIdx == 1) {
BricksToBeGet = packed_intrinsic;
uint32_t MsgIdx = Bn2MsgStartIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R23[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R23[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
} else {
for (int temp_MsgIdx = 0; temp_MsgIdx < BnGrpIdx; temp_MsgIdx++) {
uint32_t prevIdxWords = (temp_MsgIdx * GrpNum * NR_LDPC_ZMAX) >> 2;
uint32_t prev = bnProcBufPtr[prevIdxWords];
BricksToBeGet = __vsubss4(saturated_llr, prev);
uint32_t MsgIdx = Bn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R23[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R23[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
}
}
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Node_R89(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t BnGrpIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t Bn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)BnGrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < BnGrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
uint32_t BricksToBeGet;
if (BnGrpIdx == 1) {
BricksToBeGet = packed_intrinsic;
uint32_t MsgIdx = Bn2MsgStartIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R89[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R89[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
} else {
for (int temp_MsgIdx = 0; temp_MsgIdx < BnGrpIdx; temp_MsgIdx++) {
uint32_t prevIdxWords = (temp_MsgIdx * GrpNum * NR_LDPC_ZMAX) >> 2;
uint32_t prev = bnProcBufPtr[prevIdxWords];
BricksToBeGet = __vsubss4(saturated_llr, prev);
uint32_t MsgIdx = Bn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = bn_cn_map_BG1_Z_R89[MsgIdx][ZcIdx];
int8_t *p_cnProcBuf = (int8_t *)(d_cnProcBuf + bn_cn_map_BG1_Z_R89[MsgIdx][0]);
moveBricks_forput_circ(p_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
}
}
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Node_last(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t BnGrpIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t Bn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)BnGrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < BnGrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
((int32_t *)(d_llrRes))[lane] = saturated_llr;
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Edge(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t GrpIdx,
uint32_t MsgIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t circShift,
uint32_t Zc)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)GrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < GrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
uint32_t BricksToBeGet;
if (GrpIdx == 1) {
BricksToBeGet = packed_intrinsic;
} else {
uint32_t prevIdxWords = (MsgIdx * GrpNum * NR_LDPC_ZMAX) >> 2;
uint32_t prev = bnProcBufPtr[prevIdxWords];
BricksToBeGet = __vsubss4(saturated_llr, prev);
}
moveBricks_forput_circ(d_cnProcBuf, lane * 4, (uint8_t *)&BricksToBeGet, Zc, circShift);
}
__device__ __forceinline__ void bnProcKernel_BG1_int8_Gn_Edge_last(const int8_t *__restrict__ d_bnProcBuf,
int8_t *__restrict__ d_cnProcBuf,
const int8_t *__restrict__ d_llrProcBuf,
int8_t *__restrict__ d_llrRes,
uint32_t lane,
uint32_t GrpIdx,
uint32_t MsgIdx,
uint32_t BnIdx,
uint32_t GrpNum,
uint32_t circShift,
uint32_t Zc)
{
const int32_t *bnProcBufPtr = (const int32_t *)(d_bnProcBuf) + lane;
uint32_t packed_intrinsic = ((const int32_t *)(d_llrProcBuf))[lane];
uint32_t MsgSumLo, MsgSumHi;
unpack_and_sign_extend(packed_intrinsic, &MsgSumLo, &MsgSumHi);
uint32_t off = (GrpNum * NR_LDPC_ZMAX) >> 2;
const int32_t *currPtr = bnProcBufPtr;
int i = 0;
// --- 2-Way Unroll ---
for (; i < (int)GrpIdx - 1; i += 2) {
uint32_t val1 = *currPtr;
uint32_t val2 = *(currPtr + off);
uint32_t v1_lo, v1_hi;
unpack_and_sign_extend(val1, &v1_lo, &v1_hi);
MsgSumLo = __vaddss2(MsgSumLo, v1_lo);
MsgSumHi = __vaddss2(MsgSumHi, v1_hi);
uint32_t v2_lo, v2_hi;
unpack_and_sign_extend(val2, &v2_lo, &v2_hi);
MsgSumLo = __vaddss2(MsgSumLo, v2_lo);
MsgSumHi = __vaddss2(MsgSumHi, v2_hi);
currPtr += (off << 1);
}
if (i < GrpIdx) {
uint32_t val = *currPtr;
uint32_t v_lo, v_hi;
unpack_and_sign_extend(val, &v_lo, &v_hi);
MsgSumLo = __vaddss2(MsgSumLo, v_lo);
MsgSumHi = __vaddss2(MsgSumHi, v_hi);
}
uint32_t saturated_llr = saturate_and_pack(MsgSumLo, MsgSumHi);
if (MsgIdx == 0) {
((int32_t *)(d_llrRes))[lane] = saturated_llr;
}
}

View File

@@ -0,0 +1,566 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file nrLDPC_CUDA_CnProcKernel_BG1.h
* \brief Defines the kernels for check node processing
* \author Qizhi Pan, Raymond Knopp
* \company EURECOM
* \email: qizhi.pan@eurecom.fr, raymond.knopp@eurecom.fr
* \date 2025-12-30
* \version 1.0
* \note
* \warning
*/
#pragma once
#include <cuda_runtime.h>
#include <stdint.h>
#include <stdio.h>
#include "nrLDPC_CUDA_public.h"
#include "nrLDPC_CUDA_lut.h"
__device__ __forceinline__ void cnProcKernel_BG1_int8_Gn_R13_node(const int8_t *__restrict__ d_cnProcBuf,
int8_t *__restrict__ d_bnProcBuf,
uint32_t lane,
uint32_t CnIdx,
uint32_t CnNumInGrp,
uint32_t CnGrpIdxNum,
uint32_t Cn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
uint32_t min1 = 0x7F7F7F7F;
uint32_t min2 = 0x7F7F7F7F;
uint32_t total_xor = 0;
uint32_t cache_raw[19];
uint32_t cache_abs[19];
const int32_t *cnProcBufPtr = (const int32_t *)(d_cnProcBuf) + lane;
const int32_t *currPtr = cnProcBufPtr;
uint32_t offset = (CnNumInGrp * NR_LDPC_ZMAX) >> 2;
#pragma unroll
for (int MsgIdx = 0; MsgIdx < CnGrpIdxNum; MsgIdx++) {
uint32_t val = *currPtr;
cache_raw[MsgIdx] = val;
uint32_t v_abs = __vabs4(val);
cache_abs[MsgIdx] = v_abs;
total_xor = __vxor4(total_xor, val);
uint32_t old_min1 = min1;
min1 = __vminu4(old_min1, v_abs);
uint32_t candidate = __vmaxu4(old_min1, v_abs);
min2 = __vminu4(min2, candidate);
currPtr += offset;
}
#pragma unroll
for (int temp_MsgIdx = 0; temp_MsgIdx < CnGrpIdxNum; temp_MsgIdx++) {
uint32_t target_sign = __vxor4(total_xor, cache_raw[temp_MsgIdx]);
uint32_t my_abs = cache_abs[temp_MsgIdx];
uint32_t is_min_mask = __vcmpeq4(my_abs, min1);
uint32_t final_mag = (min2 & is_min_mask) | (min1 & ~is_min_mask);
// final_mag = scale_int8x4(final_mag, 0.75);
uint32_t BricksToBeMoved = __vsign4(final_mag, target_sign);
uint32_t MsgIdx = Cn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = cn_bn_map_BG1_Z_R13[MsgIdx][ZcIdx];
int8_t *p_bnProcBuf = (int8_t *)(d_bnProcBuf + cn_bn_map_BG1_Z_R13[MsgIdx][0]);
moveBricks_invput_circ(p_bnProcBuf, lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_Gn_R23_node(const int8_t *__restrict__ d_cnProcBuf,
int8_t *__restrict__ d_bnProcBuf,
uint32_t lane,
uint32_t CnIdx,
uint32_t CnNumInGrp,
uint32_t CnGrpIdxNum,
uint32_t Cn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
uint32_t min1 = 0x7F7F7F7F;
uint32_t min2 = 0x7F7F7F7F;
uint32_t total_xor = 0;
uint32_t cache_raw[19];
uint32_t cache_abs[19];
const int32_t *cnProcBufPtr = (const int32_t *)(d_cnProcBuf) + lane;
const int32_t *currPtr = cnProcBufPtr;
uint32_t offset = (CnNumInGrp * NR_LDPC_ZMAX) >> 2;
#pragma unroll
for (int MsgIdx = 0; MsgIdx < CnGrpIdxNum; MsgIdx++) {
uint32_t val = *currPtr;
cache_raw[MsgIdx] = val;
uint32_t v_abs = __vabs4(val);
cache_abs[MsgIdx] = v_abs;
total_xor = __vxor4(total_xor, val);
uint32_t old_min1 = min1;
min1 = __vminu4(old_min1, v_abs);
uint32_t candidate = __vmaxu4(old_min1, v_abs);
min2 = __vminu4(min2, candidate);
currPtr += offset;
}
#pragma unroll
for (int temp_MsgIdx = 0; temp_MsgIdx < CnGrpIdxNum; temp_MsgIdx++) {
uint32_t target_sign = __vxor4(total_xor, cache_raw[temp_MsgIdx]);
uint32_t my_abs = cache_abs[temp_MsgIdx];
uint32_t is_min_mask = __vcmpeq4(my_abs, min1);
uint32_t final_mag = (min2 & is_min_mask) | (min1 & ~is_min_mask);
// final_mag = scale_int8x4(final_mag, 0.75);
uint32_t BricksToBeMoved = __vsign4(final_mag, target_sign);
uint32_t MsgIdx = Cn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = cn_bn_map_BG1_Z_R23[MsgIdx][ZcIdx];
int8_t *p_bnProcBuf = (int8_t *)(d_bnProcBuf + cn_bn_map_BG1_Z_R23[MsgIdx][0]);
moveBricks_invput_circ(p_bnProcBuf, lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_Gn_R89_node(const int8_t *__restrict__ d_cnProcBuf,
int8_t *__restrict__ d_bnProcBuf,
uint32_t lane,
uint32_t CnIdx,
uint32_t CnNumInGrp,
uint32_t CnGrpIdxNum,
uint32_t Cn2MsgStartIdx,
uint32_t Zc,
uint32_t ZcIdx)
{
uint32_t min1 = 0x7F7F7F7F;
uint32_t min2 = 0x7F7F7F7F;
uint32_t total_xor = 0;
uint32_t cache_raw[19];
uint32_t cache_abs[19];
const int32_t *cnProcBufPtr = (const int32_t *)(d_cnProcBuf) + lane;
const int32_t *currPtr = cnProcBufPtr;
uint32_t offset = (CnNumInGrp * NR_LDPC_ZMAX) >> 2;
#pragma unroll
for (int MsgIdx = 0; MsgIdx < CnGrpIdxNum; MsgIdx++) {
uint32_t val = *currPtr;
cache_raw[MsgIdx] = val;
uint32_t v_abs = __vabs4(val);
cache_abs[MsgIdx] = v_abs;
total_xor = __vxor4(total_xor, val);
uint32_t old_min1 = min1;
min1 = __vminu4(old_min1, v_abs);
uint32_t candidate = __vmaxu4(old_min1, v_abs);
min2 = __vminu4(min2, candidate);
currPtr += offset;
}
#pragma unroll
for (int temp_MsgIdx = 0; temp_MsgIdx < CnGrpIdxNum; temp_MsgIdx++) {
uint32_t target_sign = __vxor4(total_xor, cache_raw[temp_MsgIdx]);
uint32_t my_abs = cache_abs[temp_MsgIdx];
uint32_t is_min_mask = __vcmpeq4(my_abs, min1);
uint32_t final_mag = (min2 & is_min_mask) | (min1 & ~is_min_mask);
// final_mag = scale_int8x4(final_mag, 0.75);
uint32_t BricksToBeMoved = __vsign4(final_mag, target_sign);
uint32_t MsgIdx = Cn2MsgStartIdx + temp_MsgIdx;
uint32_t circShift = cn_bn_map_BG1_Z_R89[MsgIdx][ZcIdx];
int8_t *p_bnProcBuf = (int8_t *)(d_bnProcBuf + cn_bn_map_BG1_Z_R89[MsgIdx][0]);
moveBricks_invput_circ(p_bnProcBuf, lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G3(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG3[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
// loop starts here
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG3[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G4(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG4[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG4[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG4[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G5(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG5[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG5[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG5[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG5[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G6(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG6[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG6[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG6[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG6[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG6[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G7(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG7[row][5] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G8(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][5] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG8[row][6] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G9(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][5] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][6] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG9[row][7] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G10(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][5] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][6] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][7] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG10[row][8] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}
__device__ __forceinline__ void cnProcKernel_BG1_int8_G19(const int8_t *__restrict__ p_cnProcBuf,
int8_t *__restrict__ p_bnProcBuf,
uint32_t row,
uint32_t lane,
uint32_t idxBn,
uint32_t circShift,
uint32_t Zc)
{
uint32_t ymm0, sgn, min;
const uint32_t ones = 0x01010101;
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][0] * 4);
sgn = __vxor4(ones, ymm0);
min = __vabs4(ymm0);
//-------------------------loop starts here-------------------------------
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][1] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][2] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][3] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][4] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][5] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][6] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][7] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][8] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][9] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][10] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][11] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][12] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][13] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][14] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][15] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][16] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
ymm0 = *(const uint32_t *)(p_cnProcBuf + lane * 4 + c_lut_idxG19[row][17] * 4);
min = __vminu4(min, __vabs4(ymm0));
sgn = __vxor4(sgn, ymm0);
//-------------------------------------------------------------------------
uint32_t BricksToBeMoved = __vsign4(min, sgn);
moveBricks_invput_circ((int8_t *)&p_bnProcBuf[idxBn], lane * 4, (uint8_t *)&BricksToBeMoved, Zc, circShift);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file nrLDPC_CUDA_mPassKernel.h
* \brief Defines the kernels for message passing in CUDA version of LDPC decoder
* \author Qizhi Pan, Raymond Knopp
* \company EURECOM
* \email: qizhi.pan@eurecom.fr, raymond.knopp@eurecom.fr
* \date 2025-12-30
* \version 1.0
* \note
* \warning
*/
#pragma once
#include <cuda_runtime.h>
#include <stdint.h>
#include <stdio.h>
#include "nrLDPC_CUDA_public.h"
#include "nrLDPC_CUDA_shared_param.h"
__device__ __forceinline__ void llrPreProc_Kernel_BG1_int8_Gn_stream(const int8_t *p_llr,
int8_t *p_llrProcBuf,
int8_t *p_cnProcBuf,
uint32_t MsgIdx,
uint32_t lane,
uint32_t colIdx,
uint32_t idxBn,
uint32_t GrpIdx,
uint32_t circShift,
uint32_t Zc,
uint32_t R)
{
{
uint32_t *p_cnProcBufBit;
uint8_t bricksLocal[4];
uint8_t *BricksToBeMoved = bricksLocal;
p_cnProcBufBit = (uint32_t *)(p_cnProcBuf + d_lut_numCnInCnGroups_BG1_R13[GrpIdx] * NR_LDPC_ZMAX * MsgIdx + lane * 4);
moveBricks_invget_circ((int8_t *)&p_llr[idxBn * Zc], lane * 4, BricksToBeMoved, Zc, circShift);
*p_cnProcBufBit = *(uint32_t *)BricksToBeMoved;
}
if (colIdx >= 68)
return;
const uint8_t numBn2CnG1 =
(R == 13) ? d_lut_numBnInBnGroups_BG1_R13[0]
: ((R == 89) ? d_lut_numBnInBnGroups_BG1_R89[0] : d_lut_numBnInBnGroups_BG1_R23[0]); // for R13 is 42
const uint32_t startColParity = NR_LDPC_START_COL_PARITY_BG1; // 26 for BG1
const uint32_t colG1 = startColParity * Zc;
const uint32_t *lut_llr2llrProcBufAddr =
(R == 13) ? d_llr2llrProcBufAddr_BG1_R13 : ((R == 89) ? d_llr2llrProcBufAddr_BG1_R89 : d_llr2llrProcBufAddr_BG1_R23);
const uint32_t *lut_llr2llrProcBufBnPos =
(R == 13) ? d_llr2llrProcBufBnPos_BG1_R13 : ((R == 89) ? d_llr2llrProcBufBnPos_BG1_R89 : d_llr2llrProcBufBnPos_BG1_R23);
if (colIdx < startColParity) {
const uint32_t idxBn = lut_llr2llrProcBufAddr[colIdx] + lut_llr2llrProcBufBnPos[colIdx] * NR_LDPC_ZMAX;
int32_t *dst = (int32_t *)(&p_llrProcBuf[idxBn] + lane * 4);
int32_t *src = (int32_t *)(&p_llr[colIdx * Zc] + lane * 4);
*dst = *src;
} else {
colIdx = colIdx - startColParity;
if (numBn2CnG1 > 0 && colIdx < numBn2CnG1) {
int32_t *dst = (int32_t *)(&p_llrProcBuf[colIdx * NR_LDPC_ZMAX] + lane * 4);
int32_t *src = (int32_t *)(&p_llr[colG1 + colIdx * Zc] + lane * 4);
*dst = *src;
}
}
}
__device__ void llr2bit_Kernel_BG1_int8(uint32_t R,
uint8_t *__restrict__ out,
const int8_t *__restrict__ llrRes,
uint32_t numLLR,
uint32_t Zc)
{
uint32_t lane = threadIdx.x;
uint32_t outColIdx = (blockIdx.x << 2) + threadIdx.y;
if (outColIdx >= num_TotalBlocks_llr_llrRes)
return;
// Constants Setup
const uint8_t numBn2CnG1 = (R == 13) ? d_lut_numBnInBnGroups_BG1_R13[0]
: ((R == 89) ? d_lut_numBnInBnGroups_BG1_R89[0] : d_lut_numBnInBnGroups_BG1_R23[0]);
const uint32_t startColParity = NR_LDPC_START_COL_PARITY_BG1;
const uint32_t *lut_Addr =
(R == 13) ? d_llr2llrProcBufAddr_BG1_R13 : ((R == 89) ? d_llr2llrProcBufAddr_BG1_R89 : d_llr2llrProcBufAddr_BG1_R23);
const uint32_t *lut_Pos =
(R == 13) ? d_llr2llrProcBufBnPos_BG1_R13 : ((R == 89) ? d_llr2llrProcBufBnPos_BG1_R89 : d_llr2llrProcBufBnPos_BG1_R23);
int32_t raw_llrs;
// Load Data: Handle Systematic (Scatter/Gather) vs Parity (Linear) mapping
if (outColIdx < startColParity) {
uint32_t idxBn = lut_Addr[outColIdx] + lut_Pos[outColIdx] * NR_LDPC_ZMAX;
raw_llrs = *(const int32_t *)(&llrRes[idxBn] + lane * 4);
} else {
uint32_t srcParityIdx = outColIdx - startColParity;
if (numBn2CnG1 > 0 && outColIdx < numBn2CnG1) {
raw_llrs = *(const int32_t *)(llrRes + srcParityIdx * NR_LDPC_ZMAX + lane * 4);
} else {
raw_llrs = 0;
}
}
// Hard Decision: Convert 4 int8 LLRs -> 4 bytes (0 or 1)
int8_t *p_val = (int8_t *)&raw_llrs;
uint32_t my_word = 0;
#pragma unroll
for (int i = 0; i < 4; i++) {
// Hard decision: LLR < 0 implies bit 1
uint32_t byte_val = (p_val[i] < 0) ? 1 : 0;
my_word |= (byte_val << (i * 8));
}
// Store output linearly
*(uint32_t *)(&out[outColIdx * Zc + lane * 4]) = my_word;
}
__device__ void llr2bitPacked_Kernel_BG1_int8(uint32_t R,
uint8_t *__restrict__ out,
const int8_t *__restrict__ llrRes,
uint32_t numLLR,
uint32_t Zc)
{
uint32_t lane = threadIdx.x;
uint32_t outColIdx = (blockIdx.x << 2) + threadIdx.y;
if (outColIdx >= num_TotalBlocks_llr_llrRes)
return;
const uint8_t numBn2CnG1 = (R == 13) ? d_lut_numBnInBnGroups_BG1_R13[0]
: ((R == 89) ? d_lut_numBnInBnGroups_BG1_R89[0] : d_lut_numBnInBnGroups_BG1_R23[0]);
const uint32_t startColParity = NR_LDPC_START_COL_PARITY_BG1;
const uint32_t *lut_Addr =
(R == 13) ? d_llr2llrProcBufAddr_BG1_R13 : ((R == 89) ? d_llr2llrProcBufAddr_BG1_R89 : d_llr2llrProcBufAddr_BG1_R23);
const uint32_t *lut_Pos =
(R == 13) ? d_llr2llrProcBufBnPos_BG1_R13 : ((R == 89) ? d_llr2llrProcBufBnPos_BG1_R89 : d_llr2llrProcBufBnPos_BG1_R23);
int32_t raw_llrs;
// Load Data: Handle Systematic vs Parity mapping
if (outColIdx < startColParity) {
uint32_t idxBn = lut_Addr[outColIdx] + lut_Pos[outColIdx] * NR_LDPC_ZMAX;
raw_llrs = *(const int32_t *)(&llrRes[idxBn] + lane * 4);
} else {
uint32_t srcParityIdx = outColIdx - startColParity;
if (numBn2CnG1 > 0 && outColIdx < numBn2CnG1) {
raw_llrs = *(const int32_t *)(llrRes + srcParityIdx * NR_LDPC_ZMAX + lane * 4);
} else {
raw_llrs = 0;
}
}
// Extract 4 bits from 4 LLRs (Local Packing)
// Assuming MSB order within nibble: [3][2][1][0]
uint32_t my_4_bits = 0;
int8_t *p_val = (int8_t *)&raw_llrs;
#pragma unroll
for (int i = 0; i < 4; i++) {
if (p_val[i] < 0) {
my_4_bits |= (1 << (3 - i));
}
}
// Thread Cooperation: Pair threads to pack 8 bits (1 byte)
// Even thread (0,2..) takes low nibble, Odd thread (1,3..) takes high nibble.
uint32_t neighbor_bits = __shfl_xor_sync(0xffffffff, my_4_bits, 1);
if ((lane & 1) == 0) {
// Combine: [Odd Thread Bits (High)] | [My Bits (Low)]
uint8_t packed_byte = (neighbor_bits & 0xF) | ((my_4_bits & 0xF) << 4);
// Output addr: lane steps by 4 LLRs, but we output 1 byte per 8 LLRs -> lane >> 1
uint32_t outAddr = outColIdx * (Zc >> 3) + (lane >> 1);
out[outAddr] = packed_byte;
}
}

View File

@@ -0,0 +1,120 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file nrLDPC_CUDA_public.h
* \brief Shared functions in CUDA implementation of LDPC decoder
* \author Qizhi Pan, Raymond Knopp
* \company EURECOM
* \email: qizhi.pan@eurecom.fr, raymond.knopp@eurecom.fr
* \date 2025-12-30
* \version 1.0
* \note
* \warning
*/
#pragma once
#include <cuda_runtime.h>
#include <stdint.h>
#include <stdio.h>
#include <cuda_runtime.h>
#include <stdint.h>
__device__ __forceinline__ void moveBricks_invput_circ(int8_t *__restrict__ dstBuf,
uint32_t dstBuf_Offset,
const uint8_t *__restrict__ Four_Bricks,
uint32_t Z,
uint32_t cshift)
{
uint32_t tmp = cshift + dstBuf_Offset;
uint32_t pos = (tmp >= Z) ? tmp - Z : tmp;
if (pos <= Z - 4) {
uint32_t val = *(const uint32_t*)Four_Bricks;
memcpy(dstBuf + pos, &val, 4);
}
else {
uint32_t bytes_at_end = Z - pos; // 1, 2, or 3
#pragma unroll
for(int i=0; i<4; i++) {
if (i < bytes_at_end) {
dstBuf[pos + i] = Four_Bricks[i];
} else {
dstBuf[i - bytes_at_end] = Four_Bricks[i];
}
}
}
}
__device__ __forceinline__ void moveBricks_forput_circ(int8_t *__restrict__ dstBuf,
uint32_t dstBuf_Offset,
const uint8_t *__restrict__ Four_Bricks,
uint32_t Z,
uint32_t cshift)
{
uint32_t tmp = dstBuf_Offset + Z - cshift;
uint32_t pos = (tmp >= Z) ? tmp - Z : tmp;
if (pos <= Z - 4) {
uint32_t val = *(const uint32_t*)Four_Bricks;
memcpy(dstBuf + pos, &val, 4);
}
else {
uint32_t bytes_at_end = Z - pos;
#pragma unroll
for(int i=0; i<4; i++) {
if (i < bytes_at_end) {
dstBuf[pos + i] = Four_Bricks[i];
} else {
dstBuf[i - bytes_at_end] = Four_Bricks[i];
}
}
}
}
__device__ __forceinline__ void moveBricks_invget_circ(const int8_t *__restrict__ dstBuf,
uint32_t dstBuf_Offset,
uint8_t *__restrict__ Four_Bricks,
uint32_t Z,
uint32_t cshift)
{
uint32_t tmp = cshift + dstBuf_Offset;
uint32_t pos = (tmp >= Z) ? tmp - Z : tmp;
if (pos <= Z - 4) {
uint32_t val;
memcpy(&val, dstBuf + pos, 4);
*(uint32_t*)Four_Bricks = val;
}
else {
uint32_t bytes_at_end = Z - pos;
#pragma unroll
for(int i=0; i<4; i++) {
if (i < bytes_at_end) {
Four_Bricks[i] = dstBuf[pos + i];
} else {
Four_Bricks[i] = dstBuf[i - bytes_at_end];
}
}
}
}
__device__ __forceinline__ uint32_t __vxor4(const uint32_t a, uint32_t b)
{
return a ^ b;
}
__device__ __forceinline__ uint32_t __vsign4(const uint32_t a, uint32_t b)
{
uint32_t mask = __vcmplts4(b, 0);
uint32_t bneg = __vneg4(a);
return (mask & bneg) | (~mask & a);
//uint32_t is_zero_mask = __vcmpeq4(b, 0);
//return result & (~is_zero_mask);
}

View File

@@ -0,0 +1,84 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*! \file nrLDPC_CUDA_shared_param.h
* \brief Shared parameters in CUDA implementation of LDPC decoder
* \author Qizhi Pan, Raymond Knopp
* \company EURECOM
* \email: qizhi.pan@eurecom.fr, raymond.knopp@eurecom.fr
* \date 2025-12-30
* \version 1.0
* \note
* \warning
*/
#ifndef NRLDPC_CUDA_SHARED_PARAM_H_
#define NRLDPC_CUDA_SHARED_PARAM_H_
#include <cuda_runtime.h>
#ifndef MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER
#define MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER 36
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define RowLength 96 //Zc = 384/4 = 96
#define num_TotalBlocks_BG1_R13_Edge 316//based on number of Cn2Bn Msgs
#define num_TotalBlocks_BG1_R23_Edge 144
#define num_TotalBlocks_BG1_R89_Edge 79
#define num_TotalBlocks_llr_llrRes 22 //Only includes systematic bits
#define num_TotalBlocks_cn_BG1_R13_Node 46 //based on number of CNs
#define num_TotalBlocks_bn_BG1_R13_Node 68 //based on number of BNs
#define num_TotalBlocks_cn_BG1_R23_Node 13
#define num_TotalBlocks_bn_BG1_R23_Node 35
#define num_TotalBlocks_cn_BG1_R89_Node 5
#define num_TotalBlocks_bn_BG1_R89_Node 27
#define JETSON_ORIN
//#define GH200
#if defined(GH200)
#define NodeEdge_Switch_Cn_R13 32
#define NodeEdge_Switch_Bn_R13 10
#define NodeEdge_Switch_Cn_R23 32
#define NodeEdge_Switch_Bn_R23 12
#define NodeEdge_Switch_Cn_R89 48
#define NodeEdge_Switch_Bn_R89 24
#elif defined(JETSON_ORIN)
#define NodeEdge_Switch_Cn_R13 3
#define NodeEdge_Switch_Bn_R13 1
#define NodeEdge_Switch_Cn_R23 3
#define NodeEdge_Switch_Bn_R23 1
#define NodeEdge_Switch_Cn_R89 4
#define NodeEdge_Switch_Bn_R89 2
#endif
#ifdef __cplusplus
}
#endif
typedef struct KernelLaunchConfig {
dim3 grid;
dim3 block;
}KernelLaunchConfig;
typedef struct {
int idxBn;
int idxCn;
int preBuf;
int circShift;
int8_t dd;
} DumpEntry;
typedef struct {
int8_t* p_llr_ptr;
int8_t* p_out_ptr;
} ldpc_cuda_bridge_t;
#endif /* NRLDPC_CUDA_SHARED_PARAM_H_ */

View File

@@ -0,0 +1,164 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include <stdint.h>
#include <dlfcn.h>
#include "log.h"
#include "openair1/PHY/CODING/nrLDPC_defs.h"
#include "openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_types.h"
#include "openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
/* segment interface */
extern int32_t LDPCinit_cuda();
int32_t LDPCinit()
{
return LDPCinit_cuda();
}
// LDPCshutdown_cuda
extern int32_t LDPCshutdown_cuda();
int32_t LDPCshutdown()
{
return LDPCshutdown_cuda();
}
// LDPCdecoder
extern int32_t LDPCdecoder_cuda(t_nrLDPC_dec_params* p_decParams,
int8_t* p_llr,
uint8_t* p_out,
t_nrLDPC_time_stats* p_profiler,
decode_abort_t* ab);
// LDPCencoder <= (LDPCencoder32)
extern uint32_t **LDPCencoder32(uint8_t **input, encoder_implemparams_t *impp);
/* slot interface */
// fallback for LDPC sizes not implemented by CUDA LDPC
nrLDPC_coding_interface_t ldpc_cpu;
void *ldpc_lib_handle; // corresponding dlsym() handle, see also below
extern int nrLDPC_coding_encoder32(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters, nrLDPC_TB_encoding_parameters_t *nrLDPC_TB_encoding_parameters);
int nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *slot_params)
{
// this should be the same as previous nrLDPC_coding_encoder() in nrLDPC_coding_segment_encoder.c
nrLDPC_TB_encoding_parameters_t tbCPU[32] = {0};
nrLDPC_slot_encoding_parameters_t cpu = {
.frame = slot_params->frame,
.slot = slot_params->slot,
.threadPool = slot_params->threadPool,
// timing?
.TBs = tbCPU,
};
for (int dlsch_id = 0; dlsch_id < slot_params->nb_TBs; dlsch_id++) {
nrLDPC_TB_encoding_parameters_t *tbp = &slot_params->TBs[dlsch_id];
if (tbp->BG == 1 && tbp->C > 8 && tbp->Z == 384) {
nrLDPC_coding_encoder32(slot_params, tbp);
} else {
// this is not handled by CUDA, handle with CPU
cpu.TBs[cpu.nb_TBs++] = *tbp;
}
}
if (cpu.nb_TBs > 0) {
ldpc_cpu.nrLDPC_coding_encoder(&cpu);
slot_params->TBs[0] = cpu.TBs[0];
}
return 0;
}
void nr_process_decode_segment_cuda(nrLDPC_TB_decoding_parameters_t *);
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *slot_params)
{
// this should be the same as previous nrLDPC_coding_decoder() in nrLDPC_coding_segment_decoder.c
nrLDPC_TB_decoding_parameters_t tbCPU[32] = {0};
nrLDPC_slot_decoding_parameters_t cpu = {
.frame = slot_params->frame,
.slot = slot_params->slot,
.threadPool = slot_params->threadPool,
.TBs = tbCPU,
};
for (int pusch_id = 0; pusch_id < slot_params->nb_TBs; pusch_id++) {
nrLDPC_TB_decoding_parameters_t *tbp = &slot_params->TBs[pusch_id];
if (tbp->Z >= 128 && tbp->BG == 1) {
nr_process_decode_segment_cuda(tbp);
} else {
// this is not handled by CUDA, handle with CPU
cpu.TBs[cpu.nb_TBs++] = *tbp;
}
}
if (cpu.nb_TBs > 0) {
ldpc_cpu.nrLDPC_coding_decoder(&cpu);
slot_params->TBs[0] = cpu.TBs[0];
}
return 0;
}
#define CHECK_DLFCN_ERROR(COND, SYMB) \
if (!(COND)) { \
LOG_E(NR_PHY, "could not open %s: %s\n", SYMB, dlerror()); \
return false; \
}
static bool open_default_ldpc(nrLDPC_coding_interface_t *ldpc, int max_num_pxsch)
{
const char *default_ldpc = "libldpc.so";
if (ldpc_lib_handle) {
LOG_W(NR_PHY, "%s already initialized\n", default_ldpc);
return true;
}
ldpc_lib_handle = dlopen(default_ldpc, RTLD_LAZY|RTLD_NODELETE|RTLD_GLOBAL);
CHECK_DLFCN_ERROR(ldpc_lib_handle, default_ldpc);
ldpc->nrLDPC_coding_init = dlsym(ldpc_lib_handle, "nrLDPC_coding_init");
CHECK_DLFCN_ERROR(ldpc->nrLDPC_coding_init, "nrLDPC_coding_init");
ldpc->nrLDPC_coding_shutdown = dlsym(ldpc_lib_handle, "nrLDPC_coding_shutdown");
CHECK_DLFCN_ERROR(ldpc->nrLDPC_coding_shutdown, "nrLDPC_coding_shutdown");
ldpc->nrLDPC_coding_decoder = dlsym(ldpc_lib_handle, "nrLDPC_coding_decoder");
CHECK_DLFCN_ERROR(ldpc->nrLDPC_coding_decoder, "nrLDPC_coding_decoder");
ldpc->nrLDPC_coding_encoder = dlsym(ldpc_lib_handle, "nrLDPC_coding_encoder");
CHECK_DLFCN_ERROR(ldpc->nrLDPC_coding_encoder, "nrLDPC_coding_encoder");
bool success = ldpc->nrLDPC_coding_init(max_num_pxsch) == 0;
if (!success)
LOG_E(NR_PHY, "could not initialize %s\n", default_ldpc);
return success;
}
static void close_default_ldpc(nrLDPC_coding_interface_t *ldpc)
{
if (!ldpc_lib_handle) {
LOG_W(NR_PHY, "cannot stop LDPC, not loaded\n");
return;
}
ldpc->nrLDPC_coding_shutdown();
dlclose(ldpc_lib_handle);
ldpc_lib_handle = NULL;
}
extern int32_t nrLDPC_coding_init_cuda(int max_num_pxsch);
int32_t nrLDPC_coding_init(int max_num_pxsch)
{
// we can't use load_nrLDPC_coding_interface() because:
// 1. it caches the lib under the "ldpc" name, but links to "ldpc_cuda", so
// we recursively load this function again => stack overflow
// 2. the global --loader.ldpc.shlibversion _cuda would be reapplied => we
// cannot actually load ourselves
// hence use dlsym() directly.
bool success = open_default_ldpc(&ldpc_cpu, max_num_pxsch);
AssertFatal(success, "error loading LDPC library\n");
return nrLDPC_coding_init_cuda(max_num_pxsch);
}
extern int32_t nrLDPC_coding_shutdown_cuda(void);
int32_t nrLDPC_coding_shutdown(void)
{
close_default_ldpc(&ldpc_cpu);
return nrLDPC_coding_shutdown_cuda();
}

Some files were not shown because too many files have changed in this diff Show More