Compare commits

...

26 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
8890ec7387 fix packet rx window wrap-around
Add histogram for rx packets
2026-05-20 10:59:36 +02: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
37 changed files with 3740 additions and 113 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;
}

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

@@ -0,0 +1,752 @@
/*
* 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)
{
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);
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]);
}
}
LOG_D(PHY,
"[RU_thread] transmit data: frame %d, slot %d, start_symbol %d, num_symbols %d, timestamp %ld\n",
frame,
slot,
start_symbol,
num_symbols,
timestamp_tx);
join_task_ans(&task_ans);
tx_rf_symbols(ru, frame, slot, timestamp_tx, start_symbol, num_symbols);
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,
@@ -608,7 +612,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 +680,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

@@ -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)

876
radio/fhi_72/oaioran_ru.c Normal file
View File

@@ -0,0 +1,876 @@
/*
* 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 "assertions.h"
#include "nr/nr_common.h"
#include "utils.h"
#include <asm-generic/errno.h>
#include <netinet/in.h>
#include <rte_ring.h>
#include <rte_ring_core.h>
#include <rte_ring_elem.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "xran_fh_o_du.h"
#include "xran_fh_o_ru.h"
#include "xran_compression.h"
#include "armral_bfp_compression.h"
#include <xran_pkt.h>
#include <xran_pkt_cp.h>
#include <xran_pkt_up.h>
#include <xran_transport.h>
#if defined(__arm__) || defined(__aarch64__)
#else
// xran_cp_api.h uses SIMD, but does not include it
#include <immintrin.h>
#endif
#include "xran_cp_api.h"
#include "xran_sync_api.h"
#include "oran_isolate.h"
#include "xran_up_api.h"
#include "oran-init.h"
#include "oaioran.h"
#include <rte_ethdev.h>
#include "oran-config.h" // for g_kbar
#include "common/utils/threadPool/notified_fifo.h"
#define RATE_LIMIT(n) if (({ static int _counter = 0; _counter++ % (n) == 0; }))
#define ETHER_TYPE_ECPRI 0xAEFE
#define MAX_NUM_ANTENNAS 4
#define XRAN_GET_MU_FROM_SECT_ID(sectId) (sectId/XRAN_MAX_SECTIONS_PER_SLOT)
notifiedFIFO_t ru_dl_sync_fifo;
extern volatile bool first_call_set;
#define UPLANE_DATA_RING_SIZE 1024
#define NUM_UPLANE_DATA_ELEMENTS (UPLANE_DATA_RING_SIZE - 1)
#define UPLANE_SYMBOL_RING_SIZE 16
#define MAX_UPLANE_PACKETS_PER_SYMBOL (UPLANE_SYMBOL_RING_SIZE - 1)
typedef struct {
int frame;
int slot;
int symbol;
int num_symbols;
struct timespec ts;
} ru_dl_sync_info_t;
typedef struct {
uint16_t cb_symbol_mask;
int num_symbols[NR_SYMBOLS_PER_SLOT];
int start_symbol[NR_SYMBOLS_PER_SLOT];
int symbol_diff;
int numerology;
} oran_symbol_callback_args_t;
typedef struct {
void *iq_data;
void *mbuf_to_free;
int start_prb;
int num_prb;
int iq_width;
int comp_meth;
int aatx;
} uplane_data_t;
typedef struct {
uplane_data_t uplane_data_pool[NUM_UPLANE_DATA_ELEMENTS];
struct rte_ring *uplane_data_ring;
struct rte_ring **uplane_symbol_rings;
_Atomic(uint64_t) up_not_processed;
_Atomic(uint64_t) up_symbol_pool_exhausted;
_Atomic(uint64_t) up_received;
_Atomic(uint64_t) up_processed;
_Atomic(uint64_t) up_pool_exhausted;
_Atomic(uint64_t) up_dropped;
_Atomic(uint64_t) up_late;
_Atomic(uint64_t) up_early;
_Atomic(uint64_t) up_malformed;
_Atomic(uint64_t) up_arrival_histogram[128];
} packet_processor_context_t;
static packet_processor_context_t packet_processor_context;
static void init_packet_processor_context(packet_processor_context_t *context, int mu)
{
memset(context, 0, sizeof(packet_processor_context_t));
context->uplane_data_ring = rte_ring_create("uplane_data_ring", UPLANE_DATA_RING_SIZE, rte_socket_id(), 0);
AssertFatal(context->uplane_data_ring != NULL, "Failed to create ring uplane_data_ring\n");
for (int i = 0; i < NUM_UPLANE_DATA_ELEMENTS; i++) {
int ret = rte_ring_enqueue(context->uplane_data_ring, &context->uplane_data_pool[i]);
AssertFatal(ret == 0,
"Failed to push element %d/%d to uplane_data_ring, ring_size %d entries %d free entries %d return value %d \n",
i,
NUM_UPLANE_DATA_ELEMENTS,
UPLANE_DATA_RING_SIZE,
rte_ring_count(context->uplane_data_ring),
rte_ring_free_count(context->uplane_data_ring),
ret);
}
LOG_I(HW, "Enqueued %d elements to uplane_data_ring\n", NUM_UPLANE_DATA_ELEMENTS);
int num_slots_per_frame = 10 << mu;
int num_symbol_rings = NR_SYMBOLS_PER_SLOT * num_slots_per_frame;
context->uplane_symbol_rings = malloc(sizeof(struct rte_ring *) * num_symbol_rings);
for (int i = 0; i < num_symbol_rings; i++) {
char name[128];
snprintf(name, sizeof(name), "up_symbol_%d", i);
struct rte_ring *ring = rte_ring_create(name, UPLANE_SYMBOL_RING_SIZE, rte_socket_id(), 0);
AssertFatal(ring != NULL, "Failed to create ring %s\n", name);
context->uplane_symbol_rings[i] = ring;
}
}
static void clear_old_uplane_packets(packet_processor_context_t *context, int slot_in_frame, int symbol)
{
uplane_data_t *uplane_data[MAX_UPLANE_PACKETS_PER_SYMBOL];
struct rte_ring *ring = context->uplane_symbol_rings[slot_in_frame * NR_SYMBOLS_PER_SLOT + symbol];
int dequeued = rte_ring_dequeue_burst(ring, (void **)&uplane_data, MAX_UPLANE_PACKETS_PER_SYMBOL, NULL);
if (dequeued > 0) {
context->up_not_processed += dequeued;
context->up_dropped += dequeued;
for (int i = 0; i < dequeued; i++) {
rte_pktmbuf_free(uplane_data[i]->mbuf_to_free);
}
int enqueued = rte_ring_enqueue_burst(context->uplane_data_ring, (void **)uplane_data, dequeued, NULL);
AssertFatal(enqueued == dequeued, "Failed to push elements to uplane_data_ring\n");
}
}
static void enqueue_uplane_packet(packet_processor_context_t *context,
void *iq_data,
void *mbuf_to_free,
int start_prb,
int num_prb,
int iq_width,
int comp_meth,
int slot_in_frame,
int symbol,
int aatx)
{
uplane_data_t *uplane_data;
context->up_received++;
int ret = rte_ring_dequeue(context->uplane_data_ring, (void **)&uplane_data);
if (ret == 0) {
uplane_data->iq_data = iq_data;
uplane_data->mbuf_to_free = mbuf_to_free;
uplane_data->start_prb = start_prb;
uplane_data->num_prb = num_prb;
uplane_data->iq_width = iq_width;
uplane_data->comp_meth = comp_meth;
uplane_data->aatx = aatx;
struct rte_ring *ring = context->uplane_symbol_rings[slot_in_frame * NR_SYMBOLS_PER_SLOT + symbol];
int ret = rte_ring_enqueue(ring, uplane_data);
if (ret != 0) {
context->up_symbol_pool_exhausted++;
context->up_dropped++;
rte_pktmbuf_free(mbuf_to_free);
ret = rte_ring_enqueue(context->uplane_data_ring, uplane_data);
AssertFatal(ret == 0, "Failed to push elements to uplane_data_ring\n");
}
} else {
context->up_pool_exhausted++;
context->up_dropped++;
rte_pktmbuf_free(mbuf_to_free);
}
}
static int32_t pull_uplane_packet_data(packet_processor_context_t *context,
uplane_data_t **uplane_data,
int num_data,
int slot_in_frame,
int symbol)
{
struct rte_ring *ring = context->uplane_symbol_rings[slot_in_frame * NR_SYMBOLS_PER_SLOT + symbol];
int ret = rte_ring_dequeue_burst(ring, (void **)uplane_data, num_data, NULL);
if (ret > 0) {
context->up_processed += ret;
}
return ret;
}
static void push_uplane_packet_data(packet_processor_context_t *context, uplane_data_t **uplane_data, int num_data)
{
int enqueued = rte_ring_enqueue_burst(context->uplane_data_ring, (void **)uplane_data, num_data, NULL);
AssertFatal(enqueued == num_data, "Failed to push elements to uplane_data_ring\n");
}
static void print_statistics(packet_processor_context_t *context)
{
if (context->up_pool_exhausted > 0) {
LOG_W(HW, "Packets lost due to pool exhaustion: %lu\n", context->up_pool_exhausted);
context->up_pool_exhausted = 0;
}
if (context->up_symbol_pool_exhausted > 0) {
LOG_W(HW, "Packets lost due to symbol pool exhaustion: %lu\n", context->up_symbol_pool_exhausted);
context->up_symbol_pool_exhausted = 0;
}
if (context->up_not_processed > 0) {
LOG_W(HW, "Packets not processed by the application layer (application layer too slow): %lu\n", context->up_not_processed);
context->up_not_processed = 0;
}
if (context->up_late > 0) {
LOG_W(HW, "Packets late: %lu\n", context->up_late);
context->up_late = 0;
}
if (context->up_early > 0) {
LOG_W(HW, "Packets early: %lu\n", context->up_early);
context->up_early = 0;
}
if (context->up_malformed > 0) {
LOG_W(HW, "Packets malformed (packet couldn't be processed): %lu\n", context->up_malformed);
context->up_malformed = 0;
}
LOG_I(HW, "RU: packets received %lu\n", context->up_received);
LOG_I(HW, "RU: packets processed %lu\n", context->up_processed);
LOG_I(HW, "RU: packets dropped %lu\n", context->up_dropped);
bool printed_header = false;
for (int i = 0; i < 128; i++) {
uint64_t count = context->up_arrival_histogram[i];
if (count > 0) {
if (!printed_header) {
LOG_I(HW, "RU: packet arrival histogram (diff in symbols):\n");
printed_header = true;
}
LOG_I(HW, " %3d symbols early: %lu\n", i, count);
context->up_arrival_histogram[i] = 0;
}
}
}
typedef struct {
int section_id;
int num_prb;
int start_prb;
int slot;
int frame;
int mu;
int filter_id;
} oran_prach_cplane_config_t;
typedef struct {
int section_id;
int num_prb;
int start_prb;
} oran_pusch_cplane_config_t;
static oran_pusch_cplane_config_t pusch_config[MAX_NUM_ANTENNAS][20][14];
oran_prach_cplane_config_t prach_config_per_antenna[MAX_NUM_ANTENNAS] = {0};
static uint8_t prach_seq_id[MAX_NUM_ANTENNAS] = {0};
static _Atomic(uint8_t) pusch_seq_id[MAX_NUM_ANTENNAS] = {0};
extern int32_t xran_ethdi_mbuf_send(struct rte_mbuf *mb, uint16_t ethertype, uint16_t vf_id);
extern uint16_t xran_map_ecpriPcid_to_vf(void *p_dev_ctx, int32_t dir, int32_t cc_id, int32_t ru_port_id);
void symbol_callback(void *args, struct xran_sense_of_time *p_sense_of_time)
{
if (!first_call_set) {
return;
}
oran_symbol_callback_args_t *callback_args = args;
int num_slots_per_subframe = 1 << callback_args->numerology;
int num_slots_per_frame = 10 << callback_args->numerology;
int slot_in_frame = p_sense_of_time->nSlotIdx + p_sense_of_time->nSubframeIdx * num_slots_per_subframe;
clear_old_uplane_packets(&packet_processor_context, slot_in_frame, p_sense_of_time->nSymIdx);
if ((callback_args->cb_symbol_mask & (1 << p_sense_of_time->nSymIdx)) == 0) {
return;
}
int num_symbols = callback_args->num_symbols[p_sense_of_time->nSymIdx];
int start_symbol = callback_args->start_symbol[p_sense_of_time->nSymIdx];
// Adjust timing by symbol_diff
int slot_index_increments = (p_sense_of_time->nSymIdx + callback_args->symbol_diff) / NR_SYMBOLS_PER_SLOT;
int target_slot_in_frame =
p_sense_of_time->nSlotIdx + p_sense_of_time->nSubframeIdx * num_slots_per_subframe + slot_index_increments;
int frame = p_sense_of_time->nFrameIdx;
while (target_slot_in_frame >= num_slots_per_frame) {
target_slot_in_frame -= num_slots_per_frame;
frame++;
if (frame >= 1024) {
frame = 0;
}
}
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
int mu = fh_cfg->frame_conf.nNumerology;
AssertFatal(mu == 1, "Only numerology 1 supported for RU\n");
LOG_D(HW,
"Callback triggered at frame.slot.symbol %d.%d.%d targets %d.%d.%d, num_symbols %d, symbol_diff %d\n",
p_sense_of_time->nFrameIdx,
slot_in_frame,
p_sense_of_time->nSymIdx,
frame,
target_slot_in_frame,
start_symbol,
num_symbols,
callback_args->symbol_diff);
notifiedFIFO_elt_t *req = newNotifiedFIFO_elt(sizeof(ru_dl_sync_info_t), 0, NULL, NULL);
ru_dl_sync_info_t *info = NotifiedFifoData(req);
info->frame = frame;
info->slot = target_slot_in_frame;
info->symbol = start_symbol;
info->num_symbols = num_symbols;
int slot_duration_uS[] = {1000, 500, 250, 125};
uint64_t slot_in_second_offset_nS = ((uint64_t)p_sense_of_time->tti_counter * slot_duration_uS[mu]) * 1000UL;
float symbol_duration_nS = ((float)slot_duration_uS[mu] * 1000) / 14.0f;
uint64_t symbol_in_slot_offset_nS = (uint64_t)((p_sense_of_time->nSymIdx + callback_args->symbol_diff) * symbol_duration_nS);
info->ts.tv_sec = p_sense_of_time->nSecond;
info->ts.tv_nsec = slot_in_second_offset_nS + symbol_in_slot_offset_nS;
if (info->ts.tv_nsec >= 1000000000UL) {
info->ts.tv_sec += 1;
info->ts.tv_nsec -= 1000000000UL;
}
AssertFatal(info->ts.tv_nsec < 1000000000UL, "ORAN: Invalid tv_nsec %ld\n", info->ts.tv_nsec);
pushNotifiedFIFO(&ru_dl_sync_fifo, req);
}
int xran_oru_tx_read_slot(uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol, int *num_symbols, struct timespec *ts)
{
notifiedFIFO_elt_t *res = pullNotifiedFIFO(&ru_dl_sync_fifo);
ru_dl_sync_info_t *info = NotifiedFifoData(res);
*slot = info->slot;
*frame = info->frame;
*symbol = info->symbol;
*num_symbols = info->num_symbols;
*ts = info->ts;
delNotifiedFIFO_elt(res);
if (*frame % 256 == 0 && *slot == 0 && *symbol == 0) {
print_statistics(&packet_processor_context);
}
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
int nPRBs = fh_cfg->nDLRBs;
int fftsize = 1 << fh_cfg->nDLFftSize;
int first_carrier_offset = fftsize - (nPRBs * NR_NB_SC_PER_RB / 2);
int num_sc_first_copy = (fftsize - first_carrier_offset);
int num_sc_second_copy = nPRBs * NR_NB_SC_PER_RB - num_sc_first_copy;
c16_t tx_data_sym[nb_tx][nPRBs * NR_NB_SC_PER_RB];
for (int sym = *symbol; sym < *symbol + *num_symbols; sym++) {
uplane_data_t *uplane_data[MAX_UPLANE_PACKETS_PER_SYMBOL];
int num_packets = pull_uplane_packet_data(&packet_processor_context, uplane_data, MAX_UPLANE_PACKETS_PER_SYMBOL, *slot, sym);
if (num_packets) {
memset(tx_data_sym, 0, sizeof(tx_data_sym));
for (int i = 0; i < num_packets; i++) {
int start_prb = uplane_data[i]->start_prb;
int num_prb = uplane_data[i]->num_prb;
int comp_meth = uplane_data[i]->comp_meth;
int aatx = uplane_data[i]->aatx;
void *iq_data = uplane_data[i]->iq_data;
AssertFatal(comp_meth == XRAN_COMPMETHOD_NONE, "Compression not supported\n");
uint16_t *source = (uint16_t *)iq_data;
int16_t *destination = (int16_t *)&tx_data_sym[aatx][start_prb * NR_NB_SC_PER_RB];
for (int j = 0; j < num_prb * NR_NB_SC_PER_RB * 2; j++) {
destination[j] = (int16_t)ntohs(source[j]);
}
rte_pktmbuf_free(uplane_data[i]->mbuf_to_free);
}
push_uplane_packet_data(&packet_processor_context, uplane_data, num_packets);
}
if (num_packets) {
for (int aatx = 0; aatx < nb_tx; aatx++) {
memcpy(&txdataF[aatx][fftsize * sym + first_carrier_offset], tx_data_sym[aatx], num_sc_first_copy * sizeof(c16_t));
memcpy(&txdataF[aatx][fftsize * sym], &tx_data_sym[aatx][num_sc_first_copy], num_sc_second_copy * sizeof(c16_t));
}
} else {
for (int aatx = 0; aatx < nb_tx; aatx++) {
memset(&txdataF[aatx][sym * fftsize], 0, sizeof(c16_t) * fftsize);
}
}
}
return 0;
}
int process_ru_uplane(struct rte_mbuf *pkt,
void *handle,
struct xran_eaxc_info *p_cid,
uint16_t port_id,
struct xran_sense_of_time *p_sense_of_time)
{
if (!first_call_set) {
return MBUF_FREE;
}
const struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
void *iq_data_start = NULL;
uint8_t CC_ID = 0xFF;
uint8_t Ant_ID = 0xFF;
uint8_t frame_id;
uint8_t subframe_id;
uint8_t slot_id;
uint8_t symb_id;
uint8_t filter_id;
union ecpri_seq_id seq_id;
uint16_t num_prbu;
uint16_t start_prbu;
uint16_t sym_inc;
uint16_t rb;
uint16_t sect_id;
int expect_comp = fh_cfg->ru_conf.compMeth != XRAN_COMPMETHOD_NONE;
enum xran_comp_hdr_type staticComp = fh_cfg->ru_conf.xranCompHdrType;
uint8_t compMeth = XRAN_COMPMETHOD_NONE;
uint8_t iqWidth = 0;
uint8_t is_prach;
int ret = xran_extract_iq_samples(pkt,
&iq_data_start,
&CC_ID,
&Ant_ID,
&frame_id,
&subframe_id,
&slot_id,
&symb_id,
&filter_id,
&seq_id,
&num_prbu,
&start_prbu,
&sym_inc,
&rb,
&sect_id,
expect_comp,
staticComp,
&compMeth,
&iqWidth,
&is_prach);
if (ret == 0) {
packet_processor_context.up_malformed++;
packet_processor_context.up_dropped++;
return MBUF_FREE;
}
LOG_D(HW,
"ORAN: U-plane packet received. CC_ID %d, Ant_ID %d, frame_id %d, subframe_id %d, slot_id %d, symb_id %d, filter_id %d, "
"num_prbu %d, start_prbu %d, sym_inc %d, rb %d, sect_id %d, compMeth %d, iqWidth %d, is_prach %d\n",
CC_ID,
Ant_ID,
frame_id,
subframe_id,
slot_id,
symb_id,
filter_id,
num_prbu,
start_prbu,
sym_inc,
rb,
sect_id,
compMeth,
iqWidth,
is_prach);
AssertFatal(compMeth == XRAN_COMPMETHOD_NONE, "Compression not supported\n");
int mu = fh_cfg->frame_conf.nNumerology;
int slots_per_subframe = 1 << mu;
int current_symbol_index = NR_SYMBOLS_PER_SLOT * (p_sense_of_time->nSlotIdx + p_sense_of_time->nSubframeIdx * slots_per_subframe) + p_sense_of_time->nSymIdx;
int target_symbol_index = NR_SYMBOLS_PER_SLOT * (slot_id + subframe_id * slots_per_subframe) + symb_id;
int diff = target_symbol_index - current_symbol_index;
int max_sym = NR_SYMBOLS_PER_SLOT * NR_NUMBER_OF_SUBFRAMES_PER_FRAME * slots_per_subframe;
if (diff < -max_sym / 2) {
diff += max_sym;
} else if (diff > max_sym / 2) {
diff -= max_sym;
}
packet_processor_context.up_arrival_histogram[min(max(diff, 0), 127)]++;
int slot_duration_uS = 1000 / (1 << mu);
int symbol_duration_uS = slot_duration_uS / NR_SYMBOLS_PER_SLOT;
int rx_window_start = fh_cfg->T2a_max_up / symbol_duration_uS;
int rx_window_end = fh_cfg->T2a_min_up / symbol_duration_uS;
if (diff < rx_window_end) {
packet_processor_context.up_late++;
return MBUF_FREE;
} else if (diff > rx_window_start) {
packet_processor_context.up_early++;
}
int slot_in_frame = slot_id + subframe_id * slots_per_subframe;
enqueue_uplane_packet(&packet_processor_context,
iq_data_start,
pkt,
start_prbu,
num_prbu,
iqWidth,
compMeth,
slot_in_frame,
symb_id,
Ant_ID);
return MBUF_KEEP;
}
int32_t process_ru_cplane(struct rte_mbuf *pkt, void *handle, uint16_t port_id, struct xran_sense_of_time *p_sense_of_time)
{
const struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
struct xran_ecpri_hdr *ecpri_hdr;
struct xran_recv_packet_info xran_recv_packet_info;
int ret = xran_parse_ecpri_hdr(pkt, &ecpri_hdr, &xran_recv_packet_info);
if (ret != XRAN_STATUS_SUCCESS) {
return MBUF_FREE;
}
struct xran_cp_radioapp_common_header *apphdr = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_ecpri_hdr));
if (apphdr == NULL) {
LOG_W(HW, "issue extracting apphdr\n");
return MBUF_FREE;
}
apphdr->field.all_bits = rte_be_to_cpu_32(apphdr->field.all_bits);
if (apphdr->field.payloadVer != XRAN_PAYLOAD_VER) {
LOG_W(HW, "Invalid payloadVer field %d\n", apphdr->field.payloadVer);
return MBUF_FREE;
}
switch (apphdr->sectionType) {
case XRAN_CP_SECTIONTYPE_3: {
struct xran_cp_radioapp_section3_header *hdr = (struct xran_cp_radioapp_section3_header *)apphdr;
if (hdr->cmnhdr.numOfSections != 1) {
LOG_W(HW, "Only support one section\n");
return MBUF_FREE;
}
hdr->timeOffset = rte_be_to_cpu_16(hdr->timeOffset);
hdr->cpLength = rte_be_to_cpu_16(hdr->cpLength);
struct xran_cp_radioapp_section3 *section = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_cp_radioapp_section3_header));
if (section == NULL) {
LOG_W(HW, "Issue extracting section\n");
return MBUF_FREE;
}
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
int mu = hdr->frameStructure.uScs;
int aarx = xran_recv_packet_info.eaxc.ruPortId - fh_cfg->prach_conf.eAxC_offset;
oran_prach_cplane_config_t prach_config = {
.frame = hdr->cmnhdr.field.frameId,
.slot = hdr->cmnhdr.field.slotId + hdr->cmnhdr.field.subframeId + hdr->cmnhdr.field.subframeId * hdr->frameStructure.uScs,
.num_prb = section->hdr.u1.common.numPrbc,
.start_prb = section->hdr.u1.common.startPrbc,
.section_id = section->hdr.u1.common.sectionId,
.mu = mu,
.filter_id = hdr->cmnhdr.field.filterIndex
};
prach_config_per_antenna[aarx] = prach_config;
return MBUF_FREE;
}
case XRAN_CP_SECTIONTYPE_1: {
struct xran_cp_radioapp_section1_header *hdr = (struct xran_cp_radioapp_section1_header *)apphdr;
struct xran_cp_radioapp_section1 *section = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_cp_radioapp_section1_header));
if (section == NULL) {
LOG_W(HW, "Issue extracting section\n");
return MBUF_FREE;
}
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
if (hdr->cmnhdr.field.dataDirection == XRAN_DIR_DL) {
// For now skip DL
return MBUF_FREE;
}
//int frame = hdr->cmnhdr.field.frameId;
int section_id = section->hdr.u1.common.sectionId;
int mu = fh_cfg->frame_conf.nNumerology;
int slot = hdr->cmnhdr.field.slotId + hdr->cmnhdr.field.subframeId * (1 << mu);
uint32_t start_symbol = hdr->cmnhdr.field.startSymbolId;
int startPrbc = section->hdr.u1.common.startPrbc;
int numPrbc = section->hdr.u1.common.numPrbc;
int num_symbols = section->hdr.u.s1.numSymbol;
int aarx = xran_recv_packet_info.eaxc.ruPortId;
if (start_symbol + num_symbols > 14) {
LOG_W(HW, "Invalid symbol index >= 14 start_symbol %d num_symbols %d\n", start_symbol, num_symbols);
}
LOG_D(HW,
"section_id %d mu %d slot %d frame %d startprb %d num_prb %d start_sym %u num_sym %d\n",
section_id,
mu,
slot,
hdr->cmnhdr.field.frameId,
startPrbc,
numPrbc,
start_symbol,
num_symbols);
for (int symbol = start_symbol; symbol < start_symbol + num_symbols && symbol < 14; symbol++) {
pusch_config[aarx][slot][symbol].section_id = section_id;
pusch_config[aarx][slot][symbol].start_prb = startPrbc;
pusch_config[aarx][slot][symbol].num_prb = numPrbc;
}
return MBUF_FREE;
}
default:
return MBUF_FREE;
}
}
void init_oru_packet_processor(void *handle, int callbacks_per_slot)
{
AssertFatal(callbacks_per_slot <= NR_SYMBOLS_PER_SLOT,
"Can do at most %d callbacks per slot",
NR_SYMBOLS_PER_SLOT);
static bool installed = false;
AssertFatal(!installed, "Cannot init oru twice\n");
installed = true;
for (int aarx = 0; aarx < MAX_NUM_ANTENNAS; aarx++) {
prach_config_per_antenna[aarx].section_id = -1;
prach_config_per_antenna[aarx].num_prb = -1;
prach_config_per_antenna[aarx].start_prb = -1;
prach_config_per_antenna[aarx].slot = -1;
prach_config_per_antenna[aarx].frame = -1;
}
for (int aarx = 0; aarx < MAX_NUM_ANTENNAS; aarx++) {
for (int slot = 0; slot < 20; slot++) {
for (int symbol = 0; symbol < 14; symbol++) {
pusch_config[aarx][slot][symbol].section_id = -1;
}
}
}
// Represents RX window end
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
int mu = fh_cfg->frame_conf.nNumerology;
init_packet_processor_context(&packet_processor_context, mu);
uint32_t T2a_min = fh_cfg->T2a_min_up;
int slot_duration_uS[] = {1000, 500, 250, 125};
float symbol_duration_nS = ((float)slot_duration_uS[mu] * 1000) / 14.0f;
uint32_t symbol_offset = (float)(T2a_min * 1000) / symbol_duration_nS;
AssertFatal(symbol_offset > 0, "The amount of time after RX window end for O-RU is 0. Adjust T2a_min_up %u [uS]\n", T2a_min);
LOG_I(HW, "Installing %d callbacks %d symbols before OTA\n", callbacks_per_slot, symbol_offset);
static oran_symbol_callback_args_t args = {0};
args.numerology = mu;
int symbols_per_callback = NR_SYMBOLS_PER_SLOT / callbacks_per_slot;
int start_symbol = 0;
for (int i = 0; i < callbacks_per_slot; i++) {
int extra_symbols = 0;
if (i == callbacks_per_slot - 1) {
// Extend last callback to include leftover symbols
extra_symbols += NR_SYMBOLS_PER_SLOT % callbacks_per_slot;
}
int num_sybmols_this_callback = symbols_per_callback + extra_symbols;
int end_symbol = start_symbol + num_sybmols_this_callback - 1;
int callback_symbol = (end_symbol - symbol_offset + NR_SYMBOLS_PER_SLOT) % NR_SYMBOLS_PER_SLOT;
args.cb_symbol_mask |= (1U << callback_symbol);
args.num_symbols[callback_symbol] = num_sybmols_this_callback;
args.start_symbol[callback_symbol] = start_symbol;
args.symbol_diff = symbol_offset;
start_symbol += symbols_per_callback;
}
xran_hook_install(handle, process_ru_uplane, NULL, process_ru_cplane, NULL, symbol_callback, &args, mu);
}
extern void *xran_ethdi_mbuf_alloc(void);
void fill_ecpri_header(struct xran_ecpri_hdr *ecpri_header,
uint8_t ecpri_mesg_type,
size_t ecpri_payload_size,
uint8_t CC_ID,
uint8_t Ant_ID,
uint8_t seq_id,
uint8_t oxu_port_id)
{
ecpri_header->cmnhdr.data.data_num_1 = 0x0;
ecpri_header->cmnhdr.bits.ecpri_ver = XRAN_ECPRI_VER;
ecpri_header->cmnhdr.bits.ecpri_mesg_type = ecpri_mesg_type;
ecpri_header->cmnhdr.bits.ecpri_payl_size = rte_cpu_to_be_16(ecpri_payload_size);
ecpri_header->ecpri_xtc_id = xran_compose_cid(0, 0, CC_ID, Ant_ID);
ecpri_header->ecpri_seq_id.bits.seq_id = seq_id;
ecpri_header->ecpri_seq_id.bits.e_bit = 1;
ecpri_header->ecpri_seq_id.bits.sub_seq_id = 0;
/// No byteswap for ecpri_seq_id. Possibly because of inverse definition in xran
}
void fill_radio_app_header(struct radio_app_common_hdr *radio_app_header,
int filter_id,
int direction,
int frame,
int slot,
int symbol,
int mu)
{
radio_app_header->frame_id = frame & 0xff;
radio_app_header->sf_slot_sym.slot_id = slot % (1 << mu);
radio_app_header->sf_slot_sym.subframe_id = slot / (1 << mu);
radio_app_header->sf_slot_sym.symb_id = symbol;
radio_app_header->sf_slot_sym.value = rte_cpu_to_be_16(radio_app_header->sf_slot_sym.value);
radio_app_header->data_feature.data_direction = direction;
radio_app_header->data_feature.payl_ver = 1;
radio_app_header->data_feature.filter_id = filter_id;
}
void fill_data_section_header(struct data_section_hdr *data_section_hdr, int num_prb, int start_prb, int section_id)
{
data_section_hdr->fields.all_bits = 0;
data_section_hdr->fields.num_prbu = (uint8_t)XRAN_CONVERT_NUMPRBC(num_prb);
data_section_hdr->fields.start_prbu = (start_prb & 0x03ff);
data_section_hdr->fields.sect_id = section_id;
data_section_hdr->fields.all_bits = rte_cpu_to_be_32(data_section_hdr->fields.all_bits);
}
void xran_oru_send_prach(uint32_t *prachF, int aarx, int frame, int slot, int symbol)
{
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
uint8_t mu = fh_cfg->frame_conf.nNumerology;
AssertFatal(fh_cfg->ru_conf.compMeth_PRACH == XRAN_COMPMETHOD_NONE, "Compression not supported\n");
// TODO: With compression, have to add compression header to header_len
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr);
// TODO: For compression, have to re-evaluate data size;
// TODO: Only support short format PRACH
const uint prach_length = 139;
size_t data_len = sizeof(int32_t) * prach_length;
oran_prach_cplane_config_t *prach_config = &prach_config_per_antenna[aarx];
if (prach_config->section_id == -1) {
RATE_LIMIT(1000)
LOG_W(HW, "PRACH was not yet configured by the O-DU\n");
return;
}
if (prach_config->frame != (frame & 0xff) || prach_config->slot != slot) {
RATE_LIMIT(1000)
LOG_W(HW,
"PRACH was not configured for frame.slot %d.%d, configuration is for frame.slot %d.%d\n",
frame,
slot,
prach_config->frame,
prach_config->slot);
return;
}
struct rte_mbuf *mbuf = xran_ethdi_mbuf_alloc();
AssertFatal(mbuf != NULL, "out of mbufs\n");
char *buf = rte_pktmbuf_append(mbuf, header_length + data_len);
AssertFatal(buf, "incorrect mbuf size\n");
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)rte_pktmbuf_mtod(mbuf, char *);
uint16_t ecpri_payload_size = xran_get_ecpri_hdr_size() + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr) + data_len;
fill_ecpri_header(ecpri_header, ECPRI_IQ_DATA, ecpri_payload_size, 0, aarx + fh_cfg->prach_conf.eAxC_offset, prach_seq_id[aarx]++, 0);
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
fill_radio_app_header(radio_app_header, prach_config->filter_id, XRAN_DIR_UL, frame, slot, symbol, mu);
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
fill_data_section_header(data_section_header, prach_config->num_prb, prach_config->start_prb, prach_config->section_id);
void *iq_data_start = (void *)(data_section_header + 1);
int16_t *dest = (int16_t *)iq_data_start;
uint16_t *src = (uint16_t *)prachF;
for (int i = 0; i < prach_length * 2; i++) {
dest[i + g_kbar] = (int16_t)htons(src[i]);
}
buf = rte_pktmbuf_prepend(mbuf, sizeof(struct rte_ether_hdr));
AssertFatal(buf != NULL, "incorrect mbuf size\n");
int vf_id = xran_map_ecpriPcid_to_vf(gxran_handle, XRAN_DIR_UL, 0, aarx + fh_cfg->prach_conf.eAxC_offset);
int ret = xran_ethdi_mbuf_send(mbuf, ETHER_TYPE_ECPRI, vf_id);
AssertFatal(ret == 1, "Error sending mbuf\n");
}
void xran_oru_send_pusch(uint32_t *puschF, int aarx, int frame, int slot, int symbol)
{
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
const struct xran_frame_config *frame_conf = &fh_cfg->frame_conf;
if (frame_conf->nFrameDuplexType == 1) {
int slot_in_pattern = slot % frame_conf->nTddPeriod;
AssertFatal(frame_conf->sSlotConfig[slot_in_pattern].nSymbolType[symbol] == 1, "Attempting to send PUSCH on non-UL slot\n");
}
int section_id = pusch_config[aarx][slot][symbol].section_id;
if (section_id == -1) {
RATE_LIMIT(1000)
LOG_W(HW, "PUSCH was not yet configured by the O-DU frame %d, slot %d, symbol %d, aarx %d\n", frame, slot, symbol, aarx);
return;
}
uint8_t mu = fh_cfg->frame_conf.nNumerology;
const int num_ul_rbs = fh_cfg->nULRBs;
int num_prb = pusch_config[aarx][slot][symbol].num_prb;
int start_prb = pusch_config[aarx][slot][symbol].start_prb;
AssertFatal(num_prb == num_ul_rbs && start_prb == 0, "only support full bandwidth reception\n");
AssertFatal(fh_cfg->ru_conf.compMeth == XRAN_COMPMETHOD_NONE, "Compression not supported\n");
// TODO: With compression, have to add compression header to header_len
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr);
// TODO: For compression, have to re-evaluate data size;
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
size_t data_len = sizeof(int32_t) * num_sc;
struct rte_mbuf *mbuf = xran_ethdi_mbuf_alloc();
AssertFatal(mbuf != NULL, "out of mbufs\n");
char *buf = rte_pktmbuf_append(mbuf, header_length + data_len);
AssertFatal(buf, "incorrect mbuf size\n");
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)rte_pktmbuf_mtod(mbuf, char *);
uint16_t ecpri_payload_size = xran_get_ecpri_hdr_size() + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr) + data_len;
fill_ecpri_header(ecpri_header, ECPRI_IQ_DATA, ecpri_payload_size, 0, aarx, pusch_seq_id[aarx]++, 0);
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot, symbol, mu);
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
fill_data_section_header(data_section_header, fh_cfg->nULRBs, 0, section_id);
// TODO: O-DU expect compression header here even though the standard says it's not required
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
compression_header->ud_comp_hdr.ud_comp_meth = XRAN_COMPMETHOD_NONE;
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(16);
compression_header->rsrvd = 0;
int fftsize = 1 << fh_cfg->nULFftSize;
int first_carrier_offset = fftsize - (fh_cfg->nULRBs * NR_NB_SC_PER_RB / 2);
int num_sc_first_copy = (fftsize - first_carrier_offset);
int num_sc_second_copy = fh_cfg->nULRBs * NR_NB_SC_PER_RB - num_sc_first_copy;
void *iq_data_start = (void *)(compression_header + 1);
int16_t* dest = (int16_t*)iq_data_start;
uint16_t* src = (uint16_t*)&puschF[first_carrier_offset];
for (int i = 0; i < num_sc_first_copy * 2; i++) {
*dest++ = (int16_t)htons(src[i]);
}
src = (uint16_t*)puschF;
for (int i = 0; i < num_sc_second_copy * 2; i++) {
*dest++ = (int16_t)htons(src[i]);
}
buf = rte_pktmbuf_prepend(mbuf, sizeof(struct rte_ether_hdr));
AssertFatal(buf != NULL, "incorrect mbuf size\n");
int vf_id = xran_map_ecpriPcid_to_vf(gxran_handle, XRAN_DIR_UL, 0, aarx);
int ret = xran_ethdi_mbuf_send(mbuf, ETHER_TYPE_ECPRI, vf_id);
AssertFatal(ret == 1, "Error sending mbuf\n");
}

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"
@@ -890,7 +891,13 @@ 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)
{
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 +957,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
@@ -1038,14 +1077,15 @@ bool get_xran_config(void *mplane_api, const struct openair0_config *openair0_cf
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)) {
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)) {
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"
@@ -398,12 +401,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 +461,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

@@ -112,6 +112,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 +121,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

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);
@@ -401,6 +407,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

@@ -1094,6 +1094,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 +1121,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;
};
});
};