Compare commits

...

28 Commits

Author SHA1 Message Date
Melissa
dae942c616 bfp fix. had to add extra bytes to compression header 2026-05-15 22:54:55 +00:00
Melissa
057d167eea close to BFP working. no comp with compression code works 2026-05-15 21:44:12 +00:00
Melissa
cd509aad8a oru-rebase-1 initial recopy working on single machine 2026-05-14 00:20:58 +00:00
Bartosz Podrygajlo
1720267638 Add a CI testcase for 1x1 O-RU 2026-03-17 19:00:09 +01:00
Bartosz Podrygajlo
9df46ef247 Use dpdk-20 compatible rte_ring configuration
It seems dpdk-20 does not support RING_F_MP_RTS_ENQ | RING_F_MC_RTS_DEQ
rte_ring flags. The default value (0) creates a multi-producer/multi-consumer
ring that is compatible with dpdk-20.
2026-03-17 15:29:11 +01:00
Bartosz Podrygajlo
0c8fd99cc0 Handle SIGTERM in O-RU for docker compatibility 2026-03-17 15:29:00 +01:00
Bartosz Podrygajlo
0ae66ec46b Fix and optimize O-RU docker image
Remove some unnecessary libraries and and libvrtsim.so.
2026-03-17 15:28:55 +01:00
Bartosz Podrygajlo
e969caf9af Add O-RU patch to fhi-72 build image
Also rearange the steps for optimal cache use.
2026-03-17 15:28:45 +01:00
Bartosz Podrygajlo
3c95ba0239 Add some folders to .dockerignore
- .git - changes with modifications, stashes, branches, fetches
 - .cache - a local folder for tool cache like clangd
2026-03-17 15:28:40 +01:00
Bartosz Podrygajlo
b6b93e9991 Configuration for single-server setup
Added configuation template from my single-server setup and one for peafowl
2026-03-17 15:28:28 +01:00
Bartosz Podrygajlo
b2b823c40c RU PUSCH reception 2026-03-17 15:28:17 +01:00
Bartosz Podrygajlo
59f4ec8196 O-RU: clean exit 2026-03-17 15:27:59 +01:00
Bartosz Podrygajlo
aa574dd721 O-RU: PRACH RX and PRACH UP packet generation 2026-03-17 15:27:42 +01:00
Bartosz Podrygajlo
ce42bcd30a O-RU: DL FH processing
Implemented downlink fronthaul processing: symbol rotation and DFT.

Introduced new config parameter: tp_cores in ORUs section to specify which
cores will be used for threadpool processing in O-RU.
2026-03-17 15:27:31 +01:00
Bartosz Podrygajlo
477df2c2bc Add per symbol PRACH reception 2026-03-17 15:25:43 +01:00
Bartosz Podrygajlo
aa0468dff9 Add a new tx_rf_symbols function to allow per symbol transmission 2026-03-17 15:25:30 +01:00
Bartosz Podrygajlo
1643bd61f6 O-RU: Add sync thread
Add a small thread which performs initial sync related tasks.
2026-03-17 15:25:16 +01:00
Bartosz Podrygajlo
2e96acf0d0 Load vrtsim in O-RU and start reading samples
Added oru_south_read_thread that reads samples from vrtsim.
No UL FH processing is done as of this commit.
2026-03-17 15:25:11 +01:00
Bartosz Podrygajlo
ca02b456f5 Add a function which loads the specified radio library 2026-03-17 15:25:05 +01:00
Bartosz Podrygajlo
cb1785519e Add get_timestamp to openair_device_t and vrtsim
This function can be used to convert a timespec struct to a
openair0_timestamp. It can be used to synchronize the realtime
clock to the device sample number.
2026-03-17 15:24:54 +01:00
Bartosz Podrygajlo
fd32d3afdc O-RU: Prepare ethernet packets for application processing.
This commit adds a set of rte_rings for UPlane packets processing
 - uplane_data_ring - this ring contains buffer pointers to be used for UPlane
   packet metadata
 - uplane_symbol_rings - is a set of rings where each ring hold UPlane packet
   metadata for a single symbol

On reception, metadata buffers are dequeued from the data ring and enqueued on
symbol specific rings. When the O-RU application processes the UPlane packet
it frees the rte_mbuf associated with the packet and returns the metadata buffer
back to the data ring.
2026-03-17 15:24:40 +01:00
Bartosz Podrygajlo
20c91b235d O-RU: Offset symbol_callback by T2a_min
Add offset to when symbol_callback is triggered and adjust sense_of_time
struct output to align with the extra offset.
2026-03-17 15:23:23 +01:00
Bartosz Podrygajlo
7951aab40d Add Ta3, T2a delay profile params for O-RU 2026-03-17 15:22:38 +01:00
Bartosz Podrygajlo
6b2b62ed85 O-RU: Add empty packet processing functions
Add empty processing function for O-RU. This utilizes hook functionality
in the xran K release hook modification.
2026-03-17 15:21:50 +01:00
Bartosz Podrygajlo
012f968dc9 O-RU: Add north_read thread
Add a thread that is supposed to read IQ data in DL and perform DL TX processing
2026-03-17 15:20:49 +01:00
Bartosz Podrygajlo
4d70d4f254 O-RU: Setup frame params according to new configuration
Added initialization of frame params according to configuration added
in the new ORUs section. Only one ORU is supported right now.

Added example config that allows starting the ORU and verifying manually
that the config is read.
2026-03-17 15:20:29 +01:00
Bartosz Podrygajlo
d84d4cc209 Add north_in function to oran_fhlib_5g
The north_in function is supposed to be used by the ORU to read IQ data
sent from O-DU.
2026-03-17 15:19:40 +01:00
Bartosz Podrygajlo
ae1e9bb892 O-RU F patch
This adds a patch for xran that allows using the hook functionality
required by the O-RU
2026-03-17 15:18:55 +01:00
40 changed files with 4559 additions and 146 deletions

View File

@@ -5,3 +5,5 @@ common/utils/T/T_IDs.h
common/utils/T/T_messages.txt.h
common/utils/T/genids
common/utils/T/genids.o
.cache
.git

View File

@@ -1827,13 +1827,14 @@ add_executable(nr-oru
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
${OPENAIR_DIR}/executables/main_nr_ru.c
${OPENAIR_DIR}/executables/nr-oru.c
)
target_link_libraries(nr-oru PRIVATE
UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl
radio_common softmodem_common)
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
barrier actor)
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management PHY_NR MAC_NR_COMMON)
# nr-softmodem
###################################################

View File

@@ -0,0 +1,142 @@
<!--
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
-->
<testCaseList>
<htmlTabRef>oai-nr-oru</htmlTabRef>
<htmlTabName>1x1 </htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>localhost</node>
<images>oai-gnb oai-nr-ue oai-nr-oru</images>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>localhost</node>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Setup sriov and network interfaces</desc>
<node>cacofonix</node>
<script>yaml_files/sa_fhi_7.2_oaioru_1x1_gnb/setup.sh</script>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_oaioru_1x1_gnb</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI O-RU</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_oaioru_1x1_gnb</yaml_path>
<services>oai-nr-oru</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G gNB 7.2</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_oaioru_1x1_gnb</yaml_path>
<services>oai-gnb</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G NR-UE</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_oaioru_1x1_gnb</yaml_path>
<services>oai-nr-ue</services>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<node>localhost</node>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Detach_UE</class>
<desc>Detach OAI UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_oaioru_1x1_gnb/</yaml_path>
<analysis>
<services>oai-gnb=EndsWithBye oai-nr-ue oai-nr-oru</services>
</analysis>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Setup sriov and network interfaces VVDN</desc>
<node>cacofonix</node>
<script>yaml_files/sa_fhi_7.2_oaioru_1x1_gnb/cleanup.sh</script>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>localhost</node>
<images>oai-gnb oai-nr-ue oai-nr-oru</images>
</testCase>
</testCaseList>

View File

@@ -0,0 +1,5 @@
set -e
sudo cpupower idle-set -E > /dev/null
sudo sysctl kernel.sched_rt_runtime_us=950000
sudo sysctl kernel.timer_migration=1
exit 0

View File

@@ -0,0 +1,108 @@
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
init: true
volumes:
- ./../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ./../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.101.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:v2.1.10
environment:
- TZ=Europe/paris
volumes:
- ./../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.101.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.1.10
environment:
- TZ=Europe/Paris
volumes:
- ./../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.101.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.1.10
init: true
environment:
- TZ=Europe/Paris
volumes:
- ./../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.101.134
interface_name: eth0
traffic_net:
ipv4_address: 192.168.102.134
interface_name: eth1
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
init: true
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.102.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.102.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.102.134"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.101.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.102.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"

View File

@@ -0,0 +1,113 @@
include:
- cn.yml
services:
mysql:
cpuset: "5,6,30,31"
oai-amf:
cpuset: "5,6,30,31"
oai-smf:
cpuset: "5,6,30,31"
oai-upf:
cpuset: "5,6,30,31"
oai-ext-dn:
cpuset: "5,6,30,31"
oai-gnb:
image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop}
cap_add:
- IPC_LOCK
- SYS_NICE
cap_drop:
- ALL
container_name: oai-gnb
environment:
TZ: Europe/Paris
USE_ADDITIONAL_OPTIONS: --gNBs.[0].min_rxtxtime 6
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ./gnb.conf:/opt/oai-gnb/etc/gnb.conf
- /dev/hugepages:/dev/hugepages
cpuset: "7,8,9,10,11,12,13,14"
networks:
oai-net:
ipv4_address: 172.21.18.20
public_net:
ipv4_address: 192.168.101.140
healthcheck:
test: /bin/bash -c "ps aux | grep -v grep | grep -c nr-softmodem"
start_period: 10s
interval: 5s
timeout: 5s
retries: 10
oai-nr-oru:
image: ${REGISTRY-oaisoftwarealliance/}oai-nr-oru:${TAG:-develop}
cap_add:
- IPC_LOCK
- SYS_NICE
cap_drop:
- ALL
container_name: oai-nr-oru
environment:
TZ: Europe/Paris
USE_ADDITIONAL_OPTIONS: --vrtsim.role server
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ./nr-oru.conf:/opt/nr-oru/etc/nr-oru.conf
- /dev/hugepages:/dev/hugepages
- tmp_data:/tmp/
cpuset: "15,16,17,18,19,20,21,22"
networks:
oai-net:
ipv4_address: 172.21.18.21
healthcheck:
test: /bin/bash -c "ps aux | grep -v grep | grep -c nr-oru"
start_period: 15s
start_interval: 500ms
interval: 5s
timeout: 5s
retries: 10
ipc: host
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: -r 106 --numerology 1 --band 77 -C 4049760000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim
depends_on:
- oai-nr-oru
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./nr-ue.conf:/opt/oai-nr-ue/etc/nr-ue.conf
- tmp_data:/tmp/
cpuset: "23,24,25,26,27,28,29"
networks:
public_net:
ipv4_address: 192.168.101.150
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
start_interval: 500ms
ipc: host
networks:
oai-net:
driver: macvlan
name: oai-net
ipam:
config:
- subnet: "172.21.16.0/22"
ip_range: "172.21.18.20/32"
gateway: "172.21.19.254"
driver_opts:
com.docker.network.bridge.name: "oai-net"
parent: enp5s0f0np0
volumes:
tmp_data:

View File

@@ -0,0 +1,270 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1; }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 1;
pdsch_AntennaPorts_N1 = 1;
maxMIMO_layers = 1;
pusch_AntennaPorts = 1;
do_CSIRS = 0;
do_SRS = 0;
sib1_tda = 15;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# n_TimingAdvanceOffset = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 4049.76 MHz
# selected SSB frequency = 4049.76 MHz
absoluteFrequencySSB = 669984;
dl_frequencyBand = 77;
# frequency point A = 4030.68 MHz
dl_absoluteFrequencyPointA = 668712;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 28875; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 77;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 157;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 7;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 2;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -100;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
ssb_PositionsInBurst_Bitmap = 0x1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 5;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.101.132"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.101.140";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.101.140";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
# dl_bler_target_upper = .35;
# dl_bler_target_lower = .15;
ul_bler_target_upper = .35;
ul_bler_target_lower = .15;
pusch_FailureThres = 100;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 110;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = -100;
# thread_pool_size = 8;
tx_amp_backoff_dB = 36;
L1_rx_thread_core = -1;
L1_tx_thread_core = -1;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 1;
nb_rx = 1;
att_tx = 0;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:05:02.2", "0000:05:02.3"); # one VF can be used as well
dpdk_iova_mode = "VA";
system_core = 7;
io_core = 8;
worker_cores = (9);
ru_addr = ("00:11:22:33:64:66", "00:11:22:33:64:67");
mtu = 9600;
fh_config = ({
T1a_cp_dl = (285, 470);
T1a_cp_ul = (285, 429);
T1a_up = (300, 450);
Ta4 = (400, 440);
ru_config = {
iq_width = 16;
iq_width_prach = 16;
};
});
};

View File

@@ -0,0 +1,70 @@
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
ORUs = (
{
tx_bw = [106];
rx_bw = [106];
carrier_tx = [4049760];
carrier_rx = [4049760];
prach_config_index = 157;
prach_msg1_start = 0;
tdd_period = 5;
num_dl_slots = 3;
num_dl_symbols = 6;
num_ul_slots = 1;
num_ul_symbols = 4;
numerology = 1;
tp_cores = [-1, -1, -1, -1, -1, -1]
});
RUs = (
{
local_rf = "no";
nb_tx = 1;
nb_rx = 1;
att_tx = 0;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
});
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:05:02.0", "0000:05:02.1"); # one VF can be used as well
dpdk_iova_mode = "VA";
system_core = 17;
io_core = 18;
worker_cores = (19);
du_addr = ("00:11:22:33:64:68", "00:11:22:33:64:69");
mtu = 9600;
app_id = "RU";
file_prefix = "ru";
fh_config = ({
Ta3_up = (200, 470);
T2a_up = (350, 1200);
ru_config = {
iq_width = 16;
iq_width_prach = 16;
};
});
};

View File

@@ -0,0 +1,37 @@
set -e
# Set these in the DU config
RU_U_PLANE_MAC=00:11:22:33:64:66
RU_C_PLANE_MAC=00:11:22:33:64:67
DU_U_PLANE_MAC=00:11:22:33:64:68
DU_C_PLANE_MAC=00:11:22:33:64:69
U_VLAN=3
C_VLAN=4
sudo cpupower idle-set -D 0 > /dev/null
sudo sysctl kernel.sched_rt_runtime_us=-1
sudo sysctl kernel.timer_migration=0
MTU=9216
IF=enp5s0f0np0
sudo ethtool -G $IF rx 8160 tx 8160
echo 0 | sudo tee /sys/bus/pci/devices/0000\:05\:00.0/sriov_numvfs
echo 4 | sudo tee /sys/bus/pci/devices/0000\:05\:00.0/sriov_numvfs
# this next 2 lines is for C/U planes
sudo ip link set $IF vf 0 mac $RU_U_PLANE_MAC vlan $U_VLAN qos 0 spoofchk off mtu $MTU
sudo ip link set $IF vf 1 mac $RU_C_PLANE_MAC vlan $C_VLAN qos 0 spoofchk off mtu $MTU
# this next 2 lines is for DU C/U planes
sudo ip link set $IF vf 2 mac $DU_U_PLANE_MAC vlan $U_VLAN qos 0 spoofchk off mtu $MTU
sudo ip link set $IF vf 3 mac $DU_C_PLANE_MAC vlan $C_VLAN qos 0 spoofchk off mtu $MTU
sleep 1
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.0
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.1
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.2
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.3
sudo modprobe vfio-pci
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.0
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.1
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.2
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.3
exit 0

View File

@@ -0,0 +1,308 @@
diff --git a/fhi_lib/lib/api/xran_fh_o_ru.h b/fhi_lib/lib/api/xran_fh_o_ru.h
index 7812399..8bdb539 100644
--- a/fhi_lib/lib/api/xran_fh_o_ru.h
+++ b/fhi_lib/lib/api/xran_fh_o_ru.h
@@ -28,7 +28,7 @@
* @author Intel Corporation
*
**/
-
+#pragma once
#ifdef __cplusplus
extern "C" {
#endif
@@ -48,6 +48,27 @@ extern "C" {
#include <getopt.h>
#include <unistd.h>
#include "xran_fh_o_du.h"
+#include "xran_transport.h"
+
+typedef int (*process_uplane_fn)(struct rte_mbuf *pkt, void *handle,
+ struct xran_eaxc_info *p_cid,
+ uint16_t xport_id,
+ struct xran_sense_of_time *sense_of_time);
+typedef int32_t (*process_cplane_fn)(struct rte_mbuf *pkt, void *handle, uint16_t xport_id,
+ struct xran_sense_of_time *sense_of_time);
+typedef void (*sym_ota_fn)(void * handle, struct xran_sense_of_time *sense_of_time);
+
+void xran_hook_install(void *pHandle, process_uplane_fn process_uplane_fn_p,
+ void *process_uplane_fn_args,
+ process_cplane_fn process_cplane_fn_p,
+ void *process_cplane_fn_args,
+ sym_ota_fn sym_ota_fn_p,
+ void *sym_ota_fn_args,
+ int mu);
+void xran_hook_schedule_packet(void *pHandle, struct rte_mbuf *mbuf, int port,
+ enum xran_pkt_dir direction, int ru_port_id,
+ int slot, int symbol);
+int xran_hook_send_packet(void *pHandle, struct rte_mbuf *mbuf, int port, enum xran_pkt_dir direction, int ru_port_id);
/**
* @ingroup
@@ -78,4 +99,4 @@ int32_t xran_5g_bfw_config(void * pHandle, struct xran_buffer_list *pSrcRxCpBuff
#ifdef __cplusplus
}
-#endif
\ No newline at end of file
+#endif
diff --git a/fhi_lib/lib/api/xran_pkt.h b/fhi_lib/lib/api/xran_pkt.h
index 314b8d6..478f092 100644
--- a/fhi_lib/lib/api/xran_pkt.h
+++ b/fhi_lib/lib/api/xran_pkt.h
@@ -339,6 +339,11 @@ struct xran_pkt_comm_hdr
struct xran_ecpri_hdr ecpri_hdr; /**< eCPRI Transport Header */
} __rte_packed;
+enum xran_mbuf_mem_op_id {
+ MBUF_KEEP,
+ MBUF_FREE
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/fhi_lib/lib/ethernet/ethdi.h b/fhi_lib/lib/ethernet/ethdi.h
index 5a5bf80..e32620c 100644
--- a/fhi_lib/lib/ethernet/ethdi.h
+++ b/fhi_lib/lib/ethernet/ethdi.h
@@ -117,11 +117,6 @@ struct xran_ethdi_ctx {
uint64_t rx_vf_queue_cnt[XRAN_VF_MAX][XRAN_VF_QUEUE_MAX];
};
-enum xran_mbuf_mem_op_id {
- MBUF_KEEP,
- MBUF_FREE
-};
-
extern enum xran_if_state xran_if_current_state;
static inline struct xran_ethdi_ctx *xran_ethdi_get_ctx(void)
diff --git a/fhi_lib/lib/src/xran_dev.c b/fhi_lib/lib/src/xran_dev.c
index c939edc..9037091 100644
--- a/fhi_lib/lib/src/xran_dev.c
+++ b/fhi_lib/lib/src/xran_dev.c
@@ -502,3 +502,118 @@ xran_init_vfs_mapping(void *pHandle)
return (XRAN_STATUS_SUCCESS);
}
+
+#define MAX_PACKETS_SENT_PER_SYMBOL 16 // has to be power of 2
+
+// Process the scheduled packets, done once per symbol at OTA time
+void xran_hook_process_tx_packets(void* pHandle, int port, int slot, int symbol) {
+ struct xran_device_ctx *p_dev_ctx = (struct xran_device_ctx *)pHandle;
+ int ring_index = slot % XRAN_N_FE_BUF_LEN;
+ struct rte_ring *ring = p_dev_ctx->hook_cfg.tx_rings[port][ring_index][symbol];
+ void* mbufs[MAX_PACKETS_SENT_PER_SYMBOL];
+ int dequeued = rte_ring_dequeue_burst(ring, mbufs, MAX_PACKETS_SENT_PER_SYMBOL, NULL);
+ struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
+ struct rte_ring *tx_ring = ctx->tx_ring[port];
+ int enqueued = rte_ring_enqueue_burst(tx_ring, mbufs, dequeued, NULL);
+ if (enqueued != dequeued) {
+ rte_panic("Expected to be able to enqueue all the packets on the TX ring\n");
+ }
+}
+
+typedef struct {
+ void* pHandle;
+ int port;
+ sym_ota_fn sym_ota_fn;
+ void *sym_ota_fn_args;
+} callback_args_t;
+
+int32_t send_packets(void* arg, struct xran_sense_of_time* p_sense_of_time) {
+ callback_args_t *cb_args = arg;
+ int mu = xran_get_conf_numerology(cb_args->pHandle);
+ int slot_in_frame = p_sense_of_time->nSlotIdx + p_sense_of_time->nSubframeIdx * (1 << mu);
+ xran_hook_process_tx_packets(cb_args->pHandle, cb_args->port, slot_in_frame, p_sense_of_time->nSymIdx);
+ if (cb_args->sym_ota_fn) {
+ cb_args->sym_ota_fn(cb_args->sym_ota_fn_args, p_sense_of_time);
+ }
+}
+
+void xran_hook_install(
+ void *pHandle,
+ process_uplane_fn process_uplane_fn_p,
+ void *process_uplane_fn_args,
+ process_cplane_fn process_cplane_fn_p,
+ void *process_cplane_fn_args,
+ sym_ota_fn sym_ota_fn_p,
+ void *sym_ota_fn_args,
+ int mu)
+{
+ static bool installed = false;
+ if (installed) rte_panic("Can only install once for one numerology\n");
+ installed = true;
+
+ // Workaround - indicate XRAN memory is ready so packet processor starts
+ struct xran_device_ctx *p_dev_ctx = (struct xran_device_ctx *)pHandle;
+ p_dev_ctx->xran2phy_mem_ready = 1;
+
+ if (mu != xran_get_conf_numerology(pHandle)) {
+ rte_panic("Can only use numerlogy %d as configured during init\n", xran_get_conf_numerology(pHandle));
+ }
+
+ p_dev_ctx->hook_cfg.process_uplane_fn = process_uplane_fn_p;
+ p_dev_ctx->hook_cfg.process_uplane_fn_args = process_uplane_fn_args;
+ p_dev_ctx->hook_cfg.process_cplane_fn = process_cplane_fn_p;
+ p_dev_ctx->hook_cfg.process_cplane_fn_args = process_cplane_fn_args;
+
+ struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
+ for (int port = 0; port < ctx->io_cfg.num_vfs; port++) {
+ int ring_index = 0;
+ for (int slot = 0; slot < XRAN_N_FE_BUF_LEN; slot++) {
+ for (int symbol = 0; symbol < XRAN_SYMBOLNUMBER_MAX; symbol++) {
+ char ring_name[RTE_RING_NAMESIZE];
+ snprintf(ring_name, RTE_DIM(ring_name), "%s_%d_%d", "hook_tx_ring", port, ring_index++);
+ struct rte_ring *ring = rte_ring_create(ring_name, MAX_PACKETS_SENT_PER_SYMBOL, SOCKET_ID_ANY, RING_F_SC_DEQ);
+ if (ring == NULL) rte_panic("Cannot allocate rte_ring\n");
+ p_dev_ctx->hook_cfg.tx_rings[port][slot][symbol] = ring;
+ }
+ }
+ }
+
+ // Setup a callback each OTA symbol to send packets from per-symbol rings into the TX ring
+ static struct xran_sense_of_time xran_sense_of_time[XRAN_SYMBOLNUMBER_MAX];
+ static callback_args_t cb_args = {0};
+ cb_args.port = 0;
+ cb_args.pHandle = pHandle;
+ cb_args.sym_ota_fn = sym_ota_fn_p;
+ cb_args.sym_ota_fn_args = sym_ota_fn_args;
+ for (int i = 0; i < XRAN_SYMBOLNUMBER_MAX; i++) {
+ xran_reg_sym_cb(pHandle, send_packets, &cb_args, &xran_sense_of_time[i], i, XRAN_CB_SYM_OTA_TIME);
+ }
+}
+
+// Schedule packet for slot and symbol
+void xran_hook_schedule_packet(void *pHandle, struct rte_mbuf *mbuf, int port,
+ enum xran_pkt_dir direction, int ru_port_id,
+ int slot, int symbol) {
+ void *ret = rte_pktmbuf_prepend(mbuf, sizeof(struct rte_ether_hdr));
+ if (ret == NULL) rte_panic("not enough headroom for ethernet header");
+ struct xran_device_ctx *p_dev_ctx = (struct xran_device_ctx *)pHandle;
+ int vf_id = xran_map_ecpriPcid_to_vf(pHandle, direction, 0, ru_port_id);
+ struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx();
+
+ mbuf->port = ctx->io_cfg.port[vf_id];
+ xran_add_eth_hdr_vlan(&ctx->entities[vf_id][ID_O_RU], ETHER_TYPE_ECPRI, mbuf);
+
+ struct rte_ring *ring = p_dev_ctx->hook_cfg.tx_rings[port][slot][symbol];
+ int res = rte_ring_enqueue(ring, mbuf);
+ if (res != 0) rte_panic("Cannot enqueue to ring");
+}
+
+int xran_hook_send_packet(void *pHandle, struct rte_mbuf *mbuf, int port, enum xran_pkt_dir direction, int ru_port_id) {
+ void *ret = rte_pktmbuf_prepend(mbuf, sizeof(struct rte_ether_hdr));
+ if (ret == NULL) rte_panic("not enough headroom for ethernet header");
+ int vf_id = xran_map_ecpriPcid_to_vf(pHandle, direction, 0, ru_port_id);
+ if (xran_ethdi_mbuf_send(mbuf, ETHER_TYPE_ECPRI, vf_id) == 1) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/fhi_lib/lib/src/xran_dev.h b/fhi_lib/lib/src/xran_dev.h
index 778b38d..9a19ab5 100644
--- a/fhi_lib/lib/src/xran_dev.h
+++ b/fhi_lib/lib/src/xran_dev.h
@@ -40,6 +40,7 @@ extern "C" {
#include <rte_timer.h>
#include "xran_fh_o_du.h"
+#include "xran_fh_o_ru.h"
#include "xran_prach_cfg.h"
#include "xran_up_api.h"
#include "xran_cp_api.h"
@@ -186,6 +187,17 @@ struct xran_prb_elm_proc_info_t {
uint8_t numSymsRemaining; /**< Number of symbols for DL CP transmission remaining in this slot */
};
+typedef struct {
+ // Used to replace XRAN packet processing functions
+ process_uplane_fn process_uplane_fn;
+ void *process_uplane_fn_args;
+ process_cplane_fn process_cplane_fn;
+ void *process_cplane_fn_args;
+
+ // Used to schedule TX in the future.
+ struct rte_ring *tx_rings[XRAN_VF_MAX][XRAN_N_FE_BUF_LEN][XRAN_SYMBOLNUMBER_MAX];
+} hook_cfg_t;
+
struct __rte_cache_aligned xran_device_ctx
{
uint8_t sector_id;
@@ -326,6 +338,7 @@ struct __rte_cache_aligned xran_device_ctx
uint8_t technology[XRAN_MAX_DSS_PERIODICITY]; /**< technology array represents slot is LTE(0)/NR(1) */
/* Keeps track of how many sections are processed while parsing C-plan packet */
uint8_t sectiondb_elm[XRAN_MAX_SECTIONDB_CTX][XRAN_DIR_MAX][XRAN_COMPONENT_CARRIERS_MAX][XRAN_MAX_ANTENNA_NR * 2 + XRAN_MAX_ANT_ARRAY_ELM_NR];
+ hook_cfg_t hook_cfg;
};
struct xran_eaxcid_config *xran_get_conf_eAxC(void *pHandle);
diff --git a/fhi_lib/lib/src/xran_main.c b/fhi_lib/lib/src/xran_main.c
index db03d94..1dfb5f7 100644
--- a/fhi_lib/lib/src/xran_main.c
+++ b/fhi_lib/lib/src/xran_main.c
@@ -1680,9 +1680,31 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru
}
else
{
+ struct xran_sense_of_time sense_of_time;
+ if (p_dev_ctx->hook_cfg.process_uplane_fn || p_dev_ctx->hook_cfg.process_cplane_fn) {
+ int mu = p_dev_ctx->fh_cfg.frame_conf.nNumerology;
+ int32_t slots = xran_lib_ota_sym_idx[p_dev_ctx->xran_port_id] / XRAN_NUM_OF_SYMBOL_PER_SLOT;
+ int num_slots_per_frame = 10 << mu;
+ int num_slots_per_subframe = 1 << mu;
+ int frame = slots / num_slots_per_frame;
+ int subframe = (slots % num_slots_per_frame) / num_slots_per_subframe;
+ int slot = slots - frame * num_slots_per_frame - subframe * num_slots_per_subframe;
+ int symbol = xran_lib_ota_sym_idx[p_dev_ctx->xran_port_id] % XRAN_NUM_OF_SYMBOL_PER_SLOT;
+ sense_of_time.tti_counter = xran_lib_ota_sym_idx[p_dev_ctx->xran_port_id];
+ sense_of_time.nFrameIdx = xran_getSfnSecStart() + frame;
+ sense_of_time.nSymIdx = symbol;
+ sense_of_time.nSubframeIdx = subframe;
+ sense_of_time.nSlotIdx = slot;
+ sense_of_time.type_of_event = XRAN_CB_SYM_OTA_TIME;
+ sense_of_time.nSecond = timing_get_current_second();
+ }
for (i = 0; i < num_data; i++)
{
- ret = process_mbuf(pkt_data[i], (void*)p_dev_ctx, p_cid);
+ if (p_dev_ctx->hook_cfg.process_uplane_fn) {
+ ret = p_dev_ctx->hook_cfg.process_uplane_fn(pkt_data[i], p_dev_ctx->hook_cfg.process_uplane_fn_args, p_cid, xport_id, &sense_of_time);
+ } else {
+ ret = process_mbuf(pkt_data[i], (void*)p_dev_ctx, p_cid);
+ }
if (ret == MBUF_FREE)
rte_pktmbuf_free(pkt_data[i]);
}
@@ -1692,7 +1714,11 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru
t1 = MLogXRANTick();
if (p_dev_ctx->fh_init.io_cfg.id == O_RU)
{
- ret = process_cplane(pkt_control[i], (void*)p_dev_ctx);
+ if (p_dev_ctx->hook_cfg.process_cplane_fn) {
+ ret = p_dev_ctx->hook_cfg.process_cplane_fn(pkt_control[i], p_dev_ctx->hook_cfg.process_cplane_fn_args, xport_id, &sense_of_time);
+ } else {
+ ret = process_cplane(pkt_control[i], (void*)p_dev_ctx);
+ }
p_dev_ctx->fh_counters.rx_counter++;
if (ret == MBUF_FREE)
rte_pktmbuf_free(pkt_control[i]);
@@ -3987,10 +4013,12 @@ xran_start(void *pHandle)
char buff[100];
int i;
struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx();
- struct xran_prb_map * prbMap0 = (struct xran_prb_map *) p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[0][0][0].sBufferList.pBuffers->pData;
- for(i = 0; i < XRAN_MAX_SECTIONS_PER_SLOT && i < prbMap0->nPrbElm; i++)
- {
- p_xran_dev_ctx->numSetBFWs_arr[i] = prbMap0->prbMap[i].bf_weight.numSetBFWs;
+ if (p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[0][0][0].sBufferList.pBuffers) {
+ struct xran_prb_map * prbMap0 = (struct xran_prb_map *) p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[0][0][0].sBufferList.pBuffers->pData;
+ for(i = 0; i < XRAN_MAX_SECTIONS_PER_SLOT && i < prbMap0->nPrbElm; i++)
+ {
+ p_xran_dev_ctx->numSetBFWs_arr[i] = prbMap0->prbMap[i].bf_weight.numSetBFWs;
+ }
}
if(xran_get_if_state() == XRAN_RUNNING) {

View File

@@ -1229,6 +1229,49 @@ unsigned int get_prach_K(int prach_sequence_length, int prach_fmt_id, int pusch_
return K;
}
// Based on 38.211 Table 6.3.3.1-1 and Table 6.3.3.1-2.
uint32_t get_prach_num_reps(int prach_fmt_id)
{
switch (prach_fmt_id) {
case 0:
return 1;
break;
case 1:
return 2;
break;
case 2:
return 4;
break;
case 3:
return 4;
break;
case 4: // A1
return 2;
break;
case 5: // A2
return 4;
break;
case 6: // A3
return 6;
break;
case 7: // B1
return 2;
break;
case 8: // B4
return 12;
break;
case 9: // C0
return 1;
break;
case 10: // C2
return 4;
break;
default:
AssertFatal(0, "Invalid PRACH format %d\n", prach_fmt_id);
break;
}
}
int get_delay_idx(int delay, int max_delay_comp)
{
int delay_idx = max_delay_comp + delay;

View File

@@ -326,6 +326,7 @@ unsigned short get_N_b_srs(int c_srs, int b_srs);
uint8_t get_long_prach_dur(unsigned int format, unsigned int num_slots_subframe);
uint8_t get_PRACH_k_bar(unsigned int delta_f_RA_PRACH, unsigned int delta_f_PUSCH);
unsigned int get_prach_K(int prach_sequence_length, int prach_fmt_id, int pusch_mu, int prach_mu);
uint32_t get_prach_num_reps(int prach_fmt_id);
int get_slot_idx_in_period(const int slot, const frame_structure_t *fs);

View File

@@ -0,0 +1,96 @@
# Description
This document describes the steps needed to run the O-RU + gNB + NR UE on a single server.
# Limitations
The RU does not support fragmentation or compression at this point, therefore the bandwidth is currently limited.
# Dependecies
The O-RU requires a patch applied on top of the usual OAI patch to xran library release F. The patch is available
under `cmake_targets/tools/oran_fhi_integration_patches/F/oru.patch`.
The O-RU was tested with dpdk 22.
## Using a locally installed DPDK
Instead of installing dpdk globally you can install it in a local directory, e.g.
```bash
meson --prefix=<your dpdk install directory> build
cd build
ninja
ninja install
```
Building xran
```bash
cd fhi_lib/lib/
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=<your dpdk directory> XRAN_DIR=<your xran directory> make XRAN_LIB_SO=1
```
In the next step you'll add an extra argument to `cmake` command
# Configuration
Use `setup_ru_ifs.sh` as a basis for your own vf setup script.
# Compilation
Assuming your build directory is `cmake_targets/build`
```bash
cmake ../../ -GNinja -DOAI_FHI72=ON -Dxran_LOCATION=<your xran directory>/fhi_lib/lib
```
OR if using local dpdk installation
```bash
make ../../ -GNinja -DOAI_FHI72=ON -Dxran_LOCATION=<your xran directory>/fhi_lib/lib -DCMAKE_PREFIX_PATH=<your dpdk install directory>
```
Verify DPDK version in stdout:
```
-- Checking for module 'libdpdk'
-- Found libdpdk, version 22.11.11
```
```bash
cmake --build . --target nr-softmodem nr-uesoftmodem ldpc params_libconfig nr-oru oran_fhlib_5g vrtsim
```
# Setup
You need to setup your VFs to enable packet routing between them. An example config with comments is added in this folder.
# Test commands
## My local setup
```bash
sudo ./nr-oru -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/ru.band77.mu1.106rb.2x2.conf --vrtsim.role server
```
```bash
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.106prb.fhi72.2x2-nr-oru.conf --gNBs.[0].min_rxtxtime 6
```
```bash
sudo -E LD_LIBRARY_PATH=. ./nr-uesoftmodem -C 4049760000 -r 106 --numerology 1 --ssb 516 --band 77 --device.name vrtsim
```
## Example from peafowl (using locally installed dpdk)
```bash
sudo -E LD_LIBRARY_PATH=/home/bpodrygajlo/dpdk-stable-22.11.11/install/lib/x86_64-linux-gnu taskset -c 15-23 ./nr-oru -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/ru.band77.mu1.106rb.2x2.conf --vrtsim.role server --fhi_72.dpdk_devices 0000:01:01.0,0000:01:01.1
```
```bash
sudo -E LD_LIBRARY_PATH=/home/bpodrygajlo/dpdk-stable-22.11.11/install/lib/x86_64-linux-gnu taskset -c 24-31 ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.106prb.fhi72.2x2-nr-oru.conf --gNBs.[0].min_rxtxtime 6 --fhi_72.dpdk_devices 0000:01:01.2,0000:01:01.3
```
```bash
sudo -E LD_LIBRARY_PATH=. taskset -c -c 7-14 ./nr-uesoftmodem -C 4049760000 -r 106 --numerology 1 --ssb 516 --band 77 --device.name vrtsim
```

View File

@@ -0,0 +1,42 @@
#!/bin/sh
#### 100G interface --> enp1s0f0 | VFs pcie-address --> 01:01.0, 01:01.1
#### 25G interface (PTP) --> enp193s0f1 | VFs pcie-address --> c1:11.0,c1:11.1
set -x
# Set these in the DU config
RU_U_PLANE_MAC=00:11:22:33:64:66
RU_C_PLANE_MAC=00:11:22:33:64:67
# Set these in the RU config
DU_U_PLANE_MAC=00:11:22:33:64:68
DU_C_PLANE_MAC=00:11:22:33:64:69
U_VLAN=4
C_VLAN=5
MTU=9600
IF=enp1s0f0
## It will be something like this --> $DPDK_INST/bin
DPDK_DEVBIND_PREFIX="${DPDK_DEVBIND_PREFIX:-}"
sudo ethtool -G $IF rx 8160 tx 8160
sudo sh -c "echo 0 > /sys/class/net/$IF/device/sriov_numvfs"
sudo sh -c "echo 4 > /sys/class/net/$IF/device/sriov_numvfs"
sudo modprobe -r iavf
sudo modprobe iavf
# this next 2 lines is for C/U planes
sudo ip link set $IF vf 0 mac $RU_U_PLANE_MAC vlan $U_VLAN spoofchk off mtu $MTU
sudo ip link set $IF vf 1 mac $RU_C_PLANE_MAC vlan $C_VLAN spoofchk off mtu $MTU
sudo ip link set $IF vf 2 mac $DU_U_PLANE_MAC vlan $U_VLAN spoofchk off mtu $MTU
sudo ip link set $IF vf 3 mac $DU_C_PLANE_MAC vlan $C_VLAN spoofchk off mtu $MTU
sleep 1
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --unbind 0000:01:01.0
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --unbind 0000:01:01.1
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --unbind 0000:01:01.2
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --unbind 0000:01:01.3
sudo modprobe vfio-pci
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --bind vfio-pci 0000:01:01.0
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --bind vfio-pci 0000:01:01.1
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --bind vfio-pci 0000:01:01.2
sudo ${DPDK_DEVBIND_PREFIX}dpdk-devbind.py --bind vfio-pci 0000:01:01.3
sleep 5

View File

@@ -0,0 +1,38 @@
#!/bin/bash
set -x
# Set these in the DU config
RU_U_PLANE_MAC=00:11:22:33:64:66
RU_C_PLANE_MAC=00:11:22:33:64:67
# Set these in the RU config
DU_U_PLANE_MAC=00:11:22:33:64:68
DU_C_PLANE_MAC=00:11:22:33:64:69
# Using separate VLANs, it helps with capturing on some configurations
U_VLAN=3
C_VLAN=4
MTU=9216
IF=enp5s0f0np0
echo 0 | sudo tee /sys/bus/pci/devices/0000\:05\:00.0/sriov_numvfs
echo 4 | sudo tee /sys/bus/pci/devices/0000\:05\:00.0/sriov_numvfs
# this next 2 lines is for C/U planes
sudo ip link set $IF vf 0 mac $RU_U_PLANE_MAC vlan $U_VLAN qos 0 spoofchk off mtu $MTU
sudo ip link set $IF vf 1 mac $RU_C_PLANE_MAC vlan $C_VLAN qos 0 spoofchk off mtu $MTU
# this next 2 lines is for DU C/U planes
sudo ip link set $IF vf 2 mac $DU_U_PLANE_MAC vlan $U_VLAN qos 0 spoofchk off mtu $MTU
sudo ip link set $IF vf 3 mac $DU_C_PLANE_MAC vlan $C_VLAN qos 0 spoofchk off mtu $MTU
# bind to vfio-pci
sleep 1
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.0
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.1
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.2
sudo /usr/local/bin/dpdk-devbind.py --unbind 05:02.3
sudo modprobe vfio-pci
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.0
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.1
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.2
sudo /usr/local/bin/dpdk-devbind.py --bind vfio-pci 05:02.3
sleep 5

View File

@@ -43,7 +43,6 @@ RUN apt-get update && \
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
## Download and build DPDK
RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \
@@ -53,15 +52,30 @@ RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \
ninja -C build && \
ninja install -C build
COPY ./cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch ./cmake_targets/tools/oran_fhi_integration_patches/F/oru.patch /tmp/
## Build Fronthaul library
RUN git clone https://gerrit.o-ran-sc.org/r/o-du/phy.git /opt/phy && \
cd /opt/phy && \
git checkout oran_f_release_v1.0 &&\
git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch && \
git apply /tmp/oaioran_F.patch && \
git apply /tmp/oru.patch && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
COPY . .
FROM ran-base AS ran-build-fhi72
## Build FlexRIC for SM Models
RUN mkdir -p openair2/E2AP/flexric/build && \
cd openair2/E2AP/flexric/build && \
cmake -GNinja -DCMAKE_BUILD_TYPE=Release \
-DKPM_VERSION=$KPM_VERSION \
-DE2AP_VERSION=$E2AP_VERSION .. && \
ninja && \
ninja install
## Build and install OAI
#run build_oai to build the target image
RUN /bin/sh oaienv && \
@@ -76,12 +90,3 @@ RUN /bin/sh oaienv && \
ldd ran_build/build/nr-softmodem && \
ldd ran_build/build/liboran_fhlib_5g.so && \
ldd /opt/phy/fhi_lib/lib/build/libxran.so
## Build FlexRIC for SM Models
RUN mkdir -p openair2/E2AP/flexric/build && \
cd openair2/E2AP/flexric/build && \
cmake -GNinja -DCMAKE_BUILD_TYPE=Release \
-DKPM_VERSION=$KPM_VERSION \
-DE2AP_VERSION=$E2AP_VERSION .. && \
ninja && \
ninja install

View File

@@ -57,11 +57,10 @@ COPY ./docker/scripts/nr_oru_entrypoint.sh ./entrypoint.sh
COPY --from=ran-build \
/oai-ran/cmake_targets/ran_build/build/liboai_transpro.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/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
## Libxran library
@@ -77,10 +76,8 @@ RUN ldconfig && \
ldd /opt/nr-oru/bin/nr-oru && \
echo "---- ldd on shared libraries ----" && \
ldd /usr/local/lib/liboai_transpro.so \
/usr/local/lib/libcoding.so \
/usr/local/lib/libparams_libconfig.so \
/usr/local/lib/libdfts.so \
/usr/local/lib/libldpc*.so \
/opt/phy/fhi_lib/lib/build/libxran.so
WORKDIR /opt/nr-oru

View File

@@ -38,6 +38,9 @@
#include "nfapi/oai_integration/vendor_ext.h"
#include <executables/softmodem-common.h>
#include <executables/thread-common.h>
#include "nr-oru.h"
#include "openair1/PHY/INIT/nr_phy_init.h"
#include "openair1/SCHED_NR/sched_nr.h"
pthread_cond_t sync_cond;
pthread_mutex_t sync_mutex;
@@ -54,6 +57,8 @@ extern void kill_NR_RU_proc(int inst);
extern void set_function_spec_param(RU_t *ru);
extern void start_NR_RU();
extern void init_NR_RU(configmodule_interface_t *cfg, char *);
void fill_rf_config(RU_t *ru, char *rf_config_file);
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp);
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
@@ -61,6 +66,7 @@ void nfapi_setmode(nfapi_mode_t nfapi_mode)
{
return;
}
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
if (s != NULL) {
@@ -68,26 +74,14 @@ void exit_function(const char *file, const char *function, const int line, const
}
close_log_mem();
oai_exit = 1;
RU_t *ru = RC.ru[0];
if (ru->rfdevice.trx_end_func) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
ru->rfdevice.trx_end_func = NULL;
}
if (ru->ifdevice.trx_end_func) {
ru->ifdevice.trx_end_func(&ru->ifdevice);
ru->ifdevice.trx_end_func = NULL;
}
pthread_mutex_destroy(ru->ru_mutex);
pthread_cond_destroy(ru->ru_cond);
if (assert) {
if (assert)
abort();
} else {
sleep(1); // allow lte-softmodem threads to exit first
exit(EXIT_SUCCESS);
}
}
void stop_ru(int sig)
{
exit_function(__FILE__, __FUNCTION__, __LINE__, "interrupted", false);
}
static void get_options(configmodule_interface_t *cfg)
@@ -129,14 +123,6 @@ struct timespec timespec_sub(struct timespec, struct timespec)
return t;
};
void perform_symbol_rotation(NR_DL_FRAME_PARMS *fp, double f0, c16_t *symbol_rotation)
{
return;
}
void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp)
{
return;
};
int beam_index_allocation(bool das,
int fapi_beam_index,
NR_gNB_COMMON *common_vars,
@@ -186,6 +172,7 @@ int main(int argc, char **argv)
printf("About to Init RU threads\n");
lock_memory_to_ram();
load_dftslib();
RC.nb_RU = 1;
RC.ru = malloc(sizeof(RC.ru));
@@ -193,26 +180,87 @@ int main(int argc, char **argv)
init_NR_RU(config_get_if(), NULL);
RU_t *ru = RC.ru[0];
ORU_t oru = {0};
oru.ru = ru;
oru.num_sync_messages_needed = 2;
int ret = get_oru_options(&oru);
AssertFatal(ret == 0, "Cannot configure oru, check your config file/cmdline");
ru->numerology = oru.numerology;
oru_init_frame_parms(&oru);
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nr_dump_frame_parms(fp);
init_symbol_rotation(fp);
init_timeshift_rotation(fp);
ru->if_south = LOCAL_RF;
nr_phy_init_RU(oru.ru);
fill_rf_config(ru, ru->rf_config_file);
fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp);
ru->N_TA_offset = set_default_nta_offset(fp->freq_range, fp->samples_per_subframe);
/* set PRACH configuration */
nfapi_nr_prach_config_t *prach_config = &ru->config.prach_config;
prach_config->prach_ConfigurationIndex.value = oru.prach_config_index;
prach_config->num_prach_fd_occasions_list[0].k1.value = oru.prach_msg1_freq;
prach_config->prach_sequence_length.value = 1;
prach_config->prach_sub_c_spacing.value = 1;
prach_config->num_prach_fd_occasions.value = 1;
reset_meas(&oru.rx_prach);
oru.prach_info = get_nr_prach_occasion_info_from_index(oru.prach_config_index, FR1, fp->frame_type);
LOG_A(PHY, "PRACH configuration index %d\n", oru.prach_config_index);
LOG_A(PHY,
"PRACH format %d start_symbol %d duration %d\n",
oru.prach_info.format,
oru.prach_info.start_symbol,
oru.prach_info.N_dur);
prepare_prach_item(&oru);
ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport potocol\n", ru->idx, ret);
ret = ru->nr_start_if(ru, NULL);
AssertFatal(ret == 0, "Could not start xran\n");
LOG_I(PHY, "starting vrtsim\n");
ret = openair0_load(&ru->rfdevice, "vrtsim", &ru->openair0_cfg, NULL);
AssertFatal(ret == 0, "RU %u: openair0_load() ret %d: cannot initialize vrtsim\n", ru->idx, ret);
ret = ru->rfdevice.trx_start_func(&ru->rfdevice);
AssertFatal(ret == 0, "RU %u: trx_start_func() ret %d: cannot start vrtsim\n", ru->idx, ret);
signal(SIGINT, stop_ru);
signal(SIGTERM, stop_ru);
threadCreate(&oru.north_read_thread, oru_north_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
threadCreate(&oru.south_read_thread, oru_south_read_thread, (void *)&oru, "south_read_thread", -1, OAI_PRIORITY_RT_MAX);
threadCreate(&oru.oru_sync_thread, oru_sync_thread, (void *)&oru, "oru_sync_thread", -1, OAI_PRIORITY_RT_MAX);
while (oai_exit == 0)
sleep(1);
// stop threads
kill_NR_RU_proc(0);
ret = pthread_join(oru.oru_sync_thread, NULL);
AssertFatal(ret == 0, "pthread_join failed %d\n", ret);
ret = pthread_join(oru.north_read_thread, NULL);
AssertFatal(ret == 0, "pthread_join failed %d\n", ret);
ret = pthread_join(oru.south_read_thread, NULL);
AssertFatal(ret == 0, "pthread_join failed %d\n", ret);
LOG_I(PHY, "Threads joined\n");
end_configmodule(uniqCfg);
if (ru->rfdevice.trx_end_func) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
ru->rfdevice.trx_end_func = NULL;
if (ru->ifdevice.trx_stop_func) {
ru->ifdevice.trx_stop_func(&ru->ifdevice);
}
if (ru->ifdevice.trx_end_func) {
ru->ifdevice.trx_end_func(&ru->ifdevice);
ru->ifdevice.trx_end_func = NULL;
}
sleep(1);
if (ru->rfdevice.trx_stop_func) {
ru->rfdevice.trx_stop_func(&ru->rfdevice);
}
if (ru->rfdevice.trx_end_func) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
}
logClean();
end_configmodule(uniqCfg);
printf("Bye.\n");
return 0;
}

773
executables/nr-oru.c Normal file
View File

@@ -0,0 +1,773 @@
/*
* 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 "PHY/defs_RU.h"
#include "assertions.h"
#include "common/config/config_userapi.h"
#include "nr-oru.h"
#include "openair1/PHY/defs_nr_common.h"
#include "openair1/PHY/MODULATION/nr_modulation.h"
#include "openair1/SCHED_NR/sched_nr.h"
#include "openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h"
#include "thread-pool.h"
#define CONFIG_SECTION_ORU "ORUs.[0]"
// clang-format off
#define CONFIG_STRING_ORU_TX_BW_LIST "tx_bw"
#define CONFIG_STRING_ORU_RX_BW_LIST "rx_bw"
#define CONFIG_STRING_ORU_CARRIER_TX_LIST "carrier_tx"
#define CONFIG_STRING_ORU_CARRIER_RX_LIST "carrier_rx"
#define CONFIG_STRING_ORU_FRAME_TYPE "frame_type"
#define CONFIG_STRING_ORU_PRACH_CONFIGID "prach_config_index"
#define CONFIG_STRING_ORU_PRACH_MSG1FREQ "prach_msg1_start"
#define CONFIG_STRING_ORU_NUMEROLOGY "mu"
#define CONFIG_STRING_ORU_TDD_PERIOD "tdd_period"
#define CONFIG_STRING_ORU_NUM_DL_SLOTS "num_dl_slots"
#define CONFIG_STRING_ORU_NUM_UL_SLOTS "num_ul_slots"
#define CONFIG_STRING_ORU_NUM_DL_SYMBOLS "num_dl_symbols"
#define CONFIG_STRING_ORU_NUM_UL_SYMBOLS "num_ul_symbols"
#define CONFIG_STRING_ORU_TP_CORES "tp_cores"
#define HLP_ORU_TX_BW "set the TX bandwidth list per component carrier"
#define HLP_ORU_RX_BW "set the RX bandwidth list per component carrier"
#define HLP_ORU_CARRIER_TX "set the TX carrier frequencies per component carrier"
#define HLP_ORU_CARRIER_RX "set the RX carrier frequencies per component carrier"
#define HLP_ORU_FRAMETYPE "set the Frame type TDD/FDD of all component carriers"
#define HLP_ORU_PRACH_CONFIGID "set the PRACH configuration id of all component carriers"
#define HLP_ORU_PRACH_MSG1FREQ "set the PRACH MSG1 frequency of all component carriers"
#define HLP_ORU_NUMEROLOGY "set the numerology of the RU"
#define HLP_ORU_TDD_PERIOD "set the 3GPP TDD periodificty 0-9"
#define HLP_ORU_NUM_DL_SLOTS "set the number of DL Slots in TDD"
#define HLP_ORU_NUM_UL_SLOTS "set the number of UL Slots in TDD"
#define HLP_ORU_NUM_DL_SYMBOLS "set the number of DL symbols in the mixed slot"
#define HLP_ORU_NUM_UL_SYMBOLS "set the number of UL symbols in the mixed slot"
#define HLP_ORU_TP_CORES "CPU cores used for threadpool"
#define CMDLINE_PARAMS_DESC_ORU \
{ \
{CONFIG_STRING_ORU_TX_BW_LIST, HLP_ORU_TX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_RX_BW_LIST, HLP_ORU_RX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_CARRIER_TX_LIST, HLP_ORU_CARRIER_TX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_CARRIER_RX_LIST, HLP_ORU_CARRIER_RX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_FRAME_TYPE, HLP_ORU_FRAMETYPE, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_PRACH_CONFIGID, HLP_ORU_PRACH_CONFIGID, 0, .uptr=NULL, .defintval=152, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_PRACH_MSG1FREQ, HLP_ORU_PRACH_MSG1FREQ, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUMEROLOGY, HLP_ORU_NUMEROLOGY, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_TDD_PERIOD, HLP_ORU_TDD_PERIOD, 0, .uptr=NULL, .defintval=5, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_DL_SLOTS, HLP_ORU_NUM_DL_SLOTS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_UL_SLOTS, HLP_ORU_NUM_UL_SLOTS, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_DL_SYMBOLS, HLP_ORU_NUM_DL_SYMBOLS, 0, .uptr=NULL, .defintval=7, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_UL_SYMBOLS, HLP_ORU_NUM_UL_SYMBOLS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_TP_CORES, HLP_ORU_TP_CORES, 0, .iptr=NULL, .defintarrayval=DEFTPCORES, TYPE_INTARRAY, 4}, \
}
// clang-format on
extern void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case);
typedef struct {
openair0_timestamp_t sample;
int slot;
int frame;
int symbol;
} initial_sync_t;
typedef struct {
int frame_unwrap;
int last_frame;
int64_t sync_offset;
} sync_params_t;
typedef struct {
RU_t *ru;
NR_DL_FRAME_PARMS *fp;
int slot;
int start_symbol;
int num_symbols;
int aatx;
c16_t *txdataF;
task_ans_t *task_ans;
} dl_symbol_process_t;
typedef struct {
ORU_t *oru;
int frame;
int slot;
int symbol;
int aarx;
} pusch_symbol_job_t;
extern void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols);
static void receive_pusch(void *args);
extern void rx_nr_prach_ru_internal(prach_item_t *p,
int beam_id,
int prachStartSymbol,
int prachOccasion,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset,
int rep_index,
uint reps);
static void oru_downlink_processing(ORU_t *oru,
c16_t *txDataF_ptr[oru->ru->nb_tx],
int frame,
int slot,
int start_symbol,
int num_symbols,
openair0_timestamp_t timestamp_tx);
int get_oru_options(ORU_t *oru)
{
int DEFBW[] = {273};
int DEFCARRIER[] = {3430560};
int DEFTPCORES[] = {-1, -1, -1, -1};
paramdef_t param[] = CMDLINE_PARAMS_DESC_ORU;
int nump = sizeofArray(param);
int ret = config_get(config_get_if(), param, nump, CONFIG_SECTION_ORU);
if (ret <= 0) {
printf("problem reading section \"%s\"\n", CONFIG_SECTION_ORU);
return -1;
}
for (int i = 0; i < oru->ru->num_bands; i++) {
oru->bw_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_TX_BW_LIST)->iptr[i];
oru->bw_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_RX_BW_LIST)->iptr[i];
oru->carrier_freq_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_TX_LIST)->iptr[i];
oru->carrier_freq_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_RX_LIST)->iptr[i];
}
oru->frame_type = *gpd(param, nump, CONFIG_STRING_ORU_FRAME_TYPE)->iptr;
oru->prach_config_index = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_CONFIGID)->iptr;
oru->prach_msg1_freq = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_MSG1FREQ)->iptr;
oru->numerology = *gpd(param, nump, CONFIG_STRING_ORU_NUMEROLOGY)->iptr;
oru->tdd_period = *gpd(param, nump, CONFIG_STRING_ORU_TDD_PERIOD)->iptr;
oru->num_DL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SLOTS)->iptr;
oru->num_UL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SLOTS)->iptr;
oru->num_DL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SYMBOLS)->iptr;
oru->num_UL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SYMBOLS)->iptr;
int* tp_cores = gpd(param, nump, CONFIG_STRING_ORU_TP_CORES)->iptr;
int num_tp_cores = gpd(param, nump, CONFIG_STRING_ORU_TP_CORES)->numelt;
AssertFatal(num_tp_cores > 0, "No threadpool cores specified\n");
char tpool_config[(3 + 1) * num_tp_cores + 1];
char* tpool_config_p = tpool_config;
for (int i = 0; i < num_tp_cores; i++) {
int ret = snprintf(tpool_config_p, 4, "%d,", tp_cores[i]);
AssertFatal(ret > 0, "snprintf failed\n");
tpool_config_p += ret;
}
*tpool_config_p = '\0';
LOG_A(PHY, "ORU threadpool cores: %s\n", tpool_config);
initTpool(tpool_config, &oru->tpool, false);
return 0;
}
void oru_init_frame_parms(ORU_t *oru)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
fp->frame_type = oru->frame_type;
ru->config.cell_config.frame_duplex_type.value = oru->frame_type;
ru->config.cell_config.frame_duplex_type.tl.tag = 0x100D;
fp->N_RB_DL = oru->bw_tx[0];
ru->config.ssb_config.scs_common.value = ru->numerology;
ru->config.carrier_config.dl_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_tx[0];
fp->N_RB_UL = oru->bw_rx[0];
ru->config.carrier_config.ul_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_rx[0];
fp->numerology_index = ru->numerology;
LOG_I(NR_PHY,
"Set RU frame type to %s, N_RB_DL %d, N_RB_UL %d, mu %d\n",
oru->frame_type == TDD ? "TDD" : "FDD",
oru->bw_tx[0],
oru->bw_rx[0],
ru->numerology);
set_scs_parameters(fp, fp->numerology_index, oru->bw_tx[0], 0);
fp->slots_per_frame = 10 * fp->slots_per_subframe;
fp->nb_antennas_rx = ru->nb_rx;
fp->nb_antennas_tx = ru->nb_tx;
fp->symbols_per_slot = 14;
fp->samples_per_subframe_wCP = fp->ofdm_symbol_size * fp->symbols_per_slot * fp->slots_per_subframe;
fp->samples_per_frame_wCP = 10 * fp->samples_per_subframe_wCP;
fp->samples_per_slot_wCP = fp->symbols_per_slot * fp->ofdm_symbol_size;
fp->samples_per_slotN0 = (fp->nb_prefix_samples + fp->ofdm_symbol_size) * fp->symbols_per_slot;
fp->samples_per_slot0 =
fp->nb_prefix_samples0 + ((fp->symbols_per_slot - 1) * fp->nb_prefix_samples) + (fp->symbols_per_slot * fp->ofdm_symbol_size);
fp->samples_per_subframe = (fp->nb_prefix_samples0 + fp->ofdm_symbol_size) * 2
+ (fp->nb_prefix_samples + fp->ofdm_symbol_size) * (fp->symbols_per_slot * fp->slots_per_subframe - 2);
fp->samples_per_frame = 10 * fp->samples_per_subframe;
fp->freq_range = (oru->carrier_freq_tx[0] < 6e6) ? FR1 : FR2;
fp->dl_CarrierFreq = (double)oru->carrier_freq_tx[0] * 1000;
fp->ul_CarrierFreq = (double)oru->carrier_freq_rx[0] * 1000;
fp->Ncp = NORMAL;
fp->ofdm_offset_divisor = 8;
// Split 7.2 parameters
ru->config.prach_config.num_prach_fd_occasions.value = 1;
ru->config.prach_config.prach_ConfigurationIndex.value = oru->prach_config_index;
ru->config.prach_config.prach_ConfigurationIndex.tl.tag = 0x1029;
ru->config.prach_config.num_prach_fd_occasions_list = malloc(sizeof(*ru->config.prach_config.num_prach_fd_occasions_list));
ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value = oru->prach_msg1_freq;
if (ru->config.cell_config.frame_duplex_type.value == 1 /* TDD */) {
ru->config.tdd_table.tdd_period.value = oru->tdd_period;
ru->config.tdd_table.tdd_period.tl.tag = 0x1026;
int numb_slots_frame = (1 << ru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
int numb_period_frame = get_nb_periods_per_frame(oru->tdd_period);
int numb_slots_period = numb_slots_frame / numb_period_frame;
ru->config.tdd_table.max_tdd_periodicity_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list) * (numb_slots_frame));
for (int n = 0; n < numb_slots_frame; n++) {
int s = 0;
int p = n % numb_slots_period;
if (p < oru->num_DL_slots) {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_SYMBOLS_PER_SLOT);
for (s = 0; s < 14; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
} else if (p == oru->num_DL_slots) {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_SYMBOLS_PER_SLOT);
for (s = 0; s < oru->num_DL_symbols; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
for (; s < NR_SYMBOLS_PER_SLOT - oru->num_UL_symbols; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 2;
for (; s < NR_SYMBOLS_PER_SLOT; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
} else {
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list)
* NR_SYMBOLS_PER_SLOT);
for (s = 0; s < NR_SYMBOLS_PER_SLOT; s++)
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
}
}
}
}
void initialize_sync_params(NR_DL_FRAME_PARMS *fp, sync_params_t *sync_params, initial_sync_t *initial_sync)
{
sync_params->frame_unwrap = 0;
sync_params->last_frame = initial_sync->frame;
sync_params->sync_offset = initial_sync->sample;
sync_params->sync_offset -=
(uint64_t)(initial_sync->frame) * fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, initial_sync->slot);
}
static openair0_timestamp_t get_timestamp(ORU_t *oru, sense_of_time_t *sense_of_time, sync_params_t *sync_params)
{
if (sync_params->last_frame > sense_of_time->frame) {
sync_params->frame_unwrap++;
}
sync_params->last_frame = sense_of_time->frame;
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
int num_frames = sense_of_time->frame + sync_params->frame_unwrap * 1024;
uint64_t timestamp = (uint64_t)(num_frames)*fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, sense_of_time->slot)
+ get_samples_symbol_timestamp(fp, sense_of_time->slot, sense_of_time->symbol);
timestamp += sync_params->sync_offset;
return timestamp;
}
void receive_prach(ORU_t *oru, int frame, int slot, int prach_symbol)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
oru->prach_item.frame = frame;
oru->prach_item.slot = slot;
rx_nr_prach_ru_internal(&oru->prach_item,
0,
oru->prach_info.start_symbol,
0,
ru->common.rxdata,
fp,
ru->N_TA_offset,
prach_symbol,
1);
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
ru->ifdevice.xran_api.write_prach((uint32_t *)oru->prach_item.rxsigF[0][aarx], aarx, frame, slot, prach_symbol);
}
}
// Returns PRACH symbol that was received in current frame, slot and symbol.
// If no PRACH symbol was received, returns -1
int get_prach_symbol(ORU_t *oru, int frame, int slot, int symbol, int numerology)
{
uint16_t RA_sfn_index;
AssertFatal(oru->ru->nr_frame_parms->frame_type == TDD, "Only supports TDD\n");
if (get_nr_prach_sched_from_info(oru->prach_info, oru->prach_config_index, frame, slot, numerology, FR1, &RA_sfn_index, true)) {
int format = oru->prach_item.pdu.prach_format;
int start_symbol = oru->prach_item.pdu.prach_start_symbol;
symbol -= start_symbol;
// TODO: Support more PRACH formats
AssertFatal(format == 8, "only support format B4\n");
// TODO: This is not exactly the case but it is correct
if (symbol >= 0 && symbol < 12) {
return symbol;
}
}
return -1;
}
void *oru_north_read_thread(void *arg)
{
ORU_t *oru = (ORU_t *)arg;
RU_t *ru = (RU_t *)oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
AssertFatal(ru->ifdevice.xran_api.north_in_func != NULL, "No fronthaul interface at north port");
__attribute__((aligned(32))) c16_t txDataF[ru->nb_tx][fp->ofdm_symbol_size * 14];
memset(txDataF, 0, sizeof(txDataF));
c16_t *txDataF_ptr[ru->nb_tx];
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
txDataF_ptr[aatx] = txDataF[aatx];
}
ru->common.txdataF_BF = (int32_t **)txDataF_ptr;
notifiedFIFO_elt_t *elt = pullNotifiedFIFO(&oru->sync_fifo);
initial_sync_t *initial_sync = NotifiedFifoData(elt);
sync_params_t sync_params;
initialize_sync_params(fp, &sync_params, initial_sync);
LOG_A(PHY,
"ORU North read thread started at frame %d, slot %d, symbol %d\n",
initial_sync->frame,
initial_sync->slot,
initial_sync->symbol);
delNotifiedFIFO_elt(elt);
while (!oai_exit) {
int num_symbols = 0;
sense_of_time_t sense_of_time;
ru->ifdevice.xran_api.north_in_func((uint32_t **)txDataF_ptr, ru->nb_tx, &sense_of_time, &num_symbols);
openair0_timestamp_t timestamp_tx = get_timestamp(oru, &sense_of_time, &sync_params);
if ((sense_of_time.frame % 256 == 0) && sense_of_time.slot == 0) {
LOG_I(PHY,
"[RU_thread] read data: frame %d, slot %d, start_symbol %d, num_symbols %d\n",
sense_of_time.frame,
sense_of_time.slot,
sense_of_time.symbol,
num_symbols);
}
nfapi_nr_config_request_scf_t *cfg = &ru->config;
int slot_type = nr_slot_select(cfg, sense_of_time.frame, sense_of_time.slot % fp->slots_per_frame);
if (slot_type != NR_UPLINK_SLOT) {
oru_downlink_processing(oru,
txDataF_ptr,
sense_of_time.frame,
sense_of_time.slot,
sense_of_time.symbol,
num_symbols,
timestamp_tx);
}
}
return NULL;
}
void rx_initial_sync(ORU_t *oru, int *slot, int *frame)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
const int num_samples = 3000;
c16_t throwaway_samples[ru->nb_rx][num_samples];
void *rxp[ru->nb_rx];
for (int i = 0; i < ru->nb_rx; i++)
rxp[i] = throwaway_samples[i];
openair0_timestamp_t timestamp;
initial_sync_t initial_sync;
while (!oai_exit) {
int samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, rxp, num_samples, ru->nb_rx);
AssertFatal(samples_read == num_samples, "Unexpected number of samples received\n");
notifiedFIFO_elt_t *elt = pollNotifiedFIFO(&oru->sync_fifo);
if (elt) {
memcpy(&initial_sync, NotifiedFifoData(elt), sizeof(initial_sync));
break;
}
}
// Synchornize to ORAN timing
int next_slot = initial_sync.slot;
int next_frame = initial_sync.frame;
openair0_timestamp_t next_sample = timestamp + num_samples;
int64_t diff = next_sample - initial_sync.sample;
LOG_I(PHY,
"Sychronizing to frame slot %d.%d, sample %ld next_sample %ld diff %ld\n",
next_frame,
next_slot,
initial_sync.sample,
next_sample,
diff);
uint64_t samples_to_sync_by = 0;
if (diff < 0) {
samples_to_sync_by = -diff;
} else {
while (diff > 0) {
uint32_t samples_per_slot = get_samples_per_slot(next_slot, fp);
samples_to_sync_by += samples_per_slot;
diff -= samples_per_slot;
next_slot++;
if (next_slot == fp->slots_per_frame) {
next_slot = 0;
next_frame++;
if (next_frame == 1024) {
next_frame = 0;
}
}
}
samples_to_sync_by += diff;
}
LOG_I(PHY, "Thrashing %lu samples to sync to slot %d, frame %d\n", samples_to_sync_by, next_slot, next_frame);
while (!oai_exit && samples_to_sync_by > 0) {
int samples_to_read = min(num_samples, samples_to_sync_by);
int samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, rxp, samples_to_read, ru->nb_rx);
AssertFatal(samples_to_read == samples_read, "Unexpected number of samples received\n");
samples_to_sync_by -= samples_to_read;
}
*slot = next_slot;
*frame = next_frame;
}
void *oru_south_read_thread(void *arg)
{
ORU_t *oru = arg;
int slot;
int frame;
rx_initial_sync(oru, &slot, &frame);
LOG_A(PHY, "ORU South read thread started at frame %d, slot %d\n", frame, slot);
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
const int max_pusch_jobs = 300;
pusch_symbol_job_t pusch_job_pool[max_pusch_jobs];
uint32_t pusch_job_index = 0;
while (!oai_exit) {
int rx_slot_type = nr_slot_select(&ru->config, frame, slot);
for (int symbol = 0; symbol < 14; symbol++) {
int samples_to_read = get_samples_symbol_duration(fp, slot, symbol, 1);
size_t offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, symbol);
c16_t *rxp[fp->nb_antennas_rx];
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
rxp[aarx] = (c16_t *)&ru->common.rxdata[aarx][offset];
}
openair0_timestamp_t timestamp;
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, (void **)rxp, samples_to_read, ru->nb_rx);
AssertFatal(num_samples_read == samples_to_read, "Unexpected number of samples received\n");
LOG_D(PHY,
"[ORU south] read data: frame %d, slot %d, symbol %d, timestamp %ld num_symbols %d, samples %d\n",
frame,
slot,
symbol,
timestamp,
1,
num_samples_read);
if (rx_slot_type == NR_UPLINK_SLOT || rx_slot_type == NR_MIXED_SLOT) {
nfapi_nr_config_request_scf_t *config = &ru->config;
nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;
AssertFatal(tdd_table->tdd_period.tl.tag == NFAPI_NR_CONFIG_TDD_PERIOD_TAG, "");
int nb_periods_per_frame = get_nb_periods_per_frame(tdd_table->tdd_period.value);
int n_tdd_period = fp->slots_per_frame / nb_periods_per_frame;
nfapi_nr_max_num_of_symbol_per_slot_t *max_num_of_symbol_per_slot_list =
config->tdd_table.max_tdd_periodicity_list[slot % n_tdd_period].max_num_of_symbol_per_slot_list;
if (max_num_of_symbol_per_slot_list[symbol].slot_config.value != 1)
continue;
int prach_symbol = get_prach_symbol(oru, frame, slot, symbol, ru->numerology);
if (prach_symbol != -1) {
receive_prach(oru, frame, slot, prach_symbol);
}
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
pusch_symbol_job_t *job = &pusch_job_pool[pusch_job_index++ % max_pusch_jobs];
job->oru = oru;
job->aarx = aarx;
job->frame = frame;
job->slot = slot;
job->symbol = symbol;
task_t task = {.func = receive_pusch, .args = job};
pushTpool(&oru->tpool, task);
}
stop_meas(&oru->rx);
}
}
slot++;
if (slot == fp->slots_per_frame) {
slot = 0;
frame++;
if (frame == 1024) {
frame = 0;
}
}
if (frame % 256 == 0 && slot == 0) {
LOG_I(PHY,
"[ORU south] read data: frame %d, slot %d\n", frame, slot);
}
}
// Perform RX processing
return NULL;
}
void perform_initial_sync(ORU_t *oru, sense_of_time_t *sense_of_time, initial_sync_t *initial_sync)
{
initial_sync->frame = sense_of_time->frame;
initial_sync->slot = sense_of_time->slot;
initial_sync->symbol = sense_of_time->symbol;
initial_sync->sample = oru->ru->rfdevice.get_timestamp(&oru->ru->rfdevice, &sense_of_time->ts);
LOG_I(PHY,
"RU synchronized: frame, slot %d.%d, symbol %d, sample: %ld\n",
initial_sync->frame,
initial_sync->slot,
initial_sync->symbol,
initial_sync->sample);
}
void *oru_sync_thread(void *arg)
{
ORU_t *oru = (ORU_t *)arg;
RU_t *ru = (RU_t *)oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
AssertFatal(ru->ifdevice.xran_api.north_in_func != NULL, "No fronthaul interface at north port");
__attribute__((aligned(32))) c16_t txDataF[ru->nb_tx][ceil_mod(fp->ofdm_symbol_size * 14, 32)];
c16_t *txDataF_ptr[ru->nb_tx];
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
txDataF_ptr[aatx] = txDataF[aatx];
}
initial_sync_t initial_sync;
while (!oai_exit) {
int num_symbols = 0;
sense_of_time_t sense_of_time;
ru->ifdevice.xran_api.north_in_func((uint32_t **)txDataF_ptr, ru->nb_tx, &sense_of_time, &num_symbols);
if (sense_of_time.symbol == 0) {
perform_initial_sync(oru, &sense_of_time, &initial_sync);
break;
}
}
for (int i = 0; i < oru->num_sync_messages_needed; i++) {
notifiedFIFO_elt_t *sync_msg = newNotifiedFIFO_elt(sizeof(initial_sync_t), 0, NULL, NULL);
initial_sync_t *initial_sync_p = NotifiedFifoData(sync_msg);
*initial_sync_p = initial_sync;
pushNotifiedFIFO(&oru->sync_fifo, sync_msg);
}
return NULL;
}
static void dl_symbol_process(void *arg)
{
dl_symbol_process_t *args = (dl_symbol_process_t *)arg;
apply_nr_rotation_TX(args->fp,
args->txdataF,
false,
args->fp->symbol_rotation[0],
args->slot,
args->fp->N_RB_DL,
args->start_symbol,
args->num_symbols);
nr_feptx0(args->ru, args->slot, args->start_symbol, args->num_symbols, args->aatx);
completed_task_ans(args->task_ans);
}
static void oru_downlink_processing(ORU_t *oru,
c16_t *txDataF_ptr[oru->ru->nb_tx],
int frame,
int slot,
int start_symbol,
int num_symbols,
openair0_timestamp_t timestamp_tx)
{
static int dl_proc_count = 0;
if (dl_proc_count++ < 10) {
LOG_I(PHY, "[ORU] oru_downlink_processing ENTER: frame %d, slot %d, sym %d, num_sym %d\n",
frame, slot, start_symbol, num_symbols);
}
RU_t *ru = oru->ru;
start_meas(&ru->tx_fhaul);
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
int num_paralell_workers_per_antenna = num_symbols > 4 ? 2 : 1; // Ensure at least quarter slot parallelization
task_t tasks[ru->nb_tx][num_paralell_workers_per_antenna];
dl_symbol_process_t dl_process_args[ru->nb_tx][num_paralell_workers_per_antenna];
task_ans_t task_ans;
init_task_ans(&task_ans, num_paralell_workers_per_antenna * ru->nb_tx);
if (dl_proc_count <= 10) {
LOG_I(PHY, "[ORU] Scheduling %d tasks (%d antennas x %d workers)\n",
ru->nb_tx * num_paralell_workers_per_antenna, ru->nb_tx, num_paralell_workers_per_antenna);
}
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
for (int i = 0; i < num_paralell_workers_per_antenna; i++) {
tasks[aatx][i].func = dl_symbol_process;
tasks[aatx][i].args = (void *)&dl_process_args[aatx][i];
dl_process_args[aatx][i].ru = ru;
dl_process_args[aatx][i].fp = fp;
dl_process_args[aatx][i].slot = slot;
dl_process_args[aatx][i].start_symbol = start_symbol + num_symbols / num_paralell_workers_per_antenna * i;
dl_process_args[aatx][i].num_symbols =
min(num_symbols / num_paralell_workers_per_antenna, num_symbols - (num_symbols / num_paralell_workers_per_antenna) * i);
dl_process_args[aatx][i].aatx = aatx;
dl_process_args[aatx][i].txdataF = txDataF_ptr[aatx];
dl_process_args[aatx][i].task_ans = &task_ans;
pushTpool(&oru->tpool, tasks[aatx][i]);
}
}
if (dl_proc_count <= 10) {
LOG_I(PHY, "[ORU] Tasks scheduled, joining...\n");
}
join_task_ans(&task_ans);
if (dl_proc_count <= 10) {
LOG_I(PHY, "[ORU] Tasks completed, calling tx_rf_symbols for frame %d, slot %d\n", frame, slot);
}
tx_rf_symbols(ru, frame, slot, timestamp_tx, start_symbol, num_symbols);
if (dl_proc_count <= 10) {
LOG_I(PHY, "[ORU] tx_rf_symbols returned\n");
}
stop_meas(&ru->tx_fhaul);
}
void prepare_prach_item(ORU_t *oru)
{
AssertFatal(oru->ru != NULL, "ORU not configured\n");
AssertFatal(oru->ru->nr_frame_parms != NULL, "ORU not configured\n");
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
RU_t *ru = oru->ru;
prach_item_t *prach_item = &oru->prach_item;
prach_item->num_slots = oru->prach_info.format < 4 ? get_long_prach_dur(oru->prach_info.format, fp->numerology_index) : 1;
prach_item->msg1_frequencystart = oru->prach_msg1_freq;
prach_item->mu = fp->numerology_index;
nfapi_nr_config_request_scf_t *cfg = &ru->config;
prach_item->prach_sequence_length = cfg->prach_config.prach_sequence_length.value;
prach_item->restricted_set = 0;
prach_item->numerology_index = fp->numerology_index;
prach_item->nb_rx = ru->nb_rx;
prach_item->rx_prach = &oru->rx_prach;
prach_item->beams[0] = 0; // TODO: Beamforming not supported yet
// Fill PRACH PDU
nfapi_nr_prach_pdu_t *prach_pdu = &prach_item->pdu;
prach_pdu->prach_start_symbol = oru->prach_info.start_symbol;
prach_pdu->num_prach_ocas = 1; // TODO: Hardcoded.
uint16_t format0 = oru->prach_info.format & 0xff;
uint16_t format1 = (oru->prach_info.format >> 8) & 0xff;
if (format1 != 0xff) {
switch (format0) {
case 0xa1:
prach_pdu->prach_format = 11;
break;
case 0xa2:
prach_pdu->prach_format = 12;
break;
case 0xa3:
prach_pdu->prach_format = 13;
break;
default:
AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
}
} else {
switch (format0) {
case 0:
prach_pdu->prach_format = 0;
break;
case 1:
prach_pdu->prach_format = 1;
break;
case 2:
prach_pdu->prach_format = 2;
break;
case 3:
prach_pdu->prach_format = 3;
break;
case 0xa1:
prach_pdu->prach_format = 4;
break;
case 0xa2:
prach_pdu->prach_format = 5;
break;
case 0xa3:
prach_pdu->prach_format = 6;
break;
case 0xb1:
prach_pdu->prach_format = 7;
break;
case 0xb4:
prach_pdu->prach_format = 8;
break;
case 0xc0:
prach_pdu->prach_format = 9;
break;
case 0xc2:
prach_pdu->prach_format = 10;
break;
default:
AssertFatal(1 == 0, "Invalid PRACH format");
}
}
}
static void receive_pusch(void *args)
{
pusch_symbol_job_t *job = args;
ORU_t *oru = job->oru;
int frame = job->frame;
int slot = job->slot;
int symbol = job->symbol;
int aarx = job->aarx;
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
c16_t rxdataF[fp->ofdm_symbol_size] __attribute__((aligned(32)));
nr_symbol_fep_ul(fp, (c16_t *)ru->common.rxdata[aarx], rxdataF, symbol, slot, ru->N_TA_offset);
apply_nr_rotation_symbol_RX(fp,
rxdataF,
fp->symbol_rotation[link_type_ul],
fp->N_RB_UL,
slot,
symbol);
ru->ifdevice.xran_api.write_pusch((uint32_t *)rxdataF, aarx, frame, slot, symbol);
}

78
executables/nr-oru.h Normal file
View File

@@ -0,0 +1,78 @@
/*
* 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
*/
#ifndef __NR_ORU_H__
#define __NR_ORU_H__
#include "PHY/defs_nr_common.h"
#include "openair1/PHY/defs_RU.h"
#include "thread-pool.h"
#include "common/utils/nr/nr_common.h"
#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h"
typedef struct {
RU_t *ru;
/// tx carrier
uint64_t carrier_freq_tx[MAX_BANDS_PER_RRU];
/// rx carrier
uint64_t carrier_freq_rx[MAX_BANDS_PER_RRU];
/// tx BW in PRBs
int bw_tx[MAX_BANDS_PER_RRU];
/// rx BW in PRBs
int bw_rx[MAX_BANDS_PER_RRU];
/// 3GPP FRAME Type FDD/TDD
int frame_type;
/// 3GPP PRACH configuration index
int prach_config_index;
/// 3GPP MSG1 Start frequency
int prach_msg1_freq;
/// 3GPP TDD periodicity (0.5 ms, 1 0.625ms, 2 1ms, 3 1.25ms, 4 2ms,5 2.5ms, 6 5ms, 7 10ms, 8 3ms, 9 4ms
int tdd_period;
/// number of DL slots
int num_DL_slots;
/// number of UL slots
int num_UL_slots;
/// number of DL symbols
int num_DL_symbols;
/// number of UL symbols
int num_UL_symbols;
int numerology;
pthread_t north_read_thread;
pthread_t south_read_thread;
pthread_t oru_sync_thread;
int num_sync_messages_needed;
notifiedFIFO_t sync_fifo;
tpool_t tpool;
// PRACH related
nr_prach_info_t prach_info;
time_stats_t rx_prach;
time_stats_t rx;
prach_item_t prach_item;
} ORU_t;
int get_oru_options(ORU_t *oru);
void oru_init_frame_parms(ORU_t *oru);
void *oru_north_read_thread(void *arg);
void *oru_south_read_thread(void *arg);
void *oru_sync_thread(void *arg);
void prepare_prach_item(ORU_t *oru);
#endif

View File

@@ -516,7 +516,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
return flags_gpio;
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols)
{
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
@@ -543,22 +543,23 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
txsymb++;
}
AssertFatal(txsymb>0,"illegal txsymb %d\n",txsymb);
AssertFatal(txsymb > 0, "illegal txsymb %d\n", txsymb);
if (fp->slots_per_subframe == 1) {
if (txsymb <= 7)
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = 2 * (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 2) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
} else {
if(slot%(fp->slots_per_subframe/2))
siglen = txsymb * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
if (txsymb < start_symbol) {
// No DL symbols in this transmission
return;
}
//+ ru->end_of_burst_delay;
flags_burst = TX_BURST_END;
int end_symbol = start_symbol + num_symbols - 1;
if (end_symbol >= txsymb) {
flags_burst = TX_BURST_END;
} else {
flags_burst = TX_BURST_MIDDLE;
}
int num_symbols_this_transmission = min(txsymb, end_symbol) - start_symbol + 1;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols_this_transmission);
} else if (slot_type == NR_DOWNLINK_SLOT) {
int prevslot_type = nr_slot_select(cfg,frame,(slot+(fp->slots_per_frame-1))%fp->slots_per_frame);
int nextslot_type = nr_slot_select(cfg,frame,(slot+1)%fp->slots_per_frame);
@@ -570,9 +571,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
} else {
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
}
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
} else { // FDD
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
@@ -583,8 +586,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
int nt = ru->nb_tx * ru->num_beams_period;
void *txp[nt];
uint32_t time_offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, start_symbol);
for (int i = 0; i < nt; i++)
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
txp[i] = (void *)&ru->common.txdata[i][time_offset] - sf_extension * sizeof(int32_t);
// prepare tx buffer pointers
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
@@ -593,6 +597,22 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
siglen + sf_extension,
nt,
flags);
// Data verification logging for vrtsim writes
static int vrtsim_write_count = 0;
if ((slot == 0 && vrtsim_write_count++ < 20) || (frame % 256 == 0 && slot == 0)) {
double energy = signal_energy(txp[0], siglen + sf_extension);
int32_t *samples = (int32_t *)txp[0];
LOG_I(PHY, "[VRTSIM WRITE] frame %d, slot %d, symbol %d-%d: wrote %d samples, energy=%.2f dB, "
"first_samples[0]=%d+%di [1]=%d+%di [2]=%d+%di, txs_ret=%d\n",
frame, slot, start_symbol, start_symbol + num_symbols - 1, siglen + sf_extension,
10 * log10(energy > 0 ? energy : 1.0),
((int16_t*)samples)[0], ((int16_t*)samples)[1],
((int16_t*)samples)[2], ((int16_t*)samples)[3],
((int16_t*)samples)[4], ((int16_t*)samples)[5],
txs);
}
LOG_D(PHY,
"[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, "
"returned %d, E %f\n",
@@ -608,7 +628,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
}
static void fill_rf_config(RU_t *ru, char *rf_config_file)
void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
tx_rf_symbols(ru, frame, slot, timestamp, 0, 14);
}
void fill_rf_config(RU_t *ru, char *rf_config_file)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index
@@ -671,7 +696,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
}
}
static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
{
const nfapi_nr_prach_config_t *prach_config = &config->prach_config;
const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;

View File

@@ -84,7 +84,7 @@ int nr_get_ssb_start_symbol(const NR_DL_FRAME_PARMS *fp, uint8_t i_ssb)
return symbol;
}
static void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
{
switch(mu) {
case NR_MU_0: //15kHz scs

View File

@@ -125,13 +125,15 @@ prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_n
return prach;
}
static void rx_nr_prach_ru_internal(prach_item_t *p,
int beam_id,
int prachStartSymbol,
int prachOccasion,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset)
void rx_nr_prach_ru_internal(prach_item_t *p,
int beam_id,
int prachStartSymbol,
int prachOccasion,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset,
int rep_index,
uint reps)
{
int sample_offset_slot;
const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples;
@@ -158,7 +160,6 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
p->pdu.num_ra,
prachStartSymbol,
prachOccasion);
int reps;
int Ncp;
int dftlen;
int mu = p->numerology_index;
@@ -172,25 +173,21 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
p->msg1_frequencystart);
switch (p->pdu.prach_format) {
case 0:
reps = 1;
Ncp = 3168;
dftlen = 24576;
break;
case 1:
reps = 2;
Ncp = 21024;
dftlen = 24576;
break;
case 2:
reps = 4;
Ncp = 4688;
dftlen = 24576;
break;
case 3:
reps = 4;
Ncp = 3168;
dftlen = 6144;
break;
@@ -209,22 +206,18 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
prachStartSymbol);
switch (p->pdu.prach_format) {
case 4: // A1
reps = 2;
Ncp = 288 >> mu;
break;
case 5: // A2
reps = 4;
Ncp = 576 >> mu;
break;
case 6: // A3
reps = 6;
Ncp = 864 >> mu;
break;
case 7: // B1
reps = 2;
Ncp = 216 >> mu;
break;
@@ -242,17 +235,14 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
*/
case 8: // B4
reps = 12;
Ncp = 936 >> mu;
break;
case 9: // C0
reps = 1;
Ncp = 1240 >> mu;
break;
case 10: // C2
reps = 4;
Ncp = 2048 >> mu;
break;
@@ -366,18 +356,25 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
// do DFT
c16_t *prach2 = prach + Ncp;
c16_t rxsigF_tmp[N_ZC];
memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp));
for (int i = 0; i < reps; i++, prach2 += dftlen) {
for (int i = 0; i < reps; i++) {
c16_t tmp[dftlen] __attribute__((aligned(32)));
dft(dftsize, (int16_t *)prach2, (int16_t *)tmp, 1);
dft(dftsize, (int16_t *)(prach2 + (rep_index + i) * dftlen), (int16_t *)tmp, 1);
// Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH)
LOG_D(PHY, "Doing PRACH combining of %d reptitions N_ZC %d\n", reps, N_ZC);
// if (k+N_ZC > dftlen) { // PRACH signal is split around DC
int k2 = k;
for (int j = 0; j < N_ZC; j++, k2++) {
if (k2 == dftlen)
k2 = 0;
rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]);
if (i == 0) {
for (int j = 0; j < N_ZC; j++, k2++) {
if (k2 == dftlen)
k2 = 0;
rxsigF_tmp[j] = tmp[k2];
}
} else {
for (int j = 0; j < N_ZC; j++, k2++) {
if (k2 == dftlen)
k2 = 0;
rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]);
}
}
}
memcpy(p->rxsigF[prachOccasion][aa], rxsigF_tmp, sizeof(rxsigF_tmp));
@@ -394,7 +391,15 @@ void rx_nr_prach_ru(prach_item_t *p, int32_t **rxdata, NR_DL_FRAME_PARMS *fp, in
// comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is
// given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots
// where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}.
rx_nr_prach_ru_internal(p, beam_id, prachStartSymbol, prach_oc, rxdata, fp, N_TA_offset);
rx_nr_prach_ru_internal(p,
beam_id,
prachStartSymbol,
prach_oc,
rxdata,
fp,
N_TA_offset,
0,
get_prach_num_reps(p->pdu.prach_format));
}
}

View File

@@ -197,7 +197,7 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
fp->samples_per_slot_wCP * sizeof(int32_t));
}
}
} else {
} else {
AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n");
}
stop_meas(&ru->precoding_stats);

View File

@@ -274,6 +274,22 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
for (int i = 0; i < UL_dci_req->numPdus; ++i)
nr_generate_dci(gNB, &UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
// Debug: log DL PDU scheduling
static int dl_sched_log_count = 0;
if ((slot == 0 && dl_sched_log_count++ < 30) || (frame % 256 == 0 && slot == 0)) {
LOG_I(PHY, "[MAC SCHED] frame %d, slot %d: DL_req has %d PDUs\n",
frame, slot, DL_req->dl_tti_request_body.nPDUs);
for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) {
const nfapi_nr_dl_tti_request_pdu_t *pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i];
const char *pdu_name = "UNKNOWN";
if (pdu->PDUType == NFAPI_NR_DL_TTI_SSB_PDU_TYPE) pdu_name = "SSB";
else if (pdu->PDUType == NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE) pdu_name = "PDCCH";
else if (pdu->PDUType == NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE) pdu_name = "CSI_RS";
else if (pdu->PDUType == NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE) pdu_name = "PDSCH";
LOG_I(PHY, "[MAC SCHED] PDU[%d]: type=%s (%d)\n", i, pdu_name, pdu->PDUType);
}
}
int num_pdsch = 0;
for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) {
const nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i];

View File

@@ -165,6 +165,19 @@ int openair0_transport_load(openair0_device_t *device, openair0_config_t *openai
return rc;
}
int openair0_load(openair0_device_t *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params)
{
loader_shlibfunc_t shlib_fdesc[1];
int ret = 0;
shlib_fdesc[0].fname = eth_params == NULL ? "device_init" : "transport_init";
ret = load_module_shlib(name, shlib_fdesc, 1, NULL);
AssertFatal((ret >= 0), "Library %s couldn't be loaded\n", name);
return ((devfunc_t)shlib_fdesc[0].fptr)(device, openair0_cfg, eth_params);
}
static void writerEnqueue(re_order_t *ctx, openair0_timestamp_t timestamp, void **txp, int nsamps, int nbAnt, int flags)
{
pthread_mutex_lock(&ctx->mutex_store);

View File

@@ -382,6 +382,14 @@ typedef struct {
} queue[WRITE_QUEUE_SZ];
} re_order_t;
/*! \brief Provides a way to map between a symbol and a timespec */
typedef struct {
int frame;
int slot;
int symbol;
struct timespec ts;
} sense_of_time_t;
/*!\brief structure holds the parameters to configure RF devices */
struct openair0_device {
/*!tx write thread*/
@@ -571,6 +579,10 @@ struct openair0_device {
*/
int (*trx_stop_func)(openair0_device_t *device);
/*! \brief Get timestamp from timespec
*/
openair0_timestamp_t (*get_timestamp)(openair0_device_t *device, struct timespec *ts);
/* Functions API related to UE*/
/*! \brief Set RX feaquencies
@@ -657,6 +669,38 @@ struct openair0_device {
*/
time_stats_t tx_fhaul;
re_order_t reOrder;
// Function pointers used for oran
struct {
/*! \brief O-RU only: reads DL FD IQ. Data is put into the beginning of txdataF buffer regardless
* of the returned start_symbol. maximum number of symbols returned is 7
* \param txdataF An array of nb_tx buffers to write the samples to
* \param nb_tx number of TX antennas and number of buffer in txDataF_BF
* \param sense_of_time frame, slot and symbol with mapping to clock_gettime result
* \param num_symbols number of symbols
*/
void (*north_in_func)(uint32_t **txdataF, int nb_tx, sense_of_time_t* sense_of_time, int *num_symbols);
/*!
* \brief Write prach data for one PRACH symbol
* \param prachF Frequency domain PRACH data size 139 (only short format support)
* \param aarx
* \param frame
* \param slot
* \param symbol
*/
void (*write_prach)(uint32_t *prachF, int aarx, int frame, int slot, int symbol);
/*!
* \brief Write data for one PUSCH symbol
* \param prachF Frequency domain PUSCH data
* \param aarx
* \param frame
* \param slot
* \param symbol
*/
void (*write_pusch)(uint32_t *prachF, int aarx, int frame, int slot, int symbol);
} xran_api;
};
typedef struct {
@@ -698,6 +742,7 @@ const char *get_devname(int devtype);
int openair0_device_load(openair0_device_t *device, openair0_config_t *openair0_cfg);
/*! \brief Initialize transport protocol . It returns 0 if OK */
int openair0_transport_load(openair0_device_t *device, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
int openair0_load(openair0_device_t *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
/*! \brief Set RX frequencies
* \param device the hardware to use

View File

@@ -11,6 +11,7 @@ pkg_check_modules(numa REQUIRED numa)
add_library(oran_fhlib_5g MODULE
oran_isolate.c
oaioran_ru.c
oaioran.c
oran-config.c
oran-init.c
@@ -33,7 +34,7 @@ add_compile_options(-Wno-packed-not-aligned)
set_target_properties(oran_fhlib_5g PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -march=native")
target_link_libraries(oran_fhlib_5g PRIVATE xran::xran ${dpdk_LINK_LIBRARIES} pthread dl rt m numa)
target_link_libraries(oran_fhlib_5g PRIVATE log_headers)
target_link_libraries(oran_fhlib_5g PRIVATE log_headers MAC_NR_COMMON)
target_include_directories(oran_fhlib_5g PRIVATE ${dpdk_INCLUDE_DIRS})
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
find_package(armral REQUIRED)

View File

@@ -23,6 +23,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <time.h>
#include "xran_fh_o_du.h"
#include "xran_compression.h"
#include "armral_bfp_compression.h"
@@ -91,7 +92,7 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
uint32_t rx_sym = callback_tag->symbol & 0xFF;
uint32_t ru_id = callback_tag->oXuId;
LOG_D(HW, "rx_callback at %4d.%3d (subframe %d), rx_sym %d ru_id %d\n", frame, slot, subframe, rx_sym, ru_id);
// LOG_D(HW, "rx_callback at %4d.%3d (subframe %d), rx_sym %d ru_id %d\n", frame, slot, subframe, rx_sym, ru_id);
if (rx_sym == 7) { // in F release this value is defined as XRAN_FULL_CB_SYM (full slot (offset + 7))
#if defined F_RELEASE
@@ -104,7 +105,7 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
AssertFatal(pRbMap != NULL, "(%d:%d:%d)pRbMap == NULL. Aborting.\n", cc_id, tti % XRAN_N_FE_BUF_LEN, ant_id);
for (uint32_t sym_id = 0; sym_id < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_id++) {
LOG_D(HW, "cb pRbMap->nPrbElm %d\n", pRbMap->nPrbElm);
// LOG_D(HW, "cb pRbMap->nPrbElm %d\n", pRbMap->nPrbElm);
for (uint32_t idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++ ) {
struct xran_prb_elm *pRbElm = &pRbMap->prbMap[idxElm];
pRbElm->nSecDesc[sym_id] = 0; // number of section descriptors per symbol; M-plane info <supported-section-types>
@@ -138,7 +139,7 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
info->tti = tti;
info->sl = slot2;
info->f = frame;
LOG_D(HW, "Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, info->sl, slot, ru_id, subframe, last_slot);
// LOG_D(HW, "Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, info->sl, slot, ru_id, subframe, last_slot);
atomic_fetch_add(&xran_queue_length, 1);
pushNotifiedFIFO(&oran_sync_fifo, req);
#else
@@ -225,6 +226,23 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot)
struct xranlib_decompress_response bfp_decom_rsp = {};
int payload_len = (3 * ru_conf->iqWidth_PRACH + 1) * 12; // 12 = closest number of PRBs to 139 REs
// Check compressed data first to see if we have actual PRACH transmission
int non_zero_compressed = 0;
for (int i = 0; i < payload_len; i++) {
if (((uint8_t*)src)[i] != 0) non_zero_compressed++;
}
static int prach_decomp_log_count = 0;
// Log when we have non-zero compressed data OR for first 10 occurrences
bool should_log = (prach_decomp_log_count < 10 || non_zero_compressed > 0);
if (should_log) {
LOG_I(HW, "[gNB PRACH RX] ENTER: frame=%d, slot=%d, sym=%d, aa=%d, iqWidth=%d, payload_len=%d, non_zero_compressed=%d/%d, g_kbar=%d\n",
frame, slot, sym_idx, aa, ru_conf->iqWidth_PRACH, payload_len, non_zero_compressed, payload_len, g_kbar);
LOG_I(HW, "[gNB PRACH RX] Compressed input: [0]=0x%02x [1]=0x%02x [27]=0x%02x [28]=0x%02x [29]=0x%02x [55]=0x%02x [56]=0x%02x\n",
((uint8_t*)src)[0], ((uint8_t*)src)[1], ((uint8_t*)src)[27], ((uint8_t*)src)[28],
((uint8_t*)src)[29], ((uint8_t*)src)[55], ((uint8_t*)src)[56]);
}
bfp_decom_req.data_in = (int8_t *)src;
bfp_decom_req.numRBs = 12; // closest number of PRBs to 139 REs
bfp_decom_req.len = payload_len;
@@ -233,7 +251,24 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot)
bfp_decom_rsp.data_out = (int16_t *)local_dst;
bfp_decom_rsp.len = 0;
xranlib_decompress_avx512(&bfp_decom_req, &bfp_decom_rsp);
if (should_log) {
LOG_I(HW, "[gNB PRACH RX] Decompression returned, rsp.len=%d\n", bfp_decom_rsp.len);
LOG_I(HW, "[gNB PRACH RX] local_dst: [0]=%d [4]=%d [100]=%d [281]=%d\n",
local_dst[0], local_dst[4], local_dst[100], local_dst[281]);
int max_val = 0, min_val = 0;
int non_zero_count = 0;
for (int i = 0; i < 12 * 2 * N_SC_PER_PRB; i++) {
if (local_dst[i] > max_val) max_val = local_dst[i];
if (local_dst[i] < min_val) min_val = local_dst[i];
if (local_dst[i] != 0) non_zero_count++;
}
LOG_I(HW, "[gNB PRACH RX] Decompressed range: min=%d, max=%d, non_zero=%d/288\n", min_val, max_val, non_zero_count);
if (non_zero_compressed > 0 || prach_decomp_log_count < 10)
prach_decomp_log_count++;
}
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_decompression(ru_conf->iqWidth_PRACH, 12, (int8_t *)src, (int16_t *)local_dst);
#else
@@ -246,7 +281,78 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot)
else
for (idx = 0; idx < (139 * 2); idx++)
dst[idx] += (local_dst[idx + g_kbar]);
// Log extracted PRACH data for first and last symbol
if (prach_decomp_log_count <= 5) {
if (sym_idx == prach_start_sym) {
LOG_I(HW, "[gNB PRACH RX] First symbol dst after g_kbar: [0]=%d [1]=%d [100]=%d [138]=%d\n",
dst[0], dst[1], dst[100], dst[138]);
} else if (sym_idx == prach_start_sym + 11) {
LOG_I(HW, "[gNB PRACH RX] Final accumulated dst (all 12 syms): [0]=%d [1]=%d [100]=%d [138]=%d\n",
dst[0], dst[1], dst[100], dst[138]);
}
}
} // COMPMETHOD_BLKFLOAT
else if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_BLKSCALE) {
/* 12 PRBs cover 139 REs */
int16_t local_dst[12 * 2 * N_SC_PER_PRB] __attribute__((aligned(64)));
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request bs_decom_req = {};
struct xranlib_decompress_response bs_decom_rsp = {};
int payload_len = (3 * ru_conf->iqWidth_PRACH + 1) * 12; // 12 = closest number of PRBs to 139 REs
bs_decom_req.data_in = (int8_t *)src;
bs_decom_req.numRBs = 12; // closest number of PRBs to 139 REs
bs_decom_req.len = payload_len;
bs_decom_req.compMethod = XRAN_COMPMETHOD_BLKSCALE;
bs_decom_req.iqWidth = ru_conf->iqWidth_PRACH;
bs_decom_rsp.data_out = (int16_t *)local_dst;
bs_decom_rsp.len = 0;
xranlib_decompress_blkscale_avx512(&bs_decom_req, &bs_decom_rsp);
#else
AssertFatal(1 == 0, "BFP decompression not supported on this architecture");
#endif
// note: this is hardwired for 139 point PRACH sequence, kbar=2
if (sym_idx == prach_start_sym) //
for (idx = 0; idx < (139 * 2); idx++)
dst[idx] = local_dst[idx + g_kbar];
else
for (idx = 0; idx < (139 * 2); idx++)
dst[idx] += (local_dst[idx + g_kbar]);
} //COMPMETHOD_BLKSCALE
else if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_ULAW) {
/* 12 PRBs cover 139 REs */
int16_t local_dst[12 * 2 * N_SC_PER_PRB] __attribute__((aligned(64)));
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request ulaw_decom_req = {};
struct xranlib_decompress_response ulaw_decom_rsp = {};
int payload_len = (3 * ru_conf->iqWidth_PRACH + 1) * 12; // 12 = closest number of PRBs to 139 REs
ulaw_decom_req.data_in = (int8_t *)src;
ulaw_decom_req.numRBs = 12; // closest number of PRBs to 139 REs
ulaw_decom_req.len = payload_len;
ulaw_decom_req.compMethod = XRAN_COMPMETHOD_ULAW;
ulaw_decom_req.iqWidth = ru_conf->iqWidth_PRACH;
ulaw_decom_rsp.data_out = (int16_t *)local_dst;
ulaw_decom_rsp.len = 0;
xranlib_decompress_ulaw_avx512(&ulaw_decom_req, &ulaw_decom_rsp);
#else
AssertFatal(1 == 0, "BFP decompression not supported on this architecture");
#endif
// note: this is hardwired for 139 point PRACH sequence, kbar=2
if (sym_idx == prach_start_sym) //
for (idx = 0; idx < (139 * 2); idx++)
dst[idx] = local_dst[idx + g_kbar];
else
for (idx = 0; idx < (139 * 2); idx++)
dst[idx] += (local_dst[idx + g_kbar]);
} //COMPMETHOD_ULAW
} // aa
} // symb_indx
} // is_prach_slot
@@ -409,7 +515,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
int start_totalRB = pRbMap->prbMap[0].nRBStart;
int32_t local_dst[num_totalRB * N_SC_PER_PRB] __attribute__((aligned(64)));
LOG_D(HW, "[%d.%d] pRbMap->nPrbElm %d\n", *frame, *slot, pRbMap->nPrbElm);
// LOG_D(HW, "[%d.%d] pRbMap->nPrbElm %d\n", *frame, *slot, pRbMap->nPrbElm);
for (uint32_t idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++) {
int numRB, startRB;
uint8_t *pData;
@@ -417,7 +523,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
struct xran_prb_elm *pRbElm = &pRbMap->prbMap[idxElm];
#if defined F_RELEASE
// UP_nRBSize & UP_nRBStart are for DL U-plane only
LOG_D(HW, "[%d.%d] idxElm[%d] startSym[%d]:numSym[%d] UP_startRB[%d]:UP_numRB[%d] sym_idx[%d] ant_id[%d] pRbElm->nRBStart[%d]:pRbElm->nRBSize[%d]\n", *frame, *slot, idxElm, pRbElm->nStartSymb, pRbElm->numSymb, pRbElm->UP_nRBStart, pRbElm->UP_nRBSize, sym_idx, ant_id, pRbElm->nRBStart, pRbElm->nRBSize);
// LOG_D(HW, "[%d.%d] idxElm[%d] startSym[%d]:numSym[%d] UP_startRB[%d]:UP_numRB[%d] sym_idx[%d] ant_id[%d] pRbElm->nRBStart[%d]:pRbElm->nRBSize[%d]\n", *frame, *slot, idxElm, pRbElm->nStartSymb, pRbElm->numSymb, pRbElm->UP_nRBStart, pRbElm->UP_nRBSize, sym_idx, ant_id, pRbElm->nRBStart, pRbElm->nRBSize);
for (int idxDesc = 0; idxDesc < XRAN_MAX_FRAGMENT; idxDesc++) {
p_sec_desc = &pRbElm->sec_desc[sym_idx][idxDesc];
if (p_sec_desc == NULL)
@@ -429,7 +535,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
numRB = p_sec_desc->num_prbu;
startRB = p_sec_desc->start_prbu;
// num_prbu & start_prbu are for UL U-plane only
LOG_D(HW, "p_sec_desc[%d] startRB[%d]:numRB[%d]\n", idxDesc, startRB, numRB);
// LOG_D(HW, "p_sec_desc[%d] startRB[%d]:numRB[%d]\n", idxDesc, startRB, numRB);
#endif
ptr = pData;
pos = (int32_t *)(start_ptr + (4 * sym_idx * fftsize));
@@ -447,6 +553,12 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
int16_t payload_len = (3 * pRbElm->iqWidth + 1) * numRB;
static int pusch_decomp_log_count = 0;
if (pusch_decomp_log_count < 5) {
LOG_I(HW, "[PUSCH BFP] ENTER: frame=%d, slot=%d, sym=%d, ant=%d, startRB=%d, numRB=%d, iqWidth=%d, payload_len=%d\n",
*frame, *slot, sym_idx, ant_id, startRB, numRB, pRbElm->iqWidth, payload_len);
}
bfp_decom_req.data_in = (int8_t *)src;
bfp_decom_req.numRBs = numRB;
bfp_decom_req.len = payload_len;
@@ -457,13 +569,63 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
bfp_decom_rsp.len = 0;
xranlib_decompress_avx512(&bfp_decom_req, &bfp_decom_rsp);
if (pusch_decomp_log_count < 5) {
LOG_I(HW, "[PUSCH BFP] Decompression returned, rsp.len=%d\n", bfp_decom_rsp.len);
pusch_decomp_log_count++;
}
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_decompression(pRbElm->iqWidth, numRB, (int8_t *)src, (int16_t *)local_dst);
#else
AssertFatal(1 == 0, "BFP compression not supported on this architecture");
#endif
outcnt++;
} else {
} else if (pRbElm->compMethod == XRAN_COMPMETHOD_BLKSCALE) {
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request bs_decom_req = {};
struct xranlib_decompress_response bs_decom_rsp = {};
int16_t payload_len = (3 * pRbElm->iqWidth + 1) * pRbElm->nRBSize;
bs_decom_req.data_in = (int8_t *)src;
bs_decom_req.numRBs = numRB;
bs_decom_req.len = payload_len;
bs_decom_req.compMethod = pRbElm->compMethod;
bs_decom_req.iqWidth = pRbElm->iqWidth;
bs_decom_rsp.data_out = (int16_t *)(local_dst + startRB * N_SC_PER_PRB);
bs_decom_rsp.len = 0;
xranlib_decompress_blkscale_avx512(&bs_decom_req, &bs_decom_rsp);
#else
AssertFatal(1 == 0, "BLKSCALE compression not supported on this architecture");
#endif
outcnt++;
} else if (pRbElm->compMethod == XRAN_COMPMETHOD_ULAW) {
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request ulaw_decom_req = {};
struct xranlib_decompress_response ulaw_decom_rsp = {};
int16_t payload_len = (3 * pRbElm->iqWidth + 1) * pRbElm->nRBSize;
ulaw_decom_req.data_in = (int8_t *)src;
ulaw_decom_req.numRBs = numRB;
ulaw_decom_req.len = payload_len;
ulaw_decom_req.compMethod = pRbElm->compMethod;
ulaw_decom_req.iqWidth = pRbElm->iqWidth;
ulaw_decom_rsp.data_out = (int16_t *)(local_dst + startRB * N_SC_PER_PRB);
ulaw_decom_rsp.len = 0;
xranlib_decompress_ulaw_avx512(&ulaw_decom_req, &ulaw_decom_rsp);
#else
AssertFatal(1 == 0, "ULAW compression not supported on this architecture");
#endif
outcnt++;
}
else {
printf("pRbElm->compMethod == %d is not supported\n", pRbElm->compMethod);
exit(-1);
}
@@ -489,7 +651,19 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
} // sym_ind
} // ant_ind
} // vv_inf
static FILE *stats_file = NULL;
static int stats_file_initialized = 0;
if ((*frame & 0x7f) == 0 && *slot == 0 && xran_get_common_counters(gxran_handle, &x_counters[0]) == XRAN_STATUS_SUCCESS) {
// Initialize stats file on first run
if (!stats_file_initialized) {
stats_file = fopen("gnb_compression_stats.txt", "w");
if (stats_file != NULL) {
fprintf(stats_file, "# gNB Compression Statistics Log\n");
fprintf(stats_file, "# Format: [timestamp] o_xu_id rx_kbps tx_kbps\n");
fflush(stats_file);
}
stats_file_initialized = 1;
}
for (int o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
LOG_I(HW,
"[%s%d][rx %7ld pps %7ld kbps %7ld][tx %7ld pps %7ld kbps %7ld][Total Msgs_Rcvd %ld]\n",
@@ -502,6 +676,16 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
x_counters[o_xu_id].tx_counter - old_tx_counter[o_xu_id],
x_counters[o_xu_id].tx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].Total_msgs_rcvd);
// Log to file
if (stats_file != NULL) {
time_t now = time(NULL);
fprintf(stats_file, "[%ld] o_xu_id=%d rx_kbps=%ld tx_kbps=%ld\n",
now,
o_xu_id,
x_counters[o_xu_id].rx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].tx_bytes_per_sec * 8 / 1000L);
fflush(stats_file); // Ensure data is written immediately
}
for (int rxant = 0; rxant < ru->nb_rx / fh_init->xran_ports; rxant++)
LOG_I(HW,
"[%s%d][pusch%d %7ld prach%d %7ld]\n",
@@ -534,6 +718,7 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
const struct xran_fh_init *fh_init = get_xran_fh_init();
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
int nPRBs = fh_cfg->nDLRBs;
int fftsize = 1 << fh_cfg->nDLFftSize;
int nb_tx_per_ru = ru->nb_tx / fh_init->xran_ports;
int nb_rx_per_ru = ru->nb_rx / fh_init->xran_ports;
@@ -559,7 +744,7 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
uint8_t *pPrbMapData = bufs->dstcp[ant_id % nb_rx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData;
LOG_D(HW, "pPrbMap->nPrbElm %d\n", pPrbMap->nPrbElm);
// LOG_D(HW, "pPrbMap->nPrbElm %d\n", pPrbMap->nPrbElm);
for (uint32_t idxElm = 0; idxElm < pPrbMap->nPrbElm; idxElm++) {
struct xran_prb_elm *pRbElm = &pPrbMap->prbMap[idxElm];
int numRB, startRB;
@@ -568,7 +753,7 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
startRB = pRbElm->UP_nRBStart;
struct xran_section_desc *p_sec_desc = &pRbElm->sec_desc[sym_idx][0];
#endif
LOG_D(HW, "pPrbMap[%d] : PRBstart %d nPRBs %d\n", idxElm, startRB, numRB);
// LOG_D(HW, "pPrbMap[%d] : PRBstart %d nPRBs %d\n", idxElm, startRB, numRB);
// For Liteon FR2 with RunSlotPrbMapBySymbolEnable xran_prb_map will have xran_prb_elm prbMap[14], each idxElm matches to sym_idx.
if (fh_cfg->RunSlotPrbMapBySymbolEnable) {
if (sym_idx >= pRbElm->nStartSymb && sym_idx < pRbElm->nStartSymb + pRbElm->numSymb) {
@@ -604,8 +789,12 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
const struct xran_frame_config *frame_conf = &get_xran_fh_config(ant_id / nb_tx_per_ru)->frame_conf;
// skip processing this slot is TX (no TX in this slot)
if (!is_tdd_dl_guard_slot(frame_conf, slot)) {
if (slot == 0 && frame % 2 == 0)
LOG_D(HW, "[xran_tx] Frame %d Slot %d ant_id %d: SKIPPED (not DL/guard slot)\n", frame, slot, ant_id);
continue;
}
// if (slot == 0 && frame % 2 == 0 && ant_id == 0)
// LOG_D(HW, "[xran_tx] Frame %d Slot %d ant_id %d: Processing DL symbols\n", frame, slot, ant_id);
// For Liteon FR2 with RunSlotPrbMapBySymbolEnable. Set nPrbElm if beam_id = -1 for all downlink symbols
if (fh_cfg->RunSlotPrbMapBySymbolEnable) {
@@ -646,6 +835,8 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData;
ptr = pData;
pos = &ru->txdataF_BF[ant_id][sym_idx * fftsize];
// if (slot == 0 && frame % 2 == 0 && ant_id == 0 && sym_idx >= 2 && sym_idx <= 5)
// LOG_D(HW, "[xran_tx] Frame %d Slot %d ant_id %d sym %d: ptr=%p pos=%p\n", frame, slot, ant_id, sym_idx, ptr, pos);
uint8_t *u8dptr;
// even when the fragmentation occurs, nRBSize & nRBStart carry the same values in each prbMap
@@ -709,6 +900,15 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
}
}
// Log PRB map element compression settings before packet header construction
static int prb_elm_log_count = 0;
if (prb_elm_log_count < 5 && sym_idx >= 2 && sym_idx <= 5 && slot == 0) {
LOG_I(HW, "[TX PRB ELM] frame=%d, slot=%d, sym=%d, ant=%d, idxElm=%d: compMethod=%d, iqWidth=%d, nRBStart=%d, nRBSize=%d\n",
frame, slot, sym_idx, ant_id, idxElm, p_prbMapElm->compMethod, p_prbMapElm->iqWidth,
p_prbMapElm->nRBStart, p_prbMapElm->nRBSize);
prb_elm_log_count++;
}
dst = xran_add_hdr_offset(dst, p_prbMapElm->compMethod);
uint16_t *dst16 = (uint16_t *)dst;
@@ -718,6 +918,7 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_NONE) {
payload_len = numRB * N_SC_PER_PRB * 4L;
/* convert to Network order */
// NOTE: ggc 11 knows how to generate AVX2 for this!
for (idx = 0; idx < (numRB * N_SC_PER_PRB) * 2; idx++)
@@ -725,22 +926,13 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
} else if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_BLKFLOAT) {
payload_len = (3 * p_prbMapElm->iqWidth + 1) * numRB;
/* Although arm intrinsics natively handle unaligned memory
access, we use a 64 byte aligned input here for maximum
performance. So the src_compr buffer is used for both x86 and arm.
*/
uint32_t src_compr[num_totalRB * N_SC_PER_PRB] __attribute__((aligned(64)));
/* Copy from txdataF with current symbol's PRB start (nRBStart) +
current section's PRB start (UP_nPRBStart) */
memcpy(src_compr, pos_start, (numRB * N_SC_PER_PRB) * sizeof(*pos_start));
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_compress_request bfp_com_req = {};
struct xranlib_compress_response bfp_com_rsp = {};
bfp_com_req.data_in = (int16_t *)src_compr;
// For RAU configuration (no fft_shift), txdataF_BF is already in [neg|pos] format
// which is what the BFP compression library expects. Use pos_start directly.
bfp_com_req.data_in = (int16_t *)pos_start;
bfp_com_req.numRBs = numRB;
bfp_com_req.len = payload_len;
bfp_com_req.compMethod = p_prbMapElm->compMethod;
@@ -751,11 +943,62 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
xranlib_compress_avx512(&bfp_com_req, &bfp_com_rsp);
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_compression(p_prbMapElm->iqWidth, numRB, (int16_t *)src_compr, (int8_t *)dst);
armral_bfp_compression(p_prbMapElm->iqWidth, numRB, (int16_t *)pos_start, (int8_t *)dst);
#else
AssertFatal(1 == 0, "BFP compression not supported on this architecture");
#endif
} else {
} else if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_BLKSCALE) {
payload_len = (3 * p_prbMapElm->iqWidth + 1) * numRB;
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_compress_request req = {};
struct xranlib_compress_response rsp = {};
// For RAU configuration (no fft_shift), txdataF_BF is already in [neg|pos] format
req.data_in = (int16_t *)pos_start;
req.numRBs = numRB;
req.len = payload_len;
req.compMethod = XRAN_COMPMETHOD_BLKSCALE;
req.iqWidth = p_prbMapElm->iqWidth;
rsp.data_out = (int8_t *)dst;
rsp.len = 0;
xranlib_compress_blkscale_avx512(&req, &rsp);
#else
AssertFatal(0, "BLKSCALE compression not supported on this architecture");
#endif
} else if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_ULAW) {
payload_len = (3 * p_prbMapElm->iqWidth + 1) *numRB;
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_compress_request req = {};
struct xranlib_compress_response rsp = {};
// For RAU configuration (no fft_shift), txdataF_BF is already in [neg|pos] format
req.data_in = (int16_t *)pos_start;
req.numRBs = numRB;
req.len = payload_len;
req.compMethod = XRAN_COMPMETHOD_ULAW;
req.iqWidth = p_prbMapElm->iqWidth;
rsp.data_out = (int8_t *)dst;
rsp.len = 0;
xranlib_compress_ulaw_avx512(&req, &rsp);
#else
AssertFatal(0, "ULAW compression not supported on this architecture");
#endif
}
else {
printf("p_prbMapElm->compMethod == %d is not supported\n", p_prbMapElm->compMethod);
exit(-1);
}

1293
radio/fhi_72/oaioran_ru.c Normal file

File diff suppressed because it is too large Load Diff

38
radio/fhi_72/oaioran_ru.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* 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
*/
#ifndef __OAIORAN_RU_H__
#define __OAIORAN_RU_H__
#include <stdint.h>
#include "common/utils/threadPool/notified_fifo.h"
// Initialize o-ru packet processor
// handle - xran handle
// callbacks_per_slot - number of callbacks per slot. using any value other than 14
// causes additional delay as the symbols will be delivered in batches
void init_oru_packet_processor(void* handle, int callbacks_per_slot);
// Read samples DL IQ samples for frame slot symbol for all antennas
int xran_oru_tx_read_slot(uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol, int *num_symbols, struct timespec *ts);
void xran_oru_send_prach(uint32_t *prachF, int aarx, int frame, int slot, int symbol);
void xran_oru_send_pusch(uint32_t *puschF, int aarx, int frame, int slot, int symbol);
#endif

View File

@@ -20,6 +20,7 @@
*/
#include "oran-config.h"
#include "common/config/config_userapi.h"
#include "oran-params.h"
#include "common/utils/assertions.h"
#include "common_lib.h"
@@ -852,7 +853,7 @@ static bool set_fh_ru_config(void *mplane_api, const paramdef_t *rup, uint16_t f
{
ru_config->xranTech = XRAN_RAN_5GNR; // 5GNR or LTE
ru_config->xranCat = xran_cat; // mode: Catergory A or Category B
ru_config->xranCompHdrType = XRAN_COMP_HDR_TYPE_STATIC; // dynamic or static udCompHdr handling
ru_config->xranCompHdrType = XRAN_COMP_HDR_TYPE_DYNAMIC; // dynamic or static udCompHdr handling
#ifdef OAI_MPLANE
xran_mplane_t *xran_mplane = (xran_mplane_t *)mplane_api;
ru_config->iqWidth = xran_mplane->iq_width;
@@ -863,9 +864,50 @@ static bool set_fh_ru_config(void *mplane_api, const paramdef_t *rup, uint16_t f
ru_config->iqWidth_PRACH = *gpd(rup, nru, ORAN_RU_CONFIG_IQWIDTH_PRACH)->uptr; // IQ bit width for PRACH
AssertFatal(ru_config->iqWidth_PRACH <= 16, "IQ Width for PRACH cannot be > 16!\n");
#endif
ru_config->compMeth = ru_config->iqWidth < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method
ru_config->compMeth_PRACH = ru_config->iqWidth_PRACH < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method for PRACH
//ru_config->compMeth = ru_config->iqWidth < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method
//ru_config->compMeth_PRACH = ru_config->iqWidth_PRACH < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method for PRACH
ru_config->compMeth = *gpd(rup, nru, ORAN_RU_CONFIG_COMPMETH)->uptr;
if (ru_config->iqWidth == 16)
ru_config->compMeth = XRAN_COMPMETHOD_NONE;
else{
switch(ru_config->compMeth) {
case 0:
ru_config->compMeth = XRAN_COMPMETHOD_NONE;
break;
case 1:
ru_config->compMeth = XRAN_COMPMETHOD_BLKFLOAT;
break;
case 2:
ru_config->compMeth = XRAN_COMPMETHOD_BLKSCALE;
break;
case 3:
ru_config->compMeth = XRAN_COMPMETHOD_ULAW;
break;
}
}
ru_config->compMeth_PRACH = *gpd(rup, nru, ORAN_RU_CONFIG_COMPMETH_PRACH)->uptr;
if (ru_config->iqWidth_PRACH == 16)
ru_config->compMeth_PRACH = XRAN_COMPMETHOD_NONE;
else{
switch(ru_config->compMeth_PRACH) {
case 0:
ru_config->compMeth_PRACH = XRAN_COMPMETHOD_NONE;
break;
case 1:
ru_config->compMeth_PRACH = XRAN_COMPMETHOD_BLKFLOAT;
break;
case 2:
ru_config->compMeth_PRACH = XRAN_COMPMETHOD_BLKSCALE;
break;
case 3:
ru_config->compMeth_PRACH = XRAN_COMPMETHOD_ULAW;
break;
}
}
AssertFatal(fftSize > 0, "FFT size cannot be 0\n");
ru_config->fftSize = fftSize; // FFT Size
ru_config->byteOrder = XRAN_NE_BE_BYTE_ORDER; // order of bytes in int16_t in buffer; big or little endian
@@ -890,7 +932,15 @@ static bool set_maxmin_pd(const paramdef_t *pd, int num, const char *name, uint1
return true;
}
static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_category xran_cat, const openair0_config_t *oai0, struct xran_fh_config *fh_config)
static bool set_fh_config(void *mplane_api,
int ru_idx,
int num_rus,
enum xran_category xran_cat,
const openair0_config_t *oai0,
struct xran_fh_config *fh_config,
bool is_du,
int gps_alpha,
int gps_beta)
{
AssertFatal(num_rus == 1 || num_rus == 2, "only support 1 or 2 RUs as of now\n");
AssertFatal(ru_idx < num_rus, "illegal ru_idx %d: must be < %d\n", ru_idx, num_rus);
@@ -950,15 +1000,47 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c
fh_config->ttiCb = NULL; // check tti_to_phy_cb(), tx_cp_dl_cb() and tx_cp_ul_cb => first_call
fh_config->ttiCbParam = NULL; // check tti_to_phy_cb(), tx_cp_dl_cb() and tx_cp_ul_cb => first_call
/* DU delay profile */
if (!set_maxmin_pd(fhp, nfh, ORAN_FH_CONFIG_T1A_CP_DL, &fh_config->T1a_min_cp_dl, &fh_config->T1a_max_cp_dl)) // E - min not used in xran, max yes; F - both min and max are used in xran
return false;
if (!set_maxmin_pd(fhp, nfh, ORAN_FH_CONFIG_T1A_CP_UL, &fh_config->T1a_min_cp_ul, &fh_config->T1a_max_cp_ul)) // both E and F - min not used in xran, max yes
return false;
if (!set_maxmin_pd(fhp, nfh, ORAN_FH_CONFIG_T1A_UP, &fh_config->T1a_min_up, &fh_config->T1a_max_up)) // both E and F - min not used in xran, max yes
return false;
if (!set_maxmin_pd(fhp, nfh, ORAN_FH_CONFIG_TA4, &fh_config->Ta4_min, &fh_config->Ta4_max)) // both E and F - min not used in xran, max yes
return false;
if (is_du) {
/* DU delay profile */
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_T1A_CP_DL,
&fh_config->T1a_min_cp_dl,
&fh_config->T1a_max_cp_dl)) // E - min not used in xran, max yes; F - both min and max are used in xran
return false;
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_T1A_CP_UL,
&fh_config->T1a_min_cp_ul,
&fh_config->T1a_max_cp_ul)) // both E and F - min not used in xran, max yes
return false;
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_T1A_UP,
&fh_config->T1a_min_up,
&fh_config->T1a_max_up)) // both E and F - min not used in xran, max yes
return false;
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_TA4,
&fh_config->Ta4_min,
&fh_config->Ta4_max)) // both E and F - min not used in xran, max yes
return false;
} else {
/* RU delay profile */
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_TA3,
&fh_config->Ta3_min,
&fh_config->Ta3_max)) // both E and F - min not used in xran, max yes
return false;
if (!set_maxmin_pd(fhp,
nfh,
ORAN_FH_CONFIG_T2A,
&fh_config->T2a_min_up,
&fh_config->T2a_max_up)) // both E and F - min not used in xran, max yes
return false;
}
fh_config->enableCP = 1; // enable C-plane
fh_config->prachEnable = 1; // enable PRACH
@@ -979,8 +1061,8 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c
fh_config->debugStop = 0; // enable auto stop; only used if id = O_RU
fh_config->debugStopCount = 0; // enable auto stop after number of Tx packets; not used in xran
fh_config->DynamicSectionEna = 0; // enable dynamic C-Plane section allocation
fh_config->GPS_Alpha = 0; // refers to alpha as defined in section 9.7.2 of ORAN spec. this value should be alpha*(1/1.2288ns), range 0 - 1e7 (ns); offset_nsec = (pConf->GPS_Beta - offset_sec * 100) * 1e7 + pConf->GPS_Alpha
fh_config->GPS_Beta = 0; // beta value as defined in section 9.7.2 of ORAN spec. range -32767 ~ +32767; offset_sec = pConf->GPS_Beta / 100
fh_config->GPS_Alpha = gps_alpha; // refers to alpha as defined in section 9.7.2 of ORAN spec. this value should be alpha*(1/1.2288ns), range 0 - 1e7 (ns); offset_nsec = (pConf->GPS_Beta - offset_sec * 100) * 1e7 + pConf->GPS_Alpha
fh_config->GPS_Beta = gps_beta; // beta value as defined in section 9.7.2 of ORAN spec. range -32767 ~ +32767; offset_sec = pConf->GPS_Beta / 100
if (!set_fh_prach_config(mplane_api, oai0, fh_config->neAxc, prachp, nprach, &fh_config->prach_conf, liteon_prach_eAxC_offset))
return false;
@@ -1034,18 +1116,30 @@ bool get_xran_config(void *mplane_api, const struct openair0_config *openair0_cf
return false;
}
// Read GPS timing parameters from global config
paramdef_t fhip[] = ORAN_GLOBALPARAMS_DESC;
int nump = sizeofArray(fhip);
int ret = config_get(config_get_if(), fhip, nump, CONFIG_STRING_ORAN);
if (ret <= 0) {
printf("could not read global ORAN config for GPS parameters\n");
return false;
}
int gps_alpha = *gpd(fhip, nump, ORAN_CONFIG_GPS_ALPHA)->iptr;
int gps_beta = *gpd(fhip, nump, ORAN_CONFIG_GPS_BETA)->iptr;
#ifdef OAI_MPLANE
ru_session_list_t *ru_session_list = (ru_session_list_t *)mplane_api;
for (int32_t o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
xran_mplane_t *xran_mplane = &ru_session_list->ru_session[o_xu_id].xran_mplane;
if (!set_fh_config(xran_mplane, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id])) {
if (!set_fh_config(xran_mplane, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id], true, gps_alpha, gps_beta)) {
MP_LOG_I("could not read FHI 7.2/RU-specific config\n");
return false;
}
}
#else
bool is_du = fh_init->io_cfg.id == 0;
for (int32_t o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
if (!set_fh_config(NULL, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id])) {
if (!set_fh_config(NULL, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id], is_du, gps_alpha, gps_beta)) {
printf("could not read FHI 7.2/RU-specific config\n");
return false;
}

View File

@@ -19,6 +19,9 @@
* contact@openairinterface.org
*/
#define _GNU_SOURCE
#include <sched.h>
#include "oaioran_ru.h"
#include "xran_fh_o_du.h"
#include "xran_pkt.h"
#include "xran_pkt_up.h"
@@ -242,6 +245,18 @@ static void oran_allocate_cplane_buffers(void *instHandle,
} else {
xran_init_PrbMap_from_cfg(src, ptr, mtu);
}
// Log PRB map after xRAN initialization to verify compression settings
struct xran_prb_map *initialized_map = (struct xran_prb_map *)ptr;
if (a < 2 && j < 2) { // Log only first couple of antennas/slots
LOG_I(HW, "[PRB MAP INIT] ant=%d, slot=%d: After xran_init, dir=%d, nPrbElm=%d\n",
a, j, initialized_map->dir, initialized_map->nPrbElm);
if (initialized_map->nPrbElm > 0) {
LOG_I(HW, "[PRB MAP INIT] prbMap[0]: compMethod=%d, iqWidth=%d, nRBStart=%d, nRBSize=%d\n",
initialized_map->prbMap[0].compMethod, initialized_map->prbMap[0].iqWidth,
initialized_map->prbMap[0].nRBStart, initialized_map->prbMap[0].nRBSize);
}
}
#endif
}
}
@@ -398,12 +413,29 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi
int32_t xret = 0;
print_fh_init(xran_fh_init);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
int ret = pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
AssertFatal(ret != -1, "sched_getaffinity() failed errno %d (%s)", errno, strerror(errno));
xret = xran_init(0, NULL, xran_fh_init, NULL, &gxran_handle);
if (xret != XRAN_STATUS_SUCCESS) {
printf("xran_init failed %d\n", xret);
exit(-1);
}
cpu_set_t cpuset_after;
CPU_ZERO(&cpuset_after);
ret = pthread_getaffinity_np(pthread_self(), sizeof(cpuset_after), &cpuset_after);
AssertFatal(ret != -1, "sched_getaffinity() failed errno %d (%s)", errno, strerror(errno));
if (!CPU_EQUAL(&cpuset_after, &cpuset))
{
LOG_W(HW, "XRAN modifed affinity. Correcting affinity of caller\n");
ret = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
AssertFatal(ret == 0, "Error in pthread_setaffinity_np(): ret: %d (%s)", ret, strerror(ret));
}
bool is_du = xran_fh_init->io_cfg.id == 0;
/** process all the O-RU|O-DU for use case */
for (int32_t o_xu_id = 0; o_xu_id < xran_fh_init->xran_ports; o_xu_id++) {
print_fh_config(&xran_fh_config[o_xu_id]);
@@ -441,6 +473,12 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi
memcpy(&g_fh_init, xran_fh_init, sizeof(*xran_fh_init));
memcpy(&g_fh_config, xran_fh_config, sizeof(*xran_fh_config) * xran_fh_init->xran_ports);
if (!is_du) {
// Use half-slot parallelization
const int callback_per_slot = 2;
init_oru_packet_processor(gxran_handle, callback_per_slot);
}
return (void *)gxran_handle;
}

View File

@@ -45,6 +45,8 @@
#define ORAN_CONFIG_DPDK_IOVA_MODE "dpdk_iova_mode"
#define ORAN_CONFIG_ECPRI_OWDM "owdm_enable"
#define ORAN_CONFIG_APP_ID "app_id"
#define ORAN_CONFIG_GPS_ALPHA "GPS_Alpha"
#define ORAN_CONFIG_GPS_BETA "GPS_Beta"
#define APP_ID_O_DU "DU"
#define APP_ID_O_RU "RU"
@@ -74,7 +76,9 @@
{ORAN_CONFIG_DPDK_MEM_SIZE, "DPDK huge page pre-allocation in MiB\n", 0, .uptr=NULL, .defuintval=8192, TYPE_UINT, 0}, \
{ORAN_CONFIG_DPDK_IOVA_MODE, "DPDK IOVA mode\n", 0, .strptr=NULL, .defstrval="PA", TYPE_STRING, 0}, \
{ORAN_CONFIG_ECPRI_OWDM, "eCPRI One-Way Delay Measurements\n", PARAMFLAG_BOOL, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
{ORAN_CONFIG_APP_ID, "Either DU or RU", 0, .strptr=NULL, .defstrval=APP_ID_O_DU, TYPE_STRING, 0} \
{ORAN_CONFIG_APP_ID, "Either DU or RU", 0, .strptr=NULL, .defstrval=APP_ID_O_DU, TYPE_STRING, 0}, \
{ORAN_CONFIG_GPS_ALPHA, "GPS Alpha timing parameter\n", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{ORAN_CONFIG_GPS_BETA, "GPS Beta timing parameter\n", 0, .iptr=NULL, .defintval=-9000, TYPE_INT, 0} \
}
// clang-format off
@@ -99,6 +103,8 @@
{ .s5 = { NULL } }, \
{ .s3a = { config_checkstr_assign_integer, \
{APP_ID_O_DU, APP_ID_O_RU}, {XRAN_APP_ID_O_DU, XRAN_APP_ID_O_RU}, 2} }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
}
@@ -112,6 +118,8 @@
#define ORAN_FH_CONFIG_T1A_CP_UL "T1a_cp_ul"
#define ORAN_FH_CONFIG_T1A_UP "T1a_up"
#define ORAN_FH_CONFIG_TA4 "Ta4"
#define ORAN_FH_CONFIG_TA3 "Ta3_up"
#define ORAN_FH_CONFIG_T2A "T2a_up"
#define ORAN_FH_HLP_CPLT " parameter of RU in list form (Min&Max, length 2!)\n"
@@ -119,10 +127,12 @@
#define ORAN_FH_DESC { \
{ORAN_CONFIG_RunSlotPrbMapBySymbol, "RunSlotPrbMapBySymbol\n", PARAMFLAG_BOOL, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
{ORAN_CONFIG_LiteOnIgnoreUPSectionId, "Liteon Ignore Section Id\n", PARAMFLAG_BOOL, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
{ORAN_FH_CONFIG_T1A_CP_DL, "T1a_cp_dl" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_CP_UL, "T1a_cp_ul" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_UP, "T1a_up" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_TA4, "Ta4" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_CP_DL, "T1a_cp_dl" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_CP_UL, "T1a_cp_ul" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_UP, "T1a_up" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_TA4, "Ta4" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_TA3, "Ta3" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T2A, "T2a" ORAN_FH_HLP_CPLT, 0, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
}
// clang-format on
@@ -130,11 +140,15 @@
#define ORAN_RU_CONFIG_IQWIDTH "iq_width" // not needed if M-plane used
#define ORAN_RU_CONFIG_IQWIDTH_PRACH "iq_width_prach" // not needed if M-plane used
#define ORAN_RU_CONFIG_COMPMETH "compMeth" // not needed if M-plane used
#define ORAN_RU_CONFIG_COMPMETH_PRACH "compMeth_prach" // not needed if M-plane used
// clang-format off
#define ORAN_RU_DESC {\
{ORAN_RU_CONFIG_IQWIDTH, "sample IQ width (16=uncompressed)\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=16, TYPE_UINT8, 0}, \
{ORAN_RU_CONFIG_IQWIDTH_PRACH, "PRACH sample IQ width (16=uncompressed)\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=16, TYPE_UINT8, 0}, \
{ORAN_RU_CONFIG_COMPMETH, "Enum representing compression method to use\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=0, TYPE_UINT8, 0}, \
{ORAN_RU_CONFIG_COMPMETH_PRACH, "Enum representing compression method to use for PRACH\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=0, TYPE_UINT8, 0}, \
}
// clang-format on

53
radio/fhi_72/oran_debug.c Normal file
View File

@@ -0,0 +1,53 @@
/*
* 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 "oran_debug.h"
#include "openair1/PHY/TOOLS/tools_defs.h"
void dump_nonzero_symbol(c16_t *txdataF, uint32_t ofdm_symbol_size, int frame, int slot, int symbol, const char* loc)
{
float signal_energy = signal_energy_nodc(txdataF, ofdm_symbol_size);
if (signal_energy > 1) {
// Prepare a buffer to hold the formatted string for the symbol
const int num_chars_per_sample = 4 + 6 * 2;
char symbol_buf[ofdm_symbol_size * num_chars_per_sample]; // Enough for "(r,i) " per sample
int offset = 0;
bool is_zero_block = true;
for (int i = 0; i < ofdm_symbol_size; i++) {
bool is_zero = txdataF[i].r == 0 && txdataF[i].i == 0;
if (is_zero_block && !is_zero) {
offset += snprintf(symbol_buf + offset, sizeof(symbol_buf) - offset, "[sc %d]: ", i);
is_zero_block = false;
}
if (!is_zero_block && is_zero) {
is_zero_block = true;
}
if (!is_zero) {
offset += snprintf(symbol_buf + offset, sizeof(symbol_buf) - offset, "(%d,%d) ", txdataF[i].r, txdataF[i].i);
}
}
symbol_buf[offset] = '\0';
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
LOG_D(HW, "dump_nonzero_symbol: Frame.Slot.Symbol %d.%d.%d (%s) signal_energy %.3f time %ld.%09ld samples: %s\n", frame, slot, symbol, loc, 10 * log10(signal_energy), ts.tv_sec, ts.tv_nsec, symbol_buf);
}
}

View File

@@ -33,6 +33,7 @@
#include "openair1/PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "oaioran.h"
#include "oran-config.h"
#include "oaioran_ru.h"
// include the following file for VERSIONX, version of xran lib, to print it during
// startup. Only relevant for printing, if it ever makes problem, remove this
@@ -211,6 +212,11 @@ int trx_oran_ctlrecv(openair0_device_t *device, void *msg, ssize_t msg_len)
return 0;
}
void oran_fh_if4p5_north_in(uint32_t **txdataF, int nb_tx, sense_of_time_t* sense_of_time, int *num_symbols) {
int ret = xran_oru_tx_read_slot(txdataF, nb_tx, &sense_of_time->frame, &sense_of_time->slot, &sense_of_time->symbol, num_symbols, &sense_of_time->ts);
AssertFatal(ret == 0, "ORAN: Error reading slot");
}
void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
{
prach_item_t *prach_id = find_nr_prach(&ru->gNB_list[0]->prach_list, *frame, *slot, SEARCH_EXIST);
@@ -225,11 +231,9 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
RU_proc_t *proc = &ru->proc;
int f, sl;
LOG_D(HW, "Read rxdataF %p,%p\n", ru_info.rxdataF[0], ru_info.rxdataF[1]);
start_meas(&ru->rx_fhaul);
int ret = xran_fh_rx_read_slot(&ru_info, &f, &sl);
stop_meas(&ru->rx_fhaul);
LOG_D(HW, "Read %d.%d rxdataF %p,%p\n", f, sl, ru_info.rxdataF[0], ru_info.rxdataF[1]);
if (ret != 0) {
printf("ORAN: %d.%d ORAN_fh_if4p5_south_in ERROR in RX function \n", f, sl);
}
@@ -401,6 +405,9 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device_t
device->get_internal_parameter = get_internal_parameter;
device->priv = eth;
device->openair0_cfg = &openair0_cfg[0];
device->xran_api.north_in_func = oran_fh_if4p5_north_in;
device->xran_api.write_prach = xran_oru_send_prach;
device->xran_api.write_pusch = xran_oru_send_pusch;
return 0;
}

View File

@@ -439,7 +439,6 @@ static int vrtsim_connect(openair0_device_t *device)
server_publish_client_info(client_info, vrtsim_state->connection_descriptor);
vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->client_num_rx_antennas;
vrtsim_state->last_received_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel);
vrtsim_state->run_timing_thread = true;
threadCreate(&vrtsim_state->timing_thread, vrtsim_timing_job, vrtsim_state, "vrtsim_timing", -1, OAI_PRIORITY_RT_MAX);
} else {
@@ -1094,6 +1093,14 @@ static int vrtsim_set_beams2(openair0_device_t *device, int *beam_ids, int num_b
return 0;
}
openair0_timestamp_t vrtsim_get_timestamp(openair0_device_t *device, struct timespec *ts)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
uint64_t diff = (ts->tv_sec - vrtsim_state->start_ts.tv_sec) * 1000000000 + (ts->tv_nsec - vrtsim_state->start_ts.tv_nsec);
double diff_samples = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
return diff_samples > 0 ? diff_samples : 0;
}
__attribute__((__visibility__("default"))) int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg)
{
randominit();
@@ -1113,6 +1120,9 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device_t *de
device->trx_write_beams_func = vrtsim_write_beams;
device->trx_set_beams = vrtsim_set_beams;
device->trx_set_beams2 = vrtsim_set_beams2;
if (vrtsim_state->role == ROLE_SERVER) {
device->get_timestamp = vrtsim_get_timestamp;
}
device->type = RFSIMULATOR;
device->openair0_cfg = &openair0_cfg[0];

View File

@@ -0,0 +1,271 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 1;
maxMIMO_layers = 1;
pusch_AntennaPorts = 2;
do_CSIRS = 0;
do_SRS = 0;
sib1_tda = 15;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# n_TimingAdvanceOffset = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 4049.76 MHz
# selected SSB frequency = 4049.76 MHz
absoluteFrequencySSB = 669984;
dl_frequencyBand = 77;
# frequency point A = 4030.68 MHz
dl_absoluteFrequencyPointA = 668712;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 28875; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 77;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 157;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 7;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 2;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -100;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
ssb_PositionsInBurst_Bitmap = 0x1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 5;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.70.132"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
# dl_bler_target_upper = .35;
# dl_bler_target_lower = .15;
ul_bler_target_upper = .35;
ul_bler_target_lower = .15;
pusch_FailureThres = 100;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 110;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = -100;
# thread_pool_size = 8;
tx_amp_backoff_dB = 36;
L1_rx_thread_core = 11;
L1_tx_thread_core = 12;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 0;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 10;
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:05:02.2", "0000:05:02.3"); # one VF can be used as well
system_core = 7;
io_core = 8;
worker_cores = (9);
ru_addr = ("00:11:22:33:64:66", "00:11:22:33:64:67");
mtu = 9600;
fh_config = ({
T1a_cp_dl = (285, 470);
T1a_cp_ul = (285, 429);
T1a_up = (300, 450);
Ta4 = (400, 440);
ru_config = {
iq_width = 16;
iq_width_prach = 16;
};
});
};

View File

@@ -0,0 +1,70 @@
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
ORUs = (
{
tx_bw = [106];
rx_bw = [106];
carrier_tx = [4049760];
carrier_rx = [4049760];
prach_config_index = 157;
prach_msg1_start = 0;
tdd_period = 5;
num_dl_slots = 3;
num_dl_symbols = 6;
num_ul_slots = 1;
num_ul_symbols = 4;
numerology = 1;
tp_cores = [-1, -1, -1, -1, -1, -1]
});
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 0;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
});
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:05:02.0", "0000:05:02.1"); # one VF can be used as well
system_core = 25;
io_core = 26;
worker_cores = (27);
du_addr = ("00:11:22:33:64:68", "00:11:22:33:64:69");
mtu = 9600;
file_prefix = "ru";
app_id = "RU";
fh_config = ({
Ta3_up = (200, 470);
T2a_up = (350, 1200);
ru_config = {
iq_width = 16;
iq_width_prach = 16;
};
});
};