From cef07f74965b1749dd909fc1322e211489fea2ea Mon Sep 17 00:00:00 2001 From: Luis Farias Date: Fri, 1 Nov 2019 14:21:18 -0700 Subject: [PATCH] Front Haul Interface Library update to first seed code contribution The following mandatory features of the ORAN FH interface are not yet supported in this initial release: 1) RU Category: Support of CAT-B RU (i.e. precoding in RU) 2) Beamforming: Beam Index Based and Real Time BF weights 3) Transport Features: QoS over FrontHaul Additional Information available in the readme.txt file xran root refers to the o-du/phy/fhi_lib folder Issue-Id: ODUPHY-1 Change-Id: I3c370c1dc794e73e8488a011148d367f7f3525eb Signed-off-by: Luis Farias --- fhi_lib/app/Makefile | 193 +- fhi_lib/app/ant_0.bin | Bin 3669120 -> 0 bytes fhi_lib/app/ant_1.bin | Bin 3669120 -> 0 bytes fhi_lib/app/ant_2.bin | Bin 3669120 -> 0 bytes fhi_lib/app/ant_3.bin | Bin 3669120 -> 0 bytes fhi_lib/app/common/common.c | 202 --- fhi_lib/app/config_file_lls_cu.dat | 92 - fhi_lib/app/config_file_ru.dat | 92 - fhi_lib/app/dpdk.sh | 62 +- fhi_lib/app/gen_test.m | 104 +- fhi_lib/app/ifft_in.txt | 792 +++++++++ fhi_lib/app/lls-cu/Makefile | 90 - fhi_lib/app/ru/Makefile | 85 - fhi_lib/app/{run_lls-cu.sh => run_o_du.sh} | 7 +- fhi_lib/app/{run_ru.sh => run_o_ru.sh} | 3 +- fhi_lib/app/src/common.c | 668 +++++++ fhi_lib/app/{common => src}/common.h | 64 +- fhi_lib/app/src/config.c | 249 +-- fhi_lib/app/src/config.h | 28 +- fhi_lib/app/src/debug.h | 82 + .../{lls-cu/sample-lls-cu.c => src/sample-app.c} | 819 ++++++--- fhi_lib/app/src/xran_mlog_task_id.h | 16 +- .../app/usecase/mu0_10mhz/12/config_file_o_du.dat | 139 ++ .../app/usecase/mu0_10mhz/12/config_file_o_ru.dat | 190 ++ fhi_lib/app/usecase/mu0_10mhz/config_file_o_du.dat | 139 ++ fhi_lib/app/usecase/mu0_10mhz/config_file_o_ru.dat | 145 ++ fhi_lib/app/usecase/mu0_20mhz/config_file_o_du.dat | 105 ++ fhi_lib/app/usecase/mu0_20mhz/config_file_o_ru.dat | 111 ++ fhi_lib/app/usecase/mu0_5mhz/config_file_o_du.dat | 106 ++ fhi_lib/app/usecase/mu0_5mhz/config_file_o_ru.dat | 105 ++ .../app/usecase/mu1_100mhz/config_file_o_du.dat | 116 ++ .../app/usecase/mu1_100mhz/config_file_o_ru.dat | 134 ++ .../app/usecase/mu3_100mhz/config_file_o_du.dat | 116 ++ .../app/usecase/mu3_100mhz/config_file_o_ru.dat | 122 ++ fhi_lib/build.sh | 62 +- fhi_lib/lib/Makefile | 196 +- fhi_lib/lib/api/xran_compression.hpp | 82 + fhi_lib/lib/api/xran_cp_api.h | 212 ++- .../lib/api/{xran_fh_lls_cu.h => xran_fh_o_du.h} | 550 +++--- .../{src/mlog_lnx_xRAN.h => api/xran_mlog_lnx.h} | 14 +- fhi_lib/lib/api/xran_pkt.h | 102 +- fhi_lib/lib/api/xran_pkt_cp.h | 192 +- fhi_lib/lib/api/xran_pkt_up.h | 45 +- fhi_lib/lib/api/xran_sync_api.h | 9 +- fhi_lib/lib/api/xran_timer.h | 13 +- fhi_lib/lib/api/xran_transport.h | 58 +- fhi_lib/lib/api/xran_up_api.h | 22 +- fhi_lib/lib/ethernet/ethdi.c | 260 ++- fhi_lib/lib/ethernet/ethdi.h | 19 +- fhi_lib/lib/ethernet/ethernet.c | 184 +- fhi_lib/lib/ethernet/ethernet.h | 29 +- fhi_lib/lib/src/xran_app_frag.c | 259 +++ fhi_lib/lib/src/xran_app_frag.h | 61 + fhi_lib/lib/src/xran_common.c | 566 ++++-- fhi_lib/lib/src/xran_common.h | 186 +- fhi_lib/lib/src/xran_compression.cpp | 185 ++ fhi_lib/lib/src/xran_cp_api.c | 1024 +++++++---- fhi_lib/lib/src/xran_frame_struct.c | 509 ++++++ fhi_lib/lib/src/xran_frame_struct.h | 83 + fhi_lib/lib/src/xran_hash.h | 45 - fhi_lib/lib/src/xran_lib_mlog_tasks_id.h | 7 + fhi_lib/lib/src/xran_main.c | 1867 +++++++++++++++----- fhi_lib/lib/src/xran_printf.h | 12 +- fhi_lib/lib/src/xran_sync_api.c | 25 +- fhi_lib/lib/src/xran_timer.c | 58 +- fhi_lib/lib/src/xran_transport.c | 143 +- fhi_lib/lib/src/xran_ul_tables.c | 7 +- fhi_lib/lib/src/xran_up_api.c | 141 +- fhi_lib/readme.txt | 422 ++++- 69 files changed, 10119 insertions(+), 2706 deletions(-) delete mode 100644 fhi_lib/app/ant_0.bin delete mode 100644 fhi_lib/app/ant_1.bin delete mode 100644 fhi_lib/app/ant_2.bin delete mode 100644 fhi_lib/app/ant_3.bin delete mode 100644 fhi_lib/app/common/common.c delete mode 100644 fhi_lib/app/config_file_lls_cu.dat delete mode 100644 fhi_lib/app/config_file_ru.dat create mode 100644 fhi_lib/app/ifft_in.txt delete mode 100644 fhi_lib/app/lls-cu/Makefile delete mode 100644 fhi_lib/app/ru/Makefile rename fhi_lib/app/{run_lls-cu.sh => run_o_du.sh} (85%) rename fhi_lib/app/{run_ru.sh => run_o_ru.sh} (92%) create mode 100644 fhi_lib/app/src/common.c rename fhi_lib/app/{common => src}/common.h (61%) create mode 100644 fhi_lib/app/src/debug.h rename fhi_lib/app/{lls-cu/sample-lls-cu.c => src/sample-app.c} (52%) create mode 100644 fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_ru.dat create mode 100644 fhi_lib/app/usecase/mu0_10mhz/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu0_10mhz/config_file_o_ru.dat create mode 100644 fhi_lib/app/usecase/mu0_20mhz/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu0_20mhz/config_file_o_ru.dat create mode 100644 fhi_lib/app/usecase/mu0_5mhz/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu0_5mhz/config_file_o_ru.dat create mode 100644 fhi_lib/app/usecase/mu1_100mhz/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu1_100mhz/config_file_o_ru.dat create mode 100644 fhi_lib/app/usecase/mu3_100mhz/config_file_o_du.dat create mode 100644 fhi_lib/app/usecase/mu3_100mhz/config_file_o_ru.dat create mode 100644 fhi_lib/lib/api/xran_compression.hpp rename fhi_lib/lib/api/{xran_fh_lls_cu.h => xran_fh_o_du.h} (57%) rename fhi_lib/lib/{src/mlog_lnx_xRAN.h => api/xran_mlog_lnx.h} (89%) create mode 100644 fhi_lib/lib/src/xran_app_frag.c create mode 100644 fhi_lib/lib/src/xran_app_frag.h create mode 100644 fhi_lib/lib/src/xran_compression.cpp create mode 100644 fhi_lib/lib/src/xran_frame_struct.c create mode 100644 fhi_lib/lib/src/xran_frame_struct.h delete mode 100644 fhi_lib/lib/src/xran_hash.h diff --git a/fhi_lib/app/Makefile b/fhi_lib/app/Makefile index ebad9b9..1c2437d 100644 --- a/fhi_lib/app/Makefile +++ b/fhi_lib/app/Makefile @@ -16,13 +16,192 @@ #* #*******************************************************************************/ -PHONY: all -all: - cd lls-cu && make #DEBUG=1 - cd ru && make #DEBUG=1 +MYCUSTOMTAB=' ' +MYCUSTOMSPACE='============================================================================================' +MYCUSTOMSPACE1='------------------------------------------------------------' -clean: - cd lls-cu && make clean #DEBUG=1 - cd ru && make clean #DEBUG=1 +############################################################## +# Tools configuration +############################################################## +CC := icc +CPP := icpc +AS := as +AR := ar +LD := icc +OBJDUMP := objdump +ifeq ($(SHELL),cmd.exe) +MD := mkdir.exe -p +CP := cp.exe -f +RM := rm.exe -rf +else +MD := mkdir -p +CP := cp -f +RM := rm -rf +endif + +PROJECT_NAME := sample-app +PROJECT_TYPE := elf +PROJECT_DIR := $(XRAN_DIR)/app +BUILDDIR := ./build +PROJECT_BINARY := $(BUILDDIR)/$(PROJECT_NAME) + +ifeq ($(RTE_SDK),) + $(error "Please define RTE_SDK environment variable") +endif + +RTE_TARGET ?= x86_64-native-linuxapp-gcc +RTE_INC := $(RTE_SDK)/$(RTE_TARGET)/include + +API_DIR := $(XRAN_DIR)/lib/api +SRC_DIR := $(PROJECT_DIR)/src + +ifeq ($(MLOG),1) +ifeq ($(MLOG_DIR),) + MLOG_DIR=$(XRAN_DIR)/../mlog +endif +endif + +CC_SRC = $(SRC_DIR)/common.c \ + $(SRC_DIR)/sample-app.c \ + $(SRC_DIR)/config.c + +CC_FLAGS += -std=gnu11 -Wall -Wno-deprecated-declarations \ + -fdata-sections \ + -ffunction-sections \ + -g \ + -Wall \ + -Wimplicit-function-declaration \ + -g -O3 + +CPP_FLAGS := -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D_GNU_SOURCE -D_REENTRANT -pipe -no-prec-div \ + -no-prec-div -fp-model fast=2\ + -no-prec-sqrt -falign-functions=16 -fast-transcendentals \ + -Werror -Wno-unused-variable -std=c++11 -mcmodel=large + +INC := -I$(API_DIR) -I$(RTE_INC) +DEF := + +ifeq ($(MLOG),1) + INC += -I$(MLOG_DIR)/source + DEF += -DMLOG_ENABLED +else + DEF += -UMLOG_ENABLED +endif + +XRAN_LIB_DIR=$(XRAN_DIR)/lib/build +LD_FLAGS += -L$(XRAN_LIB_DIR) -lxran + +RTE_LIBS = -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,-lrte_flow_classify -Wl,--whole-archive -Wl,-lrte_pipeline -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_table -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_port -Wl,--no-whole-archive -Wl,-lrte_pdump -Wl,-lrte_distributor -Wl,-lrte_ip_frag -Wl,-lrte_meter -Wl,-lrte_lpm -Wl,--whole-archive -Wl,-lrte_acl -Wl,--no-whole-archive -Wl,-lrte_jobstats -Wl,-lrte_metrics -Wl,-lrte_bitratestats -Wl,-lrte_latencystats -Wl,-lrte_power -Wl,-lrte_efd -Wl,-lrte_bpf -Wl,--whole-archive -Wl,-lrte_cfgfile -Wl,-lrte_gro -Wl,-lrte_gso -Wl,-lrte_hash -Wl,-lrte_member -Wl,-lrte_vhost -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_net -Wl,-lrte_ethdev -Wl,-lrte_bbdev -Wl,-lrte_cryptodev -Wl,-lrte_security -Wl,-lrte_compressdev -Wl,-lrte_eventdev -Wl,-lrte_rawdev -Wl,-lrte_timer -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_pci -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_reorder -Wl,-lrte_sched -Wl,-lrte_kni -Wl,-lrte_common_octeontx -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_bus_dpaa -Wl,-lrte_bus_fslmc -Wl,-lrte_mempool_bucket -Wl,-lrte_mempool_stack -Wl,-lrte_mempool_dpaa -Wl,-lrte_mempool_dpaa2 -Wl,-lrte_pmd_af_packet -Wl,-lrte_pmd_ark -Wl,-lrte_pmd_avf -Wl,-lrte_pmd_avp -Wl,-lrte_pmd_axgbe -Wl,-lrte_pmd_bnxt -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_cxgbe -Wl,-lrte_pmd_dpaa -Wl,-lrte_pmd_dpaa2 -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ena -Wl,-lrte_pmd_enic -Wl,-lrte_pmd_fm10k -Wl,-lrte_pmd_failsafe -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_kni -Wl,-lrte_pmd_lio -Wl,-lrte_pmd_nfp -Wl,-lrte_pmd_null -Wl,-lrte_pmd_qede -Wl,-lrte_pmd_ring -Wl,-lrte_pmd_softnic -Wl,-lrte_pmd_tap -Wl,-lrte_pmd_thunderx_nicvf -Wl,-lrte_pmd_vdev_netvsc -Wl,-lrte_pmd_virtio -Wl,-lrte_pmd_vhost -Wl,-lrte_pmd_ifc -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_bus_vmbus -Wl,-lrte_pmd_netvsc -Wl,-lrte_pmd_bbdev_null -Wl,-lrte_pmd_null_crypto -Wl,-lrte_pmd_crypto_scheduler -Wl,-lrte_pmd_dpaa2_sec -Wl,-lrte_pmd_dpaa_sec -Wl,-lrte_pmd_virtio_crypto -Wl,-lrte_pmd_octeontx_zip -Wl,-lrte_pmd_qat -Wl,-lrte_pmd_skeleton_event -Wl,-lrte_pmd_sw_event -Wl,-lrte_pmd_octeontx_ssovf -Wl,-lrte_pmd_dpaa_event -Wl,-lrte_pmd_dpaa2_event -Wl,-lrte_mempool_octeontx -Wl,-lrte_pmd_octeontx -Wl,-lrte_pmd_opdl_event -Wl,-lrte_pmd_skeleton_rawdev -Wl,-lrte_pmd_dpaa2_cmdif -Wl,-lrte_pmd_dpaa2_qdma -Wl,-lrte_bus_ifpga -Wl,-lrte_pmd_ifpga_rawdev -Wl,--no-whole-archive -Wl,-lrt -Wl,-lm -Wl,-lnuma -Wl,-ldl -Wl, +LD_FLAGS += $(RTE_LIBS) + +ifeq ($(MLOG),1) + LD_FLAGS += -L$(MLOG_DIR)/bin -lmlog +endif + +AS_FLAGS := +AR_FLAGS := rc + +PROJECT_OBJ_DIR := build/obj + +CC_OBJS := $(patsubst %.c,%.o,$(CC_SRC)) +CPP_OBJS := $(patsubst %.cpp,%.o,$(CPP_SRC)) +AS_OBJS := $(patsubst %.s,%.o,$(AS_SRC)) +OBJS := $(CC_OBJS) $(CPP_OBJS) $(AS_OBJS) $(LIBS) +DIRLIST := $(addprefix $(PROJECT_OBJ_DIR)/,$(sort $(dir $(OBJS)))) + +CC_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(CC_OBJS)) +CPP_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(CPP_OBJS)) + +AS_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(AS_OBJS)) + +CC_FLAGS_FULL := $(CC_FLAGS) $(INC) $(DEF) +CPP_FLAGS_FULL := $(CPP_FLAGS) $(INC) $(DEF) + +AS_FLAGS := $(AS_FLAGS) $(INC) + +PROJECT_DEP_FILE := $(PROJECT_OBJ_DIR)/$(PROJECT_NAME).dep + +ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) +GENERATE_DEPS := +else + +CC_DEPS := $(addprefix __dep__,$(subst ../,__up__,$(CC_SRC))) +CPP_DEPS := $(addprefix __dep__,$(subst ../,__up__,$(CPP_SRC))) +GENERATE_DEPS := generate_deps +endif + +all : welcome_line $(PROJECT_BINARY) + @echo $(PROJECT_BINARY) + +.PHONY : clear_dep +clear_dep: + @$(RM) $(PROJECT_DEP_FILE) + @echo [DEP] $(subst $(PROJECT_OBJ_DIR)/,,$(PROJECT_DEP_FILE)) + +$(CC_DEPS) : + @$(CC) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.c,%.o,$(subst __up__,../,$(subst __dep__,,$@))) $(CC_FLAGS_FULL) >> $(PROJECT_DEP_FILE) + +$(CPP_DEPS) : + @$(CPP) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.cpp,%.o,$(subst __up__,../,$(subst __dep__,,$@))) $(CPP_FLAGS_FULL) >> $(PROJECT_DEP_FILE) + +.PHONY : generate_deps +generate_deps : clear_dep $(CC_DEPS) $(CPP_DEPS) + + +.PHONY : echo_start_build +echo_start_build : + @echo [BUILD] $(PROJECT_TYPE) : $(PROJECT_NAME) + +$(DIRLIST) : + -@$(MD) $@ + +$(CC_OBJTARGETS) : + @echo [CC] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(CC) -c $(CC_FLAGS_FULL) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@)) + +$(CPP_OBJTARGETS) : + @echo [CPP] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(CPP) -c $(CPP_FLAGS_FULL) -o"$@" $(patsubst %.o,%.cpp,$(subst $(PROJECT_OBJ_DIR)/,,$@)) + +$(AS_OBJTARGETS) : + @echo [AS] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(AS) $(AS_FLAGS) -o"$@" $(patsubst %.o,%.s,$(subst $(PROJECT_OBJ_DIR)/,,$@)) + +ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) + +include $(PROJECT_DEP_FILE) + +endif + +.PHONY: clean xclean +clean: + @echo [CLEAN] : $(PROJECT_NAME) + @$(RM) $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) + +xclean: clean +ifneq ($(wildcard $(PROJECT_DIR)/$(PROJECT_MAKE)),) + @echo [XCLEAN] : $(PROJECT_NAME) + @$(RM) $(PROJECT_BINARY) $(PROJECT_BINARY_LIB) $(PROJECT_DEP_FILE) +endif + +.PHONY : welcome_line +welcome_line : + @echo $(MYCUSTOMSPACE) + @echo Building $(PROJECT_BINARY) + @echo $(MYCUSTOMTAB)RTE_TARGET = $(RTE_TARGET) + @echo $(MYCUSTOMSPACE) + + +.PHONY : debug release + +debug : all +release : all + +$(PROJECT_BINARY): $(DIRLIST) echo_start_build $(GENERATE_DEPS) $(PRE_BUILD) $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) + @echo "[LD] $@ " + @$(LD) -o $@ $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) $(LD_FLAGS) -Wl,-L $(BUILDDIR) -lrt -lpthread + +#@echo [APP] $@ +#@$(OBJDUMP) -d $(PROJECT_BINARY) > $(PROJECT_BINARY).asm diff --git a/fhi_lib/app/ant_0.bin b/fhi_lib/app/ant_0.bin deleted file mode 100644 index 4d9fb4b51f37bff64249d280643ff13ab006f1bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3669120 zcmeF)gBo0CqlV$wwwtDDY_m;m+qUi4w$U_=ZP&JKYE-8NHK|2y>QI+@)TaRrX~Z5hW>1>17fsol zW;CY-Eont-+R&DEw5J0d=|pF`(3Ng4~#Kl~~rCi44T)~xG z#noKHwOq&b+`x_8#Le8ot=z`#+`*mPMSlh`kUdpRbJzD-r!B% z;%(mHUEbq;KHx(>;$uGHQ$FK!zTiu~;%olLH+;)?e9sU3$WQ#tFZ{}H{LUZz$zS}< zKm5yoxb%w_PBDs8f|8V?G-W7DIm%Okid3R9Rj5ies#AlS)S@4&!i+;7E?*XpZ4nj^lVv;6zU1WKQ8!PUCdW;7rcqY|i0a&f|P8;6g6qVlLrQ zF5_~p;7YFIYOdj0uH$-c;6`rZW^UnDZsT_D;7;zMKLZ%ZAO&aK$t-3whq=sSJ`1>;h1|m;?qxAcSjsY%vx1eZVl``6%R1Jx zfsJfpGh5ioHny{ao$O*a_i;ZD@E{NIFpuykkMTH9@FY+1G|%uX&+$Aj@FFkqGOzF| zukku>@Fs8ZHt+B*@9{n#@F5@ZF`w`$pYb_g@FidIHUHxqzU4c<=Lde|Cw}G^e&siQ z=MVnmFaG8q{^dUkH~zxKUpU1nK}kwcnlhB79ObD%MJiF5DpaK!)u};EYEhdy)TJKv zX+T37u?LOWlP2s%Q}(7A&1pePTG5&|w51*G=|D$1(U~rEr5oMpK~H+on?CeqANHjm z`>{U)0*Ks{Ja3eQyGq-Rnw{bgna3^=sp8*VH5Q7=QP=+y_ z5sYLMqZz|k#xb4=Ok@(1nZi`2F`XIAWEQiT!(8Sup9S2_LhfM^_p+EJEM*zXS;0zH zv6?lkWgY9;z(zK)nJsK(8{65zPIj@I`?#M6c#wy9m`8Y&$9SA4c#@}hnrC>H=XjnM zc#)TQnOAs~*La;bc$2qyn|FAZ_jsQV_>hnIm{0hW&-k1#_>!;qn*Z?)-|`*b^8-Kf z6F>6{zw#Tu^9O(O7k~2)|MDL`@)vIVg-gG9;gp~xr6^4q%2JN7rN4o?)0E1 zz35FJ`mzuE(vSVvp946MgE*K&IF!RUoFh1rqd1ylIF{o$o)b8clQ@}EIF-{loijL- zvpAb`IG6J{p9{E|iIm*v)<1&jUQjLp;nQJj!D{&J#Sz zQ#{QxJj-)D&kMZBOT5f0yvl35&KtbRTfEIXyvuvM&j)iSA5O? z_=a!!j_>(_ANh%&`GsHkjoy8d(o7=X-0Ee(2`cPrVVXrM|(QZ zkxq1`3tj0(cY4s1Ui799ec6Y7>BoNT&jB3BK^)8>9LixF&Ji5RQ5?-N9LsSW&k3B! zNu10noXTmO&KaD^S)9!|oXdHf&jnn_MO@4!T*_r!&J|qARb0(AT+4M_&kfwjP29{a z+{$g-&K=yzUG!%F0~y3%hA@<23}*x*8O3PEFqUzQX95$M#AK#0m1#_81~Zw(Z00bR zdCX@4ce9XtSj4?7W(iAK#&TA$l2xo`4QpA)dN#0;O>AZhTiM2TcCeFO?B+i1=K&t% zAs*%t9_29}=Lw$VDW2vTp5-~7=LKHmC0^zgUgb4j=MCQEE#BrG-sL^s=L0_EBR=L6 zKIJn$=L^2%E57D`e8abV$M^iekNm{X{KBvN#_#;WpZvw&{KLQeN0E|63SajNANdQn z{lcYRvT#aKnlhB79ObD%MJiF5DpaK!)u};EYEhdy)TJKvX+T37u?LOWlP2s%Q}(7A z&1pePTG5&|w51*G=|D$1(U~rEr5oMpK~H+on?CeqANHjm`>{U)0*Ks{Ja3eQyGq-Rnw{bgna3^=sp8*VH5Q7=QP=+y_5sYLMqZz|k#xb4=Ok@(1 znZi`2F`XIAWEQiT!(8Sup9S2_LhfM^_p+EJEM*zXS;0zHv6?lkWgY9;z(zK)nJsK( z8{65zPIj@I`?#M6c#wy9m`8Y&$9SA4c#@}hnrC>H=XjnMc#)TQnOAs~*La;bc$2qy zn|FAZ_jsQV_>hnIm{0hW&-k1#_>!;qn*Z?)-|`*b^8-Kf6F>6{zw#Tu^9O(O7k~2) z|MDM2N);(plw$bEU%2fTF8xx4Q<^fAr5xp{Kt(E1nJQGJ8r7*mO=?k_I@F~e^=Uvu z8nFkB*^?&hMN{^s8O>=yOIp#IHngQ3?dd>AI?WgqsXAN#RC z2XG(F`ot8%|h;B5%;o~B`jqb%UQunRy5`9`$KJLmIIMjoFhX>_t=drWws?K}%ZEnl`kh9qs8rM>^4&E_9_E-RVJ3 zdeNIc^kpCRr62pTKL>Ci2XQcma43gyI7e_KM{zXAa4g4hJST7>Cvh^Ta4M&9I%jYu zXK^;?a4zR@J{NEy7jZF{a4DB@IahEcS8+Aha4pwyJvVS8H*qt!a4WZQJ9ls=chR2# z3}g_48NyJ8F`N;MWE7(r!&t^Ko(W835|f$2RHiYV8O&rBvzfzO<}sfI+|5GnVG;MT zm?bP_8OvG0N>;I&HLPVF>)F6YHnEv4Y-JnU*}+bBv77t2p9gr5hj^Grc$CL@oF{ma zr+AuYc$VjQo)>tLmw1_1c$L?9oi})sw|JX(c$fEhpAYzukNB8R_>|B1oG>AB7kEg_nGV2mOVIeT6Ulg%ACO8-L;A zFPyTJqdXO;NF^#$g{oAeIyI2r7UAPD_F@YR>(8$u4$t zANTVB5AqNX^9Yaf7?1M=Px2H`^9;}O9MAItFY*#E^9rx>8n5#PZ}Jvz^A7Lw9`Ex3 zAMz0&^9i5w8K3h7U-A`S^FO}fTfXCae&9!b;%9#0SAOGn{@_pk;&1-pU;d-;`gP8(}I??qBU)3OFP=rfsS;dGhOIPH@ee`OoPV}B0d zKn~(y4&hJ^<8Y4PNRHxYj^S92<9JTsL{8#lPT^Ee<8;p8OwQtL&f#3n<9sgQLN4NB zF5yxx<8rRxO0ME+uHjm)<9cr3MsDI}ZsAsL<96=gPVS;V0~p941~Y`A3}ZMW7|AF` zGlsE@V>}a>$Rs8+g{e$qIy0EbEM_x@xy)le3%Hww+`}U7Wid-w$}*O-f|aadHEUSQ zI@Ys+jcj5wTiD7rwzGqs>|!_faX%06AP?~{kMJmu@i?{1Zr|^Qm@RG0apuh02ukeMx@S(qO<1bwNg;SmiRHPD> zsX|q%QJospq!zWQLtW}op9VCf5qr>>J!!&TG-Yp^(VP~vq!q1cLtEO>o(^=R6P@Wo zSGv)i9`vLaz3D?=_F-T8u^;<$00(jq2XhFAau|nm1V?fdM{^9vavaBV0w;13Cvys? zavG;|24`{>XLAncavtY%0T*%+7jp@hav7I%1y^zvS91;5avj%m12=LLH**WOavQgE z2X}H8{TaYO1~Hf+3}qO@8No% zEaV;*aW9Km!cvy8oE5BO6{}gpTGp|i4Qyl+o7uuvwy~WZ>|__axsUsKfCqVqhk1lY zd5p(-f+u;3r+J2Fd5-6Kffsp+mwAO(d5zb3gEx7Lw|R$md5`z`fDieIkNJd8`Hau` zf-m`sulXO}@Gal*JwNaxKk+la@GHOZJAd#efAKf}@Gt-2C%xtUtgpPE_LTQ}zwnB$ z@R+~wsITyKzwnX2aN93j`sE9!0u`x5WvWn>YE-8NHK|2y>QI+@)TaRrX~Z5hW>1>1 z7fsolW;CY-Eont-+R&DEw5J0d=|pF`(3Ng4~#Kl~~rCi44 zT)~xG#noKHwOq&b+`x_8#Le8ot=z`#+`*mPMSlh`kUdpRbJzD z-r!B%;%(mHUEbq;KHx(>;$uGHQ$FK!zTiu~;%olLH+;)?e9sU3$WQ#tFZ{}H{LUZz z$zS}72otoW}9f-$zCRVne1h}9f- z$zCRVne1h}9f-$zCRVne1hC2=qlfF#)GU>~tFO$AZ z`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`c%>VxxZu&Cm z%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cOy44Xi|oU`^kYBv=Kv1mAP(jb z4&^Wo=LnAED30bBj^#Lx=LAmVBu?fOPUSRC=M2u|EY8keCVQFeWwMtkdT!D4IG+o+ zkc+sOOSqKFxST7vlB>9yYq*x{xSkuhk(;=gTey|mxScz=le_5800uIM!3<$2!x+v8 zMly=gjA1O}7|#SIGKtAdNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQ zNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S z(w9kJCViRoWzv^PUnYH->}9f-$zCRVne1hXF615-aW9Km!cvy8oE5BO6{}gpTGp{X zeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^P zUnYH-^kveQNna*?ne=7K_A9#|`*Q#Xau5e|2#0bQhjRo+aui2%499XD$8!QFauO$V z3a4@!r*j5pau#QE4(Dz(59(zD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%iL9@KLZ%Z zAO&aK$t-3whq=sSJ`1>;h1|m;?qxAc zSjsY%vx1eZVl``6%R1JxfsJfpGh5ioHny{ao$O*a_i;ZD@E{NIFpuykkMTH9@FY+1 zG|%uX&+$Aj@FFkqGOzF|ukku>@Fs8ZHt+B*@9}>2GTF;yFO$7Y(GQD$#K(NXr+mie ze8HD|#n=3gZ}^t)_?{p5k)QaPU-*^Z_?C2=qlfF#)GU>~tFO$AZ`ZDRu zq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$7Y`ZDRuq%V`cO!_kE%cL)pzD)Wu z>C2=qlfF#)GU>~tFO$7Y_A=SaWG|DwO!hLxny?p5*_&oGrv)u(MQhs7mUgtK10Cs< zzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~t zFO$AZ`ZDRuq%V`cO!_iqA1wP25Az6*@)(cv1W)o5PxB1V@*L0e0x$9sFY^ko@*1!6 z25<5fZ}SfC@*eN=0Uz=aAM**H@)@7=1z++NU-Lh{;ak4rdw$?Ye&T0-;a7g+cmCi{ z{^D=`;a~ovNVy{AicHNAm8eV=s#1;W)SxD{s7)Q}QjhvHpdpRe zgU0Mh6ZWDhd(({Ow4f!eXiXd1(vJ3Ypd+2=Oc%P+jqdcIC%x!RANsNn`_hm7*q;M9 zkb^jwLpYSfIGiImlA}19V>p)MIGz(Yk&`%?Q#h5=IGr;%le0LRb2vA9ne1h}9f-$zCRVne1hXE#h7lvxKEAV>v5W$tqT} zhPA9?Jsa4VzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=q zlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kA_Ahq;2XYVxa|nlW7>9ENM{*QLa}39F9LIA4 zCvp-ea|)+&8mDsxXL1&2a}MWn9_Mob7jh97a|xGn8JBYfS8^3sa}C#W9oKUMH*ym< za|^d}8@F=@cXAi~8NfgWF_C2=qlfF#) zG6RYZWDtWH!cc}WoDqy<6r&l#SjI7)2~1=XlbOO)rZJrv%w!g`nZsP>F`ot8%|h;B z5%;o~B`jqb%UQunRz;>UW~#i zK}kwcnlhB75Nj__1u9aB%2c5$)u>JlYEmnGne=7Smq}kHeVO!S(w9kJCViRoWzv^P zUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH->}ArINna*?ne=7S zmq}kHeVO!S(w9kJCViRoWzv^PUnYH->}9f-$zCRVne1hC2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=q zlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%anV#+#@{7V?53iJjqi$%`-g9b3D%ryvR$u z%qzUgYrM`IyvbX<%{#oyd%VvFe8@+9%qM)xXMD~Ve92dQ&Hwm@Z~2bz`GFt#iJ$p} zU-^yS`GY_Ci@*7YfBBCh<%^UrN-+w5h*^U4Wzv^PUnYH-^kveQNna*?ne=7Smq}kH zeVO!S(w9kJrbw}(6iy*BQG$|`qBLbFOW}c9;Y)P|DpHBcRG})>s7?)PQj6Nup)U2P zPXij#h&^b`o-|=EnzA>|Xif`S(u&r!p)KubPX{{EiOzJPE8XZ$4|>vz-t?g_`>-$l z*pK}=fCD**gE@plIgGh$~2}kBYm0lWzv^PUnYH-^kveQNna*? zne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVOcK(w9kJ zCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVOcKvX{wTCVQFeWwMtkzL+H}Wf{v^ z!Ae%Knl-Ft9qZY^MmDiIeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kH zeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7KA6Wh%4(1RJlxi$tXrMhOvxeJQJA6BqlS3sZ3)!GnmONW;2Jm%ws+a zxSNIC!y@iwF-us=GM2M~m8@blYgo%V*0X_)Y+^H8*vdAxvxA-NVmJ43KM(LA5AiUM z@Fru_RjEdGYEY9})TU1QGU>~tFO$AZ`ZDRuq%V`c zO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu*~_Fa zlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu*~?@vlf6v#GTF;yFH@o!&1peP zTG5&|w51*G=|D$1(U~rErCa(k>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)p zzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%anh#{9`=M6FkXNJk2va%X2)> z3%tlnyv!@S%4@vN8@$O|yv;kj%X_@f2Ykp!e9R|&%4dAe7ktTAe9iy(hHv?f@A-ir z`H7$Tgv8qknN>_KDpqzQY`l)Y(2b6U`nRY(34*DrVoAD zhkfbCe(cWy9LPZ&%pn}gVI0m89LZ4}%`qIyaU9PHoXAO>%qg78X`Id(oXJ_7%{iRQ zd7RG$T$sH~_A=SaWG_?V;u4o|DVK3MS8yd)aW&U)E!S~9H*h02aWl7YE4OhwcW@_n z(VqbfWDtWH!cc}WoDqy<6r&l#SjI7)2~1=XlbOO)rZJrv%w$&jGU>~tFO$AZ`ZDRu zq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu z*~_FalfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu*~?@vlf6v#GTF;yFH>SE z%UI3|RC2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE z%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%TzeH!XX^WVI0m89LZ4} z%`qIyaU9PHoXAO>%qg78X`Id(oXJ_7%{iRQd7RG$T*yUS%q3jPWn9h`T**~j%{5%h zbzIL4+{jJb%q`r?ZQRZs+{s<^X8;2k#9)Rnlwk~K1nJA9FO$AZ`ZDRuq%V`cO!_kE z%cL)pzD)Wu>C2=qlfKN5;zJq6a7HkaQH*8`V;RSICNPmnOlAsGnZ|TxFq2u#W)5?i z$9xuWHw(FkMcm6`mavp%EN2BPS;cDBu$FbKX9FAA#Addzm2GTi2RqrtZtmlL9^gS9 z;$a@)Q6A%Qp5RHI;%T1YS)Sv0Uf@Mu;$>dpRbJzD-r!B%;%(mHUEbq;KHx(>;$uGH z)9hukm&slxdzliSm-vD&`HHXkAK&mT-|;;^@FPF*Gr#aFzwtYN@F#!qH~;W2|52o5 zk&;CzMj}9f-$zCRVne1gs zwxA`gXiXd1(vJ3Ypd+2=Oc%P+jqdbFUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJ zCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViO-k5_ntCwYped4{4z ziWVtGaZ2DXCySJ#G-W7DIm%Okid3R9Rj5ies#AlS)S@3LY)@Y%eVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*? zne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?nId-J37{)S=@l0SMlbFmDrZSD`%wQ(7n9UsKGLQKz;BFRj4~w{$#Vlbd%UI3| zR~tFO$AZ`ZDRuq%V`cO!_kE z%REy2k>Zc?7?1M=Px2H`^9;}O9MAItFY*#E^9rx>8n5#PZ}Jvz^A7Lw9`Ex3AMz0& z^9i43FO$7Y_A=SaWG|DwO!hL_%ar`GC2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu z>C2=qlfF#)GU>}?FO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qlf6v#GTF;yFO$7Y z_A=SaWG|DwOsQ71rVVXrM|(QZkxq1`3tj0(cY4s1Ug^uEFO$AZ`ZDRuq%V`cO!_kE z%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qQ{GbfGKV=uQuM(u>~op)dQ8zD)Wu>C2=q zlfF#)GU>~tFO$AZ`ZDRulqgY>!XM3-rVM2%N8w?51u9aB%2c5$)u>JlYEp~Z)S)i* zs80hL(uh51%$_u1FPgG9&1g;wTGEQvw4p8SXio<^(uvM=p)1|!P7iw0i{A91FZ-}B z{n(HFIe-H>h=VzVLphAYIf5fOilaG(V>yoFIe`;7iIX{nQ#p;(IfFAfi?cb0b2*Rm zxqu6~h>NqA$zCRVne1h}9f-$zG<^N>;I&HLPVF>)F6YHnEv4Y-JnU*}=~AWzv^PUnYH-^kveQNna*?ne=7S zmq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w8aPzvuu4 zGKj$pVJO2G&Im>_iqVW=EaMo@1ST?x$xLA?)0oZ-W-^P}%waC`n9l<4W+C^mh@Fs8ZHt+B*@9{n#@F5@Z zF`w{h_A=SaWG|DwO!hL_%VaN;y-fBp*~?@vlf6u-uS@-pZ}^t)_?{p5k)QaPU-*^Z z_?B}-C@!YM;p%2A#QRHPD>sX|q%QJospq!zWQ zLtW}op9VCfQTj6J%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)p zzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZC$eq%V`cO!_kE%cL)pzD)Wu>C0pC2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=q zlfF#)GU>|{{jX?|V(H7IFO$AZ`ZDRuq%V`cO!_h4&!i+;7E?*XpZ4nj^lVv;6zU1WKQ8! zPUCdW;7rcqY|i0a&f|P8;6g6q;_PLzm&slxdztKIvX{wTCVQFeWwMvaUM72)(pQ$g zimSPXYq^f=xq%zGiJQ5FTe*$fxq~~oi~bB?AcGjp5QZ|0;f!D;qZrK?#xjoaOkg6D zn9LNWGL7lXU?#Je%^c=3kNGS}UnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViRo zWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*= zne1h}5)?W({ju$9gufkxgu73tQR7c6P9n zUF=R@CViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH- z^kveQNna*?ne=7Smq}lySU=L2Nna*?ne=7Smq}kHeVO!S(w7-lVmKoh$tXrMhOvxe zJQJA6BqlS3sZ3)!GnmONW;2Jm%ws+axSNIC!y@iwF-us=GM2M~m8@blYgo%V*0X_) zY+^H8*vdAxvxA-NVmJ43KM(LA5AiUM@F2FGZ%XfUw5B$ha{LC->%5VJ6AN08$icy?GP_`tcC{5v%r5xp{ zKt(E1nJQGJ8r7*mO=?k_I@F~e^=Uvu8nFkB)0atKCViRoWzv^PUnYH-^kveQNna*? zne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w9kJ zCViRoWzv_)UM72)>}9f-$zCRVne1hGbfGKV z=uQuM(u>~op)dQSFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ z`ZDRuq%V`cO!_kE%cL)pzD)Wu>B|%wSZol38NyJ8F`N;MWE7(r!&t^Ko(W835|f$2 zRHiYV8O&rBvzfzO<}sfI+|5GnVG;MTm?bP_8OvG0N>;I&HLPVF>)F6YHnEv4Y-JnU z*}+bBv77t2p9gr5^kveQNnd72@u3W3I3pOzC`L1ev5aFp6PU;(CNqVpOk+ATn8_?= zGl#j%V?GPGn}yuNBJO1|OIXS>ma~GDtYS55Sj#%rvw@9lVl!LV$~LyMgPrVRH}`Qr z5AYxl@i6Jjq%V`cO!_kE%akfvn!=w=m!%x#sX#?4QJE@Kr5e?#K}~8=n>y5`9`$KJ zLmIIMjoFhX>_t=drWws?K}%ZEnl`kh9qs8rM>^4&E_9_E-RVJ3deNIc^kpCRr62pT zKL>Ci2XQcma43gyI7e_KM{zXAa4g4hJST7>Cvh^Ta4M&9I%jYuXK^;?a4zR@J{NEy z7jZF{aB223*~?@vlf6v#GTF;yFO$7Y_A=SaWG|DwOqr|8T*I|o$MxL6joie|+`_Hg z#_im}o!mu#1~8C83}y&J8OCr%Fp^P>W(;E)$9N_%kx5Ku3R9WJbY?J}9f-$zCRVne1h< zm&slxdztKIvX{wTCVQDOYgxy7Hn5RRY-S5v*~WHuu#;Wv=05IEUnYH-^kveQNna*? zne=7Smq}kHeVO!S(w9kJCViRoWzv^PUnYH-^kveQNna*?ne=7Smq}kHeVO!S(w8Y- zw0JQJrvxP_MQO@VmU5J*0u`x5WvWn>YE-8NHK|2y>QI+@)TaRrX~Z5hW>1>17fsol zW;CY-Eont-+R&DEw5J0d=|pF`(3NgC2=qGos{3MlqT(jAb0-nZQIQF_|e$Wg63&!Axc`n>oy79`jki-7MrD7I80& zS;A75v78mGWEHDf!&=s{o(*hd6Pww>R<^O79qeQmySb11d4LCbh=+NEM|q6Ld4eZ- zil=#oXL*k2d4U&siI;hWS9y)sd4o53i??})cX^NZ`G61kh>!V%Px*|``GPOAm&slx zdztKIvX{wTCVQFeWwMvaUM72)>}AS)SLSy5`9`$KJLmIIMjoFhX z>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ z`ZDRuq%V`cO!hMA%cL)pzD)Wu>C2=qlfF#)GU>}?FO$7Y_A=SaWG|DwO!hL_%VaN; zy-fBp*~?@vlf6vY4s@gwo#{eXy3w5;^rRQP=|f-kVPE>CFO$AZ`ZDRuq%V`cO!_kE z%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)pzD)Wu>B|%!Tzm*a z8OCr%Fp^P>W(;E)$9N_%kx5Ku3R9WJbY?J3LY-a~M*~M<|<9;6CK_22^(w9kJCViRoWzv^PUnYH- z^kveQDO32X=~Cq=PvJ#cMJiF5DpaK!)u};EYEhdy)TJKvX+T37u?LOWlP2s%Q}(7A z&1pePTG5&|w51*G=|D$1(U~rEr5oMpK~H+on?CeqANHjm`>{UC2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL)p zzD)Kq>C2=qlfF#)GU>~tFO$AZ`ZDRuWG|DwO!hL_%VaN;y-fBp*~?@vlf6v#GTF;y zFO$7Y+4XE-Bb(UF7PhjD?d)JDyV%Wr+|L6%n7&N~tFO$AZ`ZDRuq%V`cO!_kE z%cL)pzD)Wu>C2=qlfF#)GU>~tFO$AZ`ZDRuq%V`cO!_kE%cL(;qIikIDM=|xQ--pX zqwp}j0u`x5WvWn>YE-8NHK|2y>QI+@)TaRrX~Z5hW>1>17fsolW;CY-Eont-+R&DE zw5J0d=|pF`(3NgAZhTiM2TcCeFO?B+i1=K&t%As*%t9_29}=Lw$VDW2vTp5-~7=LKHm zC0^zgUgb4j=MCQEE#BrG-sL^s=L0_EBR=L6KIJn$=L^2%E56QNCVQFeWwMvaUM72) z>}9f-$zCRVne1hC`;j#rveqJL}jW_m1}ArINna*?ne=7Smq}kHeVO!S(wE6zCVQFeWwMvaUM72)>}9f-$zCRVne1hfHtY9UpSj`&NvX1p^ zU?ZE@%oet?jqU7UC%f3qecaCjJjg>l%p*KX`ZDRuq%V`cO!_kE%cL)pzD)WuQ z0u`x5WvWn>YE-8NHK|2y>QI+@)aU=$y9YKf3pNbH3-hYkw$0XTR@>65W!ttj+g59B z3rn++ZQJ^Cedqqi{hWW|#c>^XRjN^)8q}l~wW&j0>QSEtG^7!YX+l$)(VP~vq!q1c zLtEO>o(^=R6Nl27!{|a+y3w5;^rRQP=|f-o(VqbfWDtWH!cY$92##bJ!x_O*9L+Ht z%W;h4cuwF%MsX4+a|)+&8mDsxXL1&2a}MWn9_Oc*NiUOLCcR90ne;O0Wzx%}mq{;^ zUM9Uvu}h0x#^qeWm0ZQuT*I|o$MxL6joie|+`_Hg#_im}o!rIU+{3-x$NfCOXvQ#> zag1jI6Pd(hrZAOhOlJl&nZ<18Fqe7EX8{XYlzo}(%Vb|B`!d;=$-YeXWwI}meVOdb zWM3xxGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56FOz+l?8{_dCcRAdWwI}meVOdb zWM3xxGTE2OzD)LI(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYNL6@FQSEtG^7!YX+l$)(VP~vq!q1cLtEO>o(^=R z6Nl27!{|a+y3w5;^rRQP=|f+#FOz+l?8{_dCi^nkm&v|N_GPj!b6?^6d4SQ3VJzbq z&jcniiOEc1D$|(G3}!Nm+00=s^O(;97P5%NEMY0jSk8m2;2~D>FsoS28XnsZeQHnNG$Y+)8n5#PZ}Jvz z^A7Lw9`Ex3AMz0&^9i5w8K3h7U-A`S^9|pomq{;^UM9UvdYSYx>1EQ(q?buAlU^ph zOtGJf{lc&OkKg#6Klqcs_?v(Dm;WeGyg>2&*q;M9kbNKe3$brdwg^Qj#=a}TK^)8> zl%y1;DMMMxQJxA^q!N{>LRG3!of_1n7PYBEUFuOk`!d;=$-YeXWwI}meVOdbWM3xx zGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56FOz+l?8{_dCi^nkm&v|NdYSCYWM3xx zGTE2OzD)LIvM-Z;ne5A?mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GR51{j`nn* zBb_*u&KyP;y3&pA^q?ob=$(C;?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeX zWwI}meVOdbWM3xxGTE2OzD)LIvM-Z;ne5ACUncu91@A0)7k6_H_i`Wi^8lk6!&t^K zo(W835|f$2RHiYV8O&rBvzfzO<}sfIEMyUjS;A75v7854!9%R%VOFu4H9W$jJjUa! z1EQ(q?buAlU^ph zOnRB(mlwZ+E4hlRxrS@Gj_bLB8@Y*_xrJM~joZ0{JGqOyxrckXkNbIm(Trg%;~38b zCNhc1Okpb1n9dAlGK<;FVJ`ES&jJ>*h{Y_)zD)LIvM-Z;ne5ACUncu9*_X+_O!j56 zFOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeXWwI}mUMBl8*_X+_O!j56 zFOz+l?8{_dCi^n!Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phO!3EfoV7f`lRU-K zJi|KHvw@9lVl!LV%C_vwWM3xxGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56FOz+l z?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=DO9*n5sFfb;_Um8b`S@12qh^+Y06NR za+Ie66{$pJs!)|`RHp_tsYPw-P?vhtrvVLVL}QxJlx8%i1ubbsYueD3cC@Dh9qGiO zbmlO+(3NgC9jzvzW~s<}#1@EMOsvSj-ZZvW(?C$O;}}B@eTT)vVzW9_29}XDv_g zBv0`)&#;d5Y+xgs*vuBTvW@NRU?;oS%^vphEYI;gFYqES@iMRQDzEW6Z}28>@iy=9 zF7NR^AMha`@iCw9DWCB1EQ(q?buAlU^phOnRC0GU;W~ z%M|~$`2YBg-}!?-`HR2#hkyBx0woHR*pK}=fCDMWz7PF{DMC?-QJj5u5C?MzB`HN| z%21Yal&1m}sYGR}P?c&_rv^2tMQ!R(mwMEv0S&V+lYN=&%Vb|B`!d;=$-YeXWwI}m zeVOdbWM3xxGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56FOz+l?8~H=$-YeXWwI}m zeVOdbWM3xxGTE2OzD#oy79`jkiLKd-@B`jqb%XyF$Jj6;KW)-Vh!y`P(V?54Up5RHI z;%S~?9qZY^MmDjTEo@~Q+u6ZRc9DIV?8{_dCi^nkm&v|N_GPj!lYNQ3J zl%*WysX#?4QJE@Kr5e?#K}~8=n>y5`9`$KJLmJVTCN!lP&1pePTG5&|w51*G=|D$1 zaVVWRj4pJg8{O$aPkPatKJ=v@{TaYO1~Hf+4CQc+;7EosoDm$w(Hz6E9LGqG=LAk< z6en>qr*JB#aXM#kCTDRr=Ws6PaXuGtAs2CRdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%apjX#8q6)HC)SeT+a>M$W7eLE!@g&+|C``$z9ydJ>1KE+|L7yW(;E)$9N_%kx5Ku z3R9WJbY?J-EM^HyS(bg7?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B z`!d;=$-YeXWwI}meVOdbWM3xxGTE2OzD)LIvM-Z;ne5ACUnadw_GPj!lYN=&%Vb|B z`!d;=$-YeXWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnR9TYk7hvd5WibhIOoG z0~^`IX11`EZER;p_GPj!lYN=&%Vb|B`!d;=$-YeXWwI}meVOdbWM3xxGTE2OzD)LI zvM-Z;ne5ACUncu9*_X+_O!j56FOz+l?8_7`TDTa+DZxP;%psJd6s0LcS;|qK3RI*L zm8n8is!^R9)T9=*sY6}rQJ)4hq!Ep2LQ|U2oEEgC6|HGQTiVf{4s@gwhtiqD=t5Vz z(VZUjq!+#ELtpyQp8*Uc`!d;=$-YeXWwI}meVOdbWM3xxGGmI4WgO#~z(gi7nJG+V z8q=A)F6YHnEv4Y-JnU*}+bBv70^Y84j-r{ZE;a%S2eLmnr zKH_6O;Zr{2bH3n9zT#`X;ak4rdw$@@^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mnqPs zKvSC0oEEgC6|HGQTiVf{4s@gwhtiqD=t5Vz(VZUjq!+#ELtpyQp8*VH5Q7=QP!8t^ zj$|0a8NpE;%`qIyag5}6PT)jFWnU)yGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56 zFOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=NiUOqne5ACUncu9*_X+_O!j56 zFOz+l^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UZ%kLT)>4~#Kl~~rCi44T)~xG z#noKHwOq&b*_X+_O!j56FOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeX zWwI}meVOdbWM3xxGTE2OzD)LIvM*Ek-op2BKMydPF^pv#ru_ zRjEdGYEY9})TRz~sYiVp(2zznrU^}HMsr%wl2){)4Q**hdpgjOP8>>S4xY z(34*DrVoATM}Gz|kU1EQ(q?buAlU^phOnRC0G6hx?c!-re z%qmv1hDUgm$9SBzJi(JZ#nU{)I@Ys+jcj5wTiD7rwzGqs>|!^2*vqp#$Md|vi@e0k zyuz!z#_PPno4m!_yu-V^$NSlr$-YeXWwI}meVOdbWM3xxGTE2OzD)LIvM-Z;ne5AC zUncu9*_X+_O!j56FOz+l?8{_dCi^nkm&v|N_GQw`WM3xxGTE2OzD)LIvM-Z;ne5AC zUnadwdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPen@Gal*JwNaxKk+la@GJl0H-6_2 z{^T$I=AZ1#WM3xxGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_O!j56FOz+l?8{_dCi^nk zm&v|N_GPj!lYN=&%Vb|B`!d;=DN?*h2@c|54xuEaC`}p4QjYRepdyv1Ockn9jq22( zCbg(d9qLk#`ZS;+jc800n$nEsw4f!eXiXd1(vJ3Ypd+0)l+GMR7rN4o?)0E1z35FJ z`qGd73}7IG7)*h{Y^nDa%;SgRI~oR`M{bSj`$9;ZYvran|w#Px2H`^9<`)&jvQKiOp z^95h>6<_lW-|`*b^8-Kf6F;YyNiUOLCcR90ne;O0Wzx%}mq{;^UM9WFe$Do4P77Mn ziq^EDE$wJe2RhP;L+Q+6bfGKV=uQuM(u>~op)dXD&j1E8h`|hDD2H34zjNmAa z<`|CUI7V_jCvYO8IEj<9FOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeX zWwI}meVOdbWM3xxGTE2OzD)LIvM-Z;ne5A?m&v|N_GPj!lYN=&%Vb|B`!d;=$-Yc_ zne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAv)_eW#Kl~~rCi44T)~xG#noKHwOq&b z+`x_5m&v|N_GPj!lYN=&%Vb|B`!d;=$-YeXWwI}meVOdbWM3xxGTE2OzD)LIvM-Z; zne5ACUncu9*_X+_O!j56FH_|HA`dW{F^pv#HNAm8eV=s#1;W)SxD{ zs7)Q}QjhvHpdpQDOcR>YjOMhUC9P;p8`{#2_H>{loj8=v97Y$q(v9x)peMcPO&|Kw zkNyl`AcGjp5QcI%M{p#=7|sZe;%JWHSdL>P$8!QFGK!NpnNv8G(>R?oIFqwDn{zmq z^EjUixR8sum`k{n%eXwfOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX_FK8%!>nR8Yj}i5 zd5p(d%M(1wQ#{QxtYbYJ*vKX}vxTi}V>>(8$u4%YhrK+@b3D%ryvR$u%qzUgYrM`I zyvbX<%{#oyd%VvFe3*Ti?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeXWwI}m zeVOdbWM3xxGTE2OzD)LIvM-Z;ne5ACUnadw_GPj!lYN=&%Vb|B`!d;=$-YeXWzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC9zUK#iMt6G9lV0?u4}IxJe+Dp+ zK@4UHL&?5O_GPj!lYN=&%Vb|B`!d;=$-c~l;uD#~WTr5cX-sDZGnvI~<}jCe%x3`$ zS;S(Nu#{yi=RsER5G#3@Rjg(WkMJmu@i=RFf+u;3r+J2TtY-ro*~DhHu$66WX9qjk z#cuYnmuGp7=Xrq_d5M>Kg;#lv*Lj0Cd5gDshj)38_xXSi`G}ACgira5&-sEc`HHXk zhHv?f@A-ir`H7$Tg zo(^=R6Nl27!{|a+y3w5;^rRQP=|f-o(VqbfWDtWH!cY$92##bJ!x_O*9L+Ht%W;h4 zcuwF%MsX4+a|)+sUncu9*_X+_O!j56FOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B z`!d;=$-YeXWwI}meVOdbWM3xxGTE0&FOz+l?8{_dCi^nkm&v|N_GPj!lYN==GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLX8()1giE=M%ejIpxr(c~hHJTw>$!m&xrv*z zFOz+l?8{_dCi^nkm&v|N_GPj!lYN=&%Vb|B`!d;=$-YeXWwI}meVOdbWM3xxGTE2O zzD)LIvM-Z;ne5ACU#95jqGK4#IL0%9iA-WLQ<%y$rZa4~ z#Kl~~rCi44T)~y;Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAv;V67SF?skc$CL@oV7f` zlRU-KJi|KHvw@9lVl!LV$~LyMgPrVRH+$I2vpmQ1yugdR#LK+GtGveRyuq8i#oN5Y zyS&Hye87i%#K+l}$-YeXWwI}meVOdbWM3xxGTE2OzD)LIvM-Z;ne5ACUncu9*_X+_ zO!j56FOz+l?8{_dCi^nkm&v|N_GQw`WM3xxGTE2OzD)LIvM-Z;ne5ACUnadwdYSYx z>1EQ(q?buAlU^phOnRC0GU;W~%cPgt|3`k}XMW*V{>N|p&L8~AU;NEK{L6n7IG})a znf(sfpM7Uy;(&q_qA*1$N->I4f`d4iLnuipN>hfil%qTqs7NI$Q-!KjqdGOHNiAwq zhq~0GJ`HF{BO23$rZl5DEoezATGNKMw4*&8=tw6Hr89@og|2j?J3Z)0FM895zVxF% z0~p941~Y`A9L^CO$uNd9f}=Q^V>p)M7|HRRz=@3FBu?fOPUSRCPcM^RCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^ph%mJ5j8JBYf zS8^3sa}C#W9oKUMH*ymGtmI)<{8$p zo(*hd6Pww>R<^O79qeQmyV=8Dp5-~7=LKHmC0^zgUgb4j=MCQEE#BrG-sL^s=L0_E zBR=L6K20x^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-z03hW^9#T7KYrtP{@_pk;&1-pU;d-OfdvlSkNw#D zP6-a;U=E=qr6^4q%2JN1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0G6!DH61B_-2V;RSICNPmnOlAsGnZ|TxFq2u#W)5?i$9xvBkVPzJ z2}@bVavo#_53!PmS;cDB@Cc9c7>~1-CwP*lc$#Ne$9gufkxgu73tQR7c6P9nUF>EL zdwG`Uc%Bz{k(YRxS9q1zc%3(Rlec)AcX*fgc%KjWkdOG7PxzG2_&mK#dYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fCwj%K!L{ z-}!?-`HR2#hkyBx0tE{c+>iY^fPLdq`>qg$DMC?-QJfMS#K9awNlH=yOIp#IHngQ3?dd>AI&mnSIgBoJ zr5oMpK~H+on?CfVAN?7?Kn5|GAq?ekj^Id!F`N+`#nBwYu^h)pj^_kUWE3ZHGN*7V zr*S%Ga3*JQc6yogGU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0WeQ%&Rb0(AT+4M_&kfwjP29{a+{$g-&K=yzUEIw*+{=C3&jXBR z3}YF`cqTBBNla!6Q<=teW-yak%w`UAna6w3hgrpH*6;|A z@)(b^mM3_Ur+AuYSjT!cu#rt{W(!-{#&&kFlU?j)4|{o*=XjnMc#)TQnOAs~*La;b zc$2qyn|FAZ_jsQV_>hnIm{0hW&-k1#_%gjrdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fCp1<9GhxPyXU>{^4K#qd=hoh4y2A z4&XrcjZ5vj!W5w>#VAe*4&q=Ap(Ld!O&Q8kj`CEXB9*926{=E=>eQenwWv)U>QayT zG@v1kXiO8D(v0S`pe3znO&i+Mj`nn*Bb_*u&KyP;y3&pA^q?ob=uIE`(vSWOU?77S z%n*ihI7e_K!x+v8j^b#J;aHAiB*$|CCo+nYIGIy8mD4z#GdPp8IGb}gH@!@Hne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GKH?@ z8m{F!uIC1BZs!i}!9`5Bn?&kqUGlsE@V>}a>$Rs8+g{e$qIy0Eb zEM_x@xy)le3s}e^7PEw3%tlnyv!@S%4@vN8@$O|yv;kj%X_@f2Ykp!e9R|& z%4dAe7ktTAe4Sn1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYM9h@F#!qH~;W2|52cDfx`Q-KL>Ci1=%+)weN~hlwuU81P5_2 zhftDIl%@=2DMxuKP?1VhrV3T5Ms;dXlUmfK4t1$VeHze^Ml_}gO=(7RTF{bKw5APh zX-9iH(2-6YN@otE3tj0(cY4s1Ui799ed$Mk1~8C83}y&JIh-Rnl3@&I1V?c+$8api zF_PmsffE_UNu10noXTmO&KaD^S)9!|oXdHfpI#=tOnRC0GU;W~%cPe{FOyy-y-a$U z^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0 zGU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90nZnm{9oKUMH*ymGtmI)<{8$po(*hd6Pww>R<^O79qeQmyV=8Dp5-~7 z=LKHmC0^zgUgb4j=MCQEE#BrG-sL^s=L0_EBR=L6KIJn$=L^2%E57C%zD+NaUM9Uv zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90 zne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-eZ1 z_?v(Dm;WeGq(G7V*q;M9kb)Fq-?-GiD@rknQ-Xsym_sN@DN0j@vXrAd6{tuhDpQ53 zRHHgIs7WnqQ-`|LqdpC2NFy54gr+p3IW1^OD_YZrwzQ)?9q33W4y7}P(S@#bqdPt5 zNiTZShraZqKLZ%ZAO1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9Uv zdYSYx>1EQ(q?buAlU^phOp)ukfg8Dro4JKsxsBVogFCs4ySayZxsUsKfYFR$EaMo@ z1ST?x$xLA?)0oZ-W-^P}%waC`n9l+hvWUejVJXX4&V#JrAy)D*t60q%9^p|Q<8jvV z1W)o5PxB1xSkDGFvWd-XVJq9%&JK36i{0#DFVFHE&+`H=@)9re3a|1Suk!|P@)mFN z4)5|F@ACm4@(~~N37_&ApYsJ@@)ck64d3z|-=~*JFOyy-y-a$U^fKvX(#xcmNiUOL zCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy- zy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UZ%)D{L6n7C|aQCe(cWy97sV5 zQJ8(>Qv0qL#VNr-9LynQSEtG^7!Y zX+l$)(VP~vq!q1cLtEO>o(^=R6Nl27!{|a+y3w5;^rRQP=|f-o(VqbfWDtWH!cY$9 z2##bJ!x_O*9L+Ht%W;h4cuwF%MsX4+a|)+&8mDsxXL1&2a}MWn9_Mob7jh97r1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOL zrs$2_#Le8ot=z`#+`*mP#ogS)z1+wBJiutiFqUzQX95$M#AK#0m1#_81~Zw(Z00bR zdCX@43t7Zsmavp%EayR1@DM9`m{qK14Uh0BkMTHbd4eZ-il=#ob*yIt8`;EWwy>3L zY-a~M*~M=5u$O0fj^}xS7kP=7d4*Sbjn{dDH+hS(_AJfaEmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{ zFOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FH`hC3KT0)Y(Mtr01l)eg(yrB_Ki#JyW*7KAP(jbN>Yl_ zl%Xu;C{G0{Qi;k`p(@p=P7P{Oi`vwoF7>ES0~*qZ#x$WR&1g;wTGEQvw4p8SXio<^ z(uqUq%wcq)E8XZ$4|>vz-t?g_{pimC1~Q1j3}GmTa|A~+jNy#nD30bBj^#K;ay%z+ zBBMBolR1S`IgQgfgEKjcvpI)zIgj(XfD5^Zi@AhL)61lnNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAQ|xAL;Z|nV;IXg#xsG5Oky%qn94M!GlQATVm5P_%RJ_@fQ2k#F-us=GM4ioD|m>N zJj^OqvxY}_l*f3SwLHO-JjK&I!#dWpfsJfpGh5ioHny{ao$O*ad)UjfJje6Az>B=Z z%e=y?yvFOi!JE9r+q}cOyvO@|z=wRq$9%%4e8%T|!Iyl+*L=gbe8>0vz>oaI&*^2- z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmnps<`*Q#XQjkIvrU*sZH!ijBN^lSda|k6VMQO@VmU5J*0u`x5WvWn>YE-8NHK|2y z>QI+@)TaRrX+&e1(3EC0rv)u(MQhs7mUgtK10Ctap>*ajy3mzwbf*VB=|yk)(3gJn zX8;2k#9)Rnl*2iKBN@hUMsO5Ia}39F93wfN6F8AkoW#kT!l|6b>72otoW_V8%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0Wzx%}mnnWLw{bgna3^)F6YHnEv4Y-JnU*}+bBv70^Y84j-r{ZE;a%S2eLmnr zKH_6O;Zr{2bH3n9zT#`X;ak4rdw$?Ye&T0-;n(ys>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#w=MfCDK=AqrE3q7-A_xYWKo zh=VzVl9Zw}WhhHI%2R=gRH8Cfs7f`eQ-hk+qBeD?OFin-fQB@pF->SnGn&(amb9WZ zZD>n7+S7rKbmCAta~NIdN;kUGgP!!FH+|?!Kl(F(fed0WLm0~89Kn$cV>lx?ilaG( zV>ynI9M1`y$S6+YWKQ8!PUCdW;7rcqY|i0a&f|P8;6g6qVlLrQF5_~p;L7we>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#w>% zojbUbySSTsxR?95p9dJt7{)S=@l0SMlbFmDrZSD`%wQ(7n9UsKGLQKzU?GcG%o3Ke zjO9GY3Lau553`EZtl<$J?d zf&B{X&jB1rK?+frA{3<<#VNr-9LynQSEtG^7!YX_8(hy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx z>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYJ;9IF!yDMi;u$jqdcIC%x!RANtad{tRFsgBZ*ZhH^MZa3sSR z&Ipd;XpZ4nj$9yYq*x{xSkuhk(;=gTey|mxScz=le@T^d$^bTxSt0Y%^1cqj`2)jB9oZR6s9tb z>C9jzvzW~s<}#1@EMOsvSj-ZZvW(?C$cpqb>1EQ(q?buAlU^phOnRC0GU;W~%cPe{ zFOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#sThil=#ob*yIt8`;EWwy>3LY-a~M z*~M=5u$O0fj^}xS7kP=7d4*Sbjn{dDH+hS(_ANh%&`GsHkAHVTCfAA-N@i+hQFaKd$YQKHAKL>Ci1t~;ficpkd?0c{- z!9g6%A(W&Pr71&M%2A#QRHPD>sX|q%QJospq!zWQLtW}op9VCf5shg=Q<|lhNiUOL zCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy- zy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU`=O z&KyP;y3&pA^q?ob=uIE`(vSWOU?77S%n*ihI7e_K!x+v8j^b#J;aHAiB*$|CCo+nY zIGIy8mD4z#GdPp8IGb}gm-9HE3%HPrxR^`0l*_oBE4Y%YxSDIYmg~5l8@Q31xS3nH zmD{+TJGhg(xSM;pm;1P%2N=y5#xjoaOkg6Dn9LNWGL7lXU?#Je%^c=3kNGTMA&Xed z5|*-z1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;XZdxmwaX9FAA#Addzm2GTi2RqrtZuYR3XL*k2d4U&siI;hW zS9y)sd4o53i??})cX^NZ`G61kh>!V%Px*|``GPO`im&;GZ~2bz`GFt#iJ$p}U-=)u z@jHL;Cx7ub|L`yWQDFZ9`=+J#-**RaAO$HzVTw?cViczY2XQcmP?A!VrVM2%M|mnx zkxEpi3RS5_b!t$PTGXZvb*V>v8qknNG^PnnX-0Eeq?buAlU^phOnRC0GU;W~%cPe{ zFOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLX8$g9r5oMpK~H+on?CfV zAN?7?Kn5|GAq?ekj^Id!F`N+`#nBwYu^h)pj^_kUWE3ZHGN*7Vr*S%Ga3*JQHs^3I z=W#w4a3L3QF_&;DmvK2)a3xo9HP>)0*Ks{Ja3eQyGq-Rnw{bgna3^1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zW%ggs1~#&Z&1_*S+t|(ycCw4z>|rm@@*L0e0x$9sFY^ko@*1!625<5fZ}SfC@*eN= z0Uz=aAM**H@)@7=1z++NU-J#$@*Usv13&T;Kl2N}@;`p#cmCi{{^D=`;a~ovzySpg z*pGeVQv2>e3Q~x|6rm`^D9*l@>Vr6#LnuipN>hfil%qTqs7NI$Q-!KjqdGOHNiAwq zhq~0GJ`HF{BO23$rZl5DEoe!r^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9Uv zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90 zne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(9MFyK^q?ob=uIE`(vSWOU?77S%n*ihI7e_K z!x+v8j^b#J;aHAiB*$|CCo+nYIGIy8mD4z#GdPp8IGb}gm-9HE3%HPrxR^`0l*_oB zE4Y%YxSDIYmg~5l8@Q31xS3nHmD{+TJGhg(xSM;pm;1P%2N=y5#xjoaOkg6Dn9LNW zGL7lXU?#Je%^c=3kNGTMA&Xed5|*-z1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^ph%mEwO#Addzm2GTi z2RqrtZuYR3XL*k2d4U&siI;hWS9y)sd4o53i??})cX^NZ`G61kh>!V%Px*|``GPO` zim&;GZ~2bz`GFt#iJ$p}U-=)u@jHL;Cx7ub|L`yWQQ*J=2kyuI>>HQbcLga#VTw?c zViczY2XQcmP?A!VrVM2%M|mnxkxEpi3RS5_b!t$PTGXZvb*V>v8qknNG^PnnX-0Ee z(2`cPrcHX8^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>17V=K~H+on?CfVAN?7?Kn5|GAq?ekj^Id!F`N+`#nBwYu^h)pj^_kU zWE3ZHGN*7Vr*S%Ga3*JQHs^3I=W#w4a3L3QF_&;DmvK2)a3xo9HP>)0*Ks{Ja3eQy zGq-Rnw{bgna3^1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnR9EH?xJUY-2k+*vT$-vxmJr%X2)>3%tln zyv!@S%4@vN8@$O|yv;kj%X_@f2Ykp!e9R|&%4dAe7ktTAe9bp}%XfUw5B$ha{LC-> z%K!L{-}!?-`HR2#hkyBx0tE{c+>iY^fPLdq`>qg$DMC?-QJfMS#K9awNlH=yOIp#IHngQ(dYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX3ihHm zedtR+`ZIum3}P@t7|P)s!I2DOI3qZUqdA6SIgXJW&k3B!C{E&JPT^Ee<8;p8OwQtL z&f#3n<9sgQLN4NBF5yxx<8rRxO0ME+uHjm)<9cr3MsDI}ZsAsL<96=gPVVAv?%`hU z<9;4sG-DXcIL0%9iA-WLQ<%y$rZa1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0G6lD?jqU7UC%f3q9`^Dq&+$Aj@FFkqGOzF|ukku>@Fs8ZHt+B* z@9{n#@F5@ZF`w`$pYb_g@FidIHQ(?p-|;;^@FPF*Gr#aF|Km4)=MVnmFaG8q{^dUk z6e>_?KlbMU4rJfB)V?cB5sFfb;*{VZ4(1R_Qi{@)p)BPnPX#JciON)=D%Ge?4Qf)0 z+SH*g^{7t+8q$cyG@&WYXif`S(u&r!p)KubPlxm}>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#sU;LtpyQp8*VH5Q7=QP!8t^ zj$|0a8NpE;%`qIyag5}6PT)jFaS|tU3a4@!r*j5pau#QE4(D1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx$O z+RhGkvWwm9VK2|}9MAItFY*#E^9rx>8n5#PZ}Jvz^A7Lw9`Ex3AMz0&^9i5w8K3h7 zU-A`S^9|qf9pCcDP6-a;U=E=qr6^4q%2JN1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAQ@9`f8NfgWF_<9?<#3MRNQN<-5gf(Q9K*33$4HLn z1WsfWCvh^Ta4M&9I%jYuXK^;?a4zR@J{NEy7jZF{a4DB@IahEcS8+Aha4pwyJvVS8 zH*qt!a4WZQJ9ls=cX2oOa4+|9KMydPF^pv#1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-eYq>|!^2*vqp#$Md|v zi@e0kyuz!z#_PPno4m!_yu-V^$NPN1hkV4xe8Q)E#^-#&mwd(7e8abV$M^iekNm{X z{KBvNkKg#6Klqcs_?v(Dm;WeGq(G7V*q;M9kb)Fq-?-GiD@rknQ-Xsym_sN@DN0j@ zvXrAd6{tuhDpQ53RHHgIs7WnqQ-`|LqdpC2NFy54gr+p3IW1^OD_YZrwzQ)?9q33W z4yAK?ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy- zy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^ph zOnRC0GU;WC3}7IG7|alcayUnDB*Pfa2#(@tj^S92V^bB z!f28T_RhS=TJst9IAff>zwu9DD$|(G3}!Nm z+00=s^O(;97P5%NEMY0jSe{-cy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOL zCcR90ne;O0Wzx%}mq{;^UM9UvdYJ>>;a%S2eLmnrKH_6O;Zr{2bH3n9zT#`X;ak4r zd;Z4{{K!xI%rE@PZ~V?5{K*>rVlC@f&jvQKiOp6s8D8*>_xO--=U$k{rlE9Lymc%3&PN5gf@;9L+J5qBLbFOF7C@fr?b3 zGF7NbHL6pCn$)5;b*M`{>eGORG@>z0Xi77h(}I??qBU)3OS|+k>1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9Uv zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#sS(jng@UGdYX1 zIfrvOkMp^J3%Q7kxr9r(jLW%#E4hlRxrS@Gj_bLB8@Y+jbfGKV=uQuM(u>~op)dXD z&j1E8h`|hDD8sm!Tey|mxScz=le@T^d$^bTxSt1jkcW7fM|hOSc$_DAlBal@XLy$9 zc%Bz{k>R|=2u3oB(Trg%;~38bCNhc1Okpb1n9dAlGK<;FVJ`ES&jJ>*h{Y^nDa%;S z3Rb31EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU}CSd%VvFe8@+9%qM)xXMD~Ve92dQ%{P3@cYM$P_<(9K^vK!l4|-;T*w{9L3QbLn%sAhO(5SJQb)&B`Q;es#K#oHK<7~YEy^0)T2HP zXh1EQ(q?buAlU^phOnRC0 zGU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiS3U49?^%&gLA>5+fMNC`L1ev5aFp z6PU;(CNqVpOk+ATn8_?=Gl#j%V?GO5$RZZAgrzKFIV)JnDpseLNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU}Cy2Ykp!e9R|& z%4dAe7ktTAe9bp}%XfUw|M-C)`H7$Tg1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmDREGVgE@plIgG1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOL zCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPenaTaHD z4(D1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90 zne;O0Wzx%}mq{;E;v+uh6F%iLKIaR*oaI&-}u#{KoJ6!Jn+* zFV?b-^=x1xo7l`2wz7@w>|iIm*v%gPW-tHnFaJ@XWPy?eDa3y4PhpBslmjS+pZ$_0 z*tY{Yh=VzVLphAYIf5fOilaG(Qk13)WhqB_Do~M1RHh15sYZ2bP?K8JrVe$fM|~R5 zkVZ772~BB6b6U`nR1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPenc@F1t9_Mob7jh97a|xGn8JBYf zS8^3sa}C#W9oKUMH*yo5=|We!(VZUjq!+#ELtpyQp8*VH5Q7=QP=;|cw{R=BaXWW# zCwFl-_i!)waX%06AP?~{kMJmu@i1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;Epa?}d zfMOJ<1kN`VIFN%lm_s;}!#JEHIFh3{nqw$MY06NRa+Ie66{$pJs!)|`RHp_tsYPw- zP?vhtrvVLVL}QxJlx8%i1ubbsYueD3cC@Dh9qGie9LMpTz=@p1$(+KeoW|*#!I_-J z*_^|G!IfOa)m+21T*vj?z>VBQXS&dpZgi&yJ?TYn`p}nt z^k)DA8N^_Qq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90 zne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U z^fKvX(#xcmNiUOLroesN&jUQjLp;nQJj!D{&J#SzQ#{QxJj-)D&kMZBa9&~rBN@eL z#xRy~jAsH9nZ#tKFqLUcX9hEw#cbv1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%M?6-ViczYB{`6TIG95?l*2fjBRGYE-8NHK|2y>QI+@)TaRrX+&e1(3EC0rv)u(MQhs7 zmUgtK10Ctau^h+ooWO~k#L1k(shq~?oWYr##o3(0xtz!OT)>4~#Kl~~rCi44T)~xG z#noKHwOq&b+`x_8L}$9tm2PyW2R-RUZ~D-ee)MMm0~y3%hA@<2>1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9Uv zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmDfj>n@(>U6 z2#@j@kMjgi@)S?=4A1f$&+`H=GMtwf!AM3inlX%J9OIe5L?$trDNJP=)0x3cW-*&N z%w-<)S-?UTv6v++Wf{v^!Ae%KnwNQnS9y)sd4o53i??})cX^NZ`G61kh>!V%Px*|` z`GPO`im&;GZ~2bz`5!;1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90nL@=WK}ineAP(jb4&^Wo=LnAED30bBN>Q3Jl%*WysX#?4QJE@Kr5e?# zK}~8=n>y5`9`$KJLmJVTCN!lP&1pePTG5&|w51*G=|D$1aV*DiJST7>Cvh^Ta4M&9 zI%jYuXK^;?a4zR@J{NEy7jZF{a4DB@IahEcS8+Aha4pwyJvVS8H_@3cbfp{J=|N9= z(VIT>r62tnz(58um>~>h7&miEdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy- zy-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX3O&TbJi?84j-r{ZE;a%S2eLmnrKH_6O;Zr{2bH3n9zT#`X;ak4rd;Z4{{K!xI%rE@P zZ~V?5{K*>rVlC@f&jvQKiOp1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLX1|gg$Uz*;AsotK z9L^CO$x$55F_fY-WhhHI%2R=gRH8Cfs7f`eQ-hk+qBeD?OFin-fQB@pF->SnGn&(a zmb9WZZD>n7+S7rKbmCZ!<9JTsL{8#lPT^Ee<8;p8OwQtL&f#3n<9sgQLN4NBF5yxx z<8rRxO0ME+uHjm)<9cr3MsA`rUFb?Ty3>Q6^rAO?=u1EPGk}2%VlYD($}n!`7H;LX z^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0 zGU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx z>1EQ(?Dq(d@)(cv1W)o5PxB1V@*L0e0xvS0ml(lFMlqT(jAb0-nZQIQF_|e$Wg63& z!Axc`n>oy79`jkiLKd-@B`jqb%UQunR&&Kky?z@iV{hE5GqOfAA-3_=~lyV?7(#$R;+kg{^F3 zJ3H9PE_SnrzuC(_{L6n7NH3FKCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA zlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^ zUM9UvdYSYx>1EQ(q?buAlU^ph%>D;)Fo$p`hjBPZa3n`@G{;bi(v+brs7?)PQj6Nup)U2PPXij#h{iObDa~k33tG~O*0iB5?PyO2I?{<_IgaBwffG52 zlR1S`IgQgfgEKjcvpI)zIgj(XfD5^Zi@AhLxs1!Xf-AX-tGR}2xsL0(fg8Dr&UB$G z-RMpadeV#D^r0{P=+6KKGKj$pVJO46nOnG(+qj)O(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAv;Si}&J#SzQ#{QxJj-)D z&kMZBa9&~rBN@eL#xRy~jAsH9nZ#tKFqLUcX9hEw#cbvv!UZXm zUM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOL zCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy- zy-a$U!iR7uhjBPZa3n`@G{;bi(v+brs7?)PQj6Nup)U2PPXij# zh{iObDa~k33tG~O*0iB5?PyO2I?{<_IgaBwffG52lR1S`IgQgfgEKjcvpI)zIgj(X zfD5^Zi@AhLxs1!Xf-AX-tGR}2xsL0(fg8Dr&UB$G-RMpadeV#D^r0{P=+6KKGKj$p zVJO46nOnG(+qj)OxRblm%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%} zmq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiUOLCcR90ne;O0Wzx%}mq{;E_z9lmDW2vTp5-~7=LKG5I4?1Rk&I$AV;IXg#xsG5 zOky%qn94M!GlQATVm5P_%RJ_@fQ2k#F-us=GM2M~m8@blFY^ko@*1!625<5fZ}SfC z@*eN=0Uz=aAM**H@)@7=1z++NU-J#$@*UsvKYrjxe&T0-;a7g+cmCi{*61EQ(q?buAlU^phOnRC0GU;W~%cPe{ zFOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UZ%)l9L^CO$x$55F_fY- zWhhHI%2R=gRH8Cfs7f`eQ-hk+qBeD?OFin-fQB@pF->SnGn&(amb9WZZD>n7+S7rK zbmCZ!<9JTsL{8#lPT^Ee<8;p8OwQtL&f#3n<9sgQLN4NBF5yxx<8rRxO0ME+uHjm) z<9cr3MsA`rUFb?Ty3>Q6^rAO?=u1EPGk}2%VlYD($}n!`7H;J>Zs!i}!p7b*5 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%M^Kvr+J2Fd5-6KffpIhON?M7qZrK?#xjoaOkg6Dn9LNWGL7lXU?#Je%^c=3kNGTM zA&Xed5|*-z<*Z;Ot60s;yuz!z#_PPno4m!_yu-V^$NPN1hkV4xe8Q)E#^-#&mwd(7 ze8abV$M^h?ANY}<_?ch$mEZWCKlqb1{KZ<1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(6g`3?If|n>hEkNK3}q=tc`8tmN>ru_RjEdGYEY9} z)TRz~sYiVp(2zznrU^}HMsr%wl2){)4Q**hdpgjOP8`c|9M1`y$Vr^cDV)k_oX#1X z$yuDuIh@ORoX-VZ$VFVtC0xp7T+S6-$yHpM$W3&n3tj0(cY4s1Ui799 zed$Mk1~8C83}y&J8OF`r!mZrK?cBkg+{NA8!@b;>UM9UvdYSYx>1EQ(q?buAlU^ph zOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9Uv zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$UqR;Rw&+$Aj@FK%`i4lxs z6r&l#SjI7)2~1=XlbOO)rZJrv%w!g`nZsP>F`or2WD$#5!cvy8oE5BO6{~rfS9q1z zc%3(Rlec)AcX*fgc%KjWkdOG7PxzG2_?$2JlCSuhZ}^t)_@4jq13&T;Kl2N}@*BVN z2Y<4LzgWvU*0X_)Y+^H8*vdAxvxA-NVmEvEo4x$Qzx+pm0}33l?9LixF&Ji5RQ5?-Nl%h0cC`&oYQ-O+9qB2#eN;RregPPQ$Hg%{= zJ?hhdhBTrvO=wCpn$v=ow4ya_XiGcV(}9k3;#iL3cuwF%PU2)v;Z#oJbk5*R&f;v& z;atw+d@kTZF5+S?;ZiQ+a<1S?uHtI0;aaZadT!uGZlW_?=t?)b(}SM$qBni$OF#NE zfPoBRFhdy1FmC1+Zsj&^=ML`VF7D1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#st19MAItFEX5$7{N$JF`6-q zWgO#~z(gi7nJG+V8q=Art%YPIoR-jlx3b7yiQ1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcm zNiS3E7)nu^GL)qp<*7hLDp8p#RHYi#sX=yOIp#I zHngQ3?dd>AI&mz=aXcq*A}4V&r*JB#aXM#kCTDRr=Ws6PaXuGtAs2BmmvAYUaXD9T zC0B7Z*KjS@aXmM1BRA2RE_9_E-RVJ3deNIc^ravD8NfgWF_<9?Wf(Ve3%7C`w{r(~ zau;`V5BG8(_wxV`@=$u2^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx z>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1B$&z>5s$B}OokQH*8`V;RSICNPmnOlAsGnZ|TxFq2u# zW)5?i$9xvBkVPzJ2}@bVa#paCRjlS^Ug1?<<8|KPP2S>d-r-%|<9$BhLq6hTKH*b7 z<8!{?OTOZ3zTsQG<9q(c5B$ha{LC->%5VJ6AN1EQ(q?buAlU^phOnRC0 zGU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx z>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmDPEc~l%*WysX#?4 zQJE@Kr5e?#K}~8=n>y5`9`$KJLmJVTCN!lP&1pePTG5&|w51*G=|D$1aV*DiJST7> zCvh^Ta4M&9I%jYuXK^;?a4zR@J{NEy7jZF{a4DB@IahEcS8+Aha4pwyJvVS8H_@3c zbfp{J=|N9=(VIT>r62tnz(58um>~>h7&mhZw{jb|a|d^F7k6_H_i`Wi^8gR>5D)W6 zdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90 zne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U z^fKvXiVx={Mlh05jAjgD8OL}gFp)`2W(rf8#&l*dlUdAW4s)5ud={{fMJ#3sOIgNp zR84j-r{ZE;a%S2eLmnrKH_6O;Zr{2bH3n9zT#`X;ak4rd;Z4{ z{K!xI%rE@PZ~V?5{K*>rVlC@f&jvQKiOp6s8D8Ie=p9J1(_vB{`6T(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx z>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAQ=%;8C{G0{Qi;k`p(@p=P7P{Oi`vwoF7>ES z0~*qZ#x$WR&1g;wTGEQvw4p8SXio<^(urd^j^jCj6FG^KIfYX>jng@UGdYX1IfrvO zkMp^J3%Q7kxr9r(jLW%#E4hlRxrS@Gj_bLB8@Y+jbfGKV=uQuM(u>~op)dXD&j1E8 zh`|hDD8sm!Tey|mxScz=le@T^d$^bTxSt1jkcW7fM|hOS(#xcmNiUOLCcR90ne;O0 zWzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~%cPe{FOyy-y-a$U^fKvX z(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAQ(^=o8O3PEFqUzQ zX95$M#AK#0m1#_81~Zw(Z00bRdCX@43t7Zsmavp%EN2BPS;cBz<`rJ$HD2cp-sCOb z<{jSUJ>KU7KI9`l<`X{UGd|}FzT_*u<{Q4{JHF?C{J@X=#LxV~ul&aE{K22B;V;&* zj`eI{Bb(UF7PhjD?d)JDyV%Vh{$?-#@Gt*Spk#rP1u4Y7k9+o~FhwZJ0TiP+`;JTP z+kqU!!5oraCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buAlU^phOnRC0GU;W~ z%cPe{FOyy-y-a$U^fKvX(#xcmNiUOLCcR90ne;O0Wzx%}mq{;^UM9UvdYSYx>1EQ( zq?buAlU^phOnRA;<*7hLDp8p#RHYi#sX=yOIp#I zHngQ3?dd>AI&mz=aXcq*A}4V&r*JB#aXM#kCTDRr=Ws6PaXuGtAs2BmmvAYUaXD9T zC0B7Z*KjS@aXmM1BRA2RE_9_E-RVJ3deNIc^ravD8NfgWF_<9?Wf(Ve3%7C`w{r(~ zau;`V5BG8(_wxV`@(>U62#@j@kMl%≠O0Wzx%}mq{;^UM9UvdYSYx>1EQ(q?buA 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 N(#xcm`Cs)i{{gq6$|e8+ diff --git a/fhi_lib/app/ant_1.bin b/fhi_lib/app/ant_1.bin deleted file mode 100644 index 6cb4fe7470f48bb494b30c38322aa21c7aeb410a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3669120 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 UkSEd0L@yJ)O!PAUPrc0l0l>s=Y5)KL diff --git a/fhi_lib/app/ant_2.bin b/fhi_lib/app/ant_2.bin deleted file mode 100644 index d761f9591d0de83e68f63e2d82d93fe250d46a0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3669120 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 diff --git a/fhi_lib/app/ant_3.bin b/fhi_lib/app/ant_3.bin deleted file mode 100644 index 6962e6063f98f4b6a6901a9070cc4406aad648ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3669120 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 -#include -#include -#include -#include - -#include "../common/common.h" -#include "xran_pkt.h" -#include "xran_pkt_up.h" -#include "xran_cp_api.h" -#include "xran_up_api.h" -#include "../src/xran_printf.h" - - -#define MBUFS_CNT 256 - -extern enum app_state state; - -uint8_t numCCPorts = 1; -/* Number of antennas supported by front-end */ - -uint8_t num_eAxc = 4; -/* Number of CPRI ports supported by front-end */ - -int16_t *p_tx_play_buffer[MAX_ANT_CARRIER_SUPPORTED]; -int32_t tx_play_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; -int32_t tx_play_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; - -int16_t *p_rx_log_buffer[MAX_ANT_CARRIER_SUPPORTED]; -int32_t rx_log_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; -int32_t rx_log_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; - -int16_t *p_prach_log_buffer[MAX_ANT_CARRIER_SUPPORTED]; -int32_t prach_log_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; -int32_t prach_log_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; - -int16_t *p_tx_buffer[MAX_ANT_CARRIER_SUPPORTED]; -int32_t tx_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; - -int16_t *p_rx_buffer[MAX_ANT_CARRIER_SUPPORTED]; -int32_t rx_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; - -void sys_save_buf_to_file(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) -{ - if (size) - { - if (filename && bufname) - { - FILE *file; - printf("Storing %s to file %s: ", bufname, filename); - file = fopen(filename, "wb"); - if (file == NULL) - { - print_err("can't open file %s!!!", filename); - } - else - { - uint32_t num; - num = fwrite(pBuffer, buffers_num, size, file); - fflush(file); - fclose(file); - printf("from addr (0x%lx) size (%d) bytes num (%d)", (uint64_t)pBuffer, size, num); - } - printf(" \n"); - } - else - { - print_err(" the file name, buffer name are not set!!!"); - } - } - else - { - print_err(" the %s is free: size = %d bytes!!!", bufname, size); - } -} - -int sys_load_file_to_buff(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) -{ - unsigned int file_size = 0; - int num= 0; - - if (size) - { - if (filename && bufname) - { - FILE *file; - printf("Loading file %s to %s: ", filename, bufname); - file = fopen(filename, "rb"); - - - if (file == NULL) - { - print_err("can't open file %s!!!", filename); - exit(-1); - } - else - { - fseek(file, 0, SEEK_END); - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - if ((file_size > size) || (file_size == 0)) - file_size = size; - - printf("Reading IQ samples from file: File Size: %d [Buffer Size: %d]\n", file_size, size); - - num = fread(pBuffer, buffers_num, size, file); - fflush(file); - fclose(file); - printf("from addr (0x%lx) size (%d) bytes num (%d)", (uint64_t)pBuffer, file_size, num); - } - printf(" \n"); - - } - else - { - print_err(" the file name, buffer name are not set!!!"); - } - } - else - { - print_err(" the %s is free: size = %d bytes!!!", bufname, size); - } - return num; -} - - -void sys_save_buf_to_file_txt(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) -{ - unsigned int i; - int ret = 0; - if (pBuffer == NULL) - return; - - if (size) - { - if (filename && bufname) - { - FILE *file; - printf("Storing %s to file %s: ", bufname, filename); - file = fopen(filename, "w"); - if (file == NULL) - { - print_err("can't open file %s!!!", filename); - exit(-1); - } - else - { - uint32_t num = 0; - - signed short *ptr = (signed short*)pBuffer; - for (i = 0; i < (size/((unsigned int)sizeof(signed short) /** 2 * 2 * 2*/)); i = i + 2) - { - ret = fprintf(file,"%d %d\n", ptr[i], ptr[i + 1]); - if (ret < 0) - { - printf("fprintf %d\n", ret); - fclose(file); - break; - } - num++; - } - fflush(file); - fclose(file); - printf("from addr (0x%lx) size (%d) IQ num (%d)", (uint64_t)pBuffer, size, num); - } - printf(" \n"); - } - else - { - print_err(" the file name, buffer name are not set!!!"); - } - } - else - { - print_err(" the %s is free: size = %d bytes!!!", bufname, size); - } -} - - -#endif /* _XRAN_APP_COMMON_ */ diff --git a/fhi_lib/app/config_file_lls_cu.dat b/fhi_lib/app/config_file_lls_cu.dat deleted file mode 100644 index 7a17943..0000000 --- a/fhi_lib/app/config_file_lls_cu.dat +++ /dev/null @@ -1,92 +0,0 @@ -####################################################################### -# -# -# -####################################################################### - -# This is simple configuration file. Use '#' sign for comments -appMode=0 # lls-CU(0) | RU(1) -xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) -ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) -##Numerology -mu=3 #mmWave 120Khz Sub Carrier Spacing -antNum=4 # Number of Antennas per CC (default: 4) -ttiPeriod=125 # in us TTI period (mmWave default 125us) -llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF -ruMac=00:11:22:33:44:55 #RU VF for RU app -#ruMac=3c:fd:fe:b1:d8:98 #RU PF for tcpdump -#56:1e:4b:0c:f5:9b # RU MAC -antC0=./ant_0.bin #CC0 -antC1=./ant_1.bin #CC0 -antC2=./ant_2.bin #CC0 -antC3=./ant_3.bin #CC0 -antC4=./ant_4.bin #CC1 -antC5=./ant_5.bin #CC1 -antC6=./ant_6.bin #CC1 -antC7=./ant_7.bin #CC1 -antC8=./ant_8.bin #CC2 -antC9=./ant_9.bin #CC2 -antC10=./ant_10.bin #CC2 -antC11=./ant_11.bin #CC2 -antC12=./ant_12.bin #CC3 -antC13=./ant_13.bin #CC3 -antC14=./ant_14.bin #CC3 -antC15=./ant_15.bin #CC3 - -## RACH TODO: update for PRACH -#rachEanble=1 # Enable (1)| disable (0) PRACH configuration -#rachOffset=43 # RB offset for prach detection (see RIU spec) -#rachCfgIdx=14 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index - -## control of IQ byte order -iqswap=0 #do swap of IQ before send buffer to eth -nebyteorderswap=0 #do swap of byte order for each I and Q from CPU byte order to network byte order - -##Debug -debugStop=0 #stop app on 1pps boundary (gps_second % 30) - -CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled -c_plane_vlan_tag=1 #VLAN Tag used for C-Plane -u_plane_vlan_tag=2 #VLAN Tag used for U-Plane - -##RU Settings -Tadv_cp_dl=25 #in us TODO: update per RU implementation - #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages - -#Reception Window C-plane DL -T2a_min_cp_dl=50 #in us TODO: update per RU implementation -T2a_max_cp_dl=140 #in us TODO: update per RU implementation - -#Reception Window C-plane UL -T2a_min_cp_ul=50 #in us TODO: update per RU implementation -T2a_max_cp_ul=140 #in us TODO: update per RU implementation - -#Reception Window U-plane -T2a_min_up=25 #in us -T2a_max_up=140 #in us - -#Transmission Window -Ta3_min=20 #in us -Ta3_max=32 #in us - -########################################################### -##lls-CU Settings -#C-plane -#Transmission Window Fast C-plane DL -T1a_min_cp_dl=70 -T1a_max_cp_dl=100 - -##Transmission Window Fast C-plane UL -T1a_min_cp_ul=70 -T1a_max_cp_ul=80 - -#U-plane -##Transmission Window -T1a_min_up=35 -T1a_max_up=50 - -#Reception Window -Ta4_min=0 -Ta4_max=45 -########################################################### - diff --git a/fhi_lib/app/config_file_ru.dat b/fhi_lib/app/config_file_ru.dat deleted file mode 100644 index d1edf90..0000000 --- a/fhi_lib/app/config_file_ru.dat +++ /dev/null @@ -1,92 +0,0 @@ -####################################################################### -# -# -# -####################################################################### - -# This is simple configuration file. Use '#' sign for comments -appMode=1 # lls-CU(0) | RU(1) -xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) -ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) -##Numerology -mu=3 #mmWave 120Khz Sub Carrier Spacing -antNum=4 # Number of Antennas per CC (default: 4) -ttiPeriod=125 # in us TTI period (mmWave default 125us) -llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF -#llsCUMac=3c:fd:fe:9e:93:68 # PF for tcpdump -ruMac=00:11:22:33:44:55 -#56:1e:4b:0c:f5:9b # RU MAC -antC0=./ant_0.bin #CC0 -antC1=./ant_1.bin #CC0 -antC2=./ant_2.bin #CC0 -antC3=./ant_3.bin #CC0 -antC4=./ant_4.bin #CC1 -antC5=./ant_5.bin #CC1 -antC6=./ant_6.bin #CC1 -antC7=./ant_7.bin #CC1 -antC8=./ant_8.bin #CC2 -antC9=./ant_9.bin #CC2 -antC10=./ant_10.bin #CC2 -antC11=./ant_11.bin #CC2 -antC12=./ant_12.bin #CC3 -antC13=./ant_13.bin #CC3 -antC14=./ant_14.bin #CC3 -antC15=./ant_15.bin #CC3 - -## RACH TODO: update for PRACH -#rachEanble=1 # Enable (1)| disable (0) PRACH configuration -#rachOffset=43 # RB offset for prach detection (see RIU spec) -#rachCfgIdx=14 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index - -## control of IQ byte order -iqswap=0 #do swap of IQ before send buffer to eth -nebyteorderswap=0 #do swap of byte order for each I and Q from CPU byte order to network byte order - -##Debug -debugStop=0 #stop app on 1pps boundary (gps_second % 30) - -CPenable=0 #(1) C-Plane is enabled| 0 C-Plane is disabled -c_plane_vlan_tag=1 #VLAN Tag used for C-Plane -u_plane_vlan_tag=2 #VLAN Tag used for U-Plane - -##RU Settings -Tadv_cp_dl=25 #in us TODO: update per RU implementation - #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages - -#Reception Window C-plane DL -T2a_min_cp_dl=50 #in us TODO: update per RU implementation -T2a_max_cp_dl=140 #in us TODO: update per RU implementation - -#Reception Window C-plane UL -T2a_min_cp_ul=50 #in us TODO: update per RU implementation -T2a_max_cp_ul=140 #in us TODO: update per RU implementation - -#Reception Window U-plane -T2a_min_up=25 #in us -T2a_max_up=140 #in us - -#Transmission Window -Ta3_min=20 #in us -Ta3_max=32 #in us - -########################################################### -##lls-CU Settings -#C-plane -#Transmission Window Fast C-plane DL -T1a_min_cp_dl=70 -T1a_max_cp_dl=100 - -##Transmission Window Fast C-plane UL -T1a_min_cp_ul=70 -T1a_max_cp_ul=80 - -#U-plane -##Transmission Window -T1a_min_up=35 -T1a_max_up=50 - -#Reception Window -Ta4_min=10 -Ta4_max=100 -########################################################### - diff --git a/fhi_lib/app/dpdk.sh b/fhi_lib/app/dpdk.sh index 96bb84f..5b267bd 100644 --- a/fhi_lib/app/dpdk.sh +++ b/fhi_lib/app/dpdk.sh @@ -18,6 +18,7 @@ #* #*******************************************************************************/ + export RTE_SDK=/home/turner/dpdk export RTE_TARGET=x86_64-native-linuxapp-icc @@ -65,7 +66,57 @@ load_igb_uio_module() fi } +# +# Unloads VFIO modules. +# +remove_vfio_module() +{ + echo "Unloading any existing VFIO module" + /sbin/lsmod | grep -s vfio > /dev/null + if [ $? -eq 0 ] ; then + sudo /sbin/rmmod vfio-pci + sudo /sbin/rmmod vfio_iommu_type1 + sudo /sbin/rmmod vfio + fi +} + +# +# Loads new vfio-pci (and vfio module if needed). +# +load_vfio_module() +{ + remove_vfio_module + + VFIO_PATH="kernel/drivers/vfio/pci/vfio-pci.ko" + + echo "Loading VFIO module" + /sbin/lsmod | grep -s vfio_pci > /dev/null + if [ $? -ne 0 ] ; then + if [ -f /lib/modules/$(uname -r)/$VFIO_PATH ] ; then + sudo /sbin/modprobe vfio-pci + fi + fi + + # make sure regular users can read /dev/vfio + echo "chmod /dev/vfio" + sudo chmod a+x /dev/vfio + if [ $? -ne 0 ] ; then + echo "FAIL" + quit + fi + echo "OK" + + # check if /dev/vfio/vfio exists - that way we + # know we either loaded the module, or it was + # compiled into the kernel + if [ ! -e /dev/vfio/vfio ] ; then + echo "## ERROR: VFIO not found!" + fi +} + + load_igb_uio_module +load_vfio_module CPU_FEATURES_DETECT=`cat /proc/cpuinfo |grep hypervisor | wc -l` @@ -81,10 +132,13 @@ fi $RTE_SDK/usertools/dpdk-devbind.py --status if [ ${VM_DETECT} == 'HOST' ]; then #HOST - $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio 0000:d8:02.0 - $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio 0000:d8:02.1 - $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio 0000:07:02.0 - $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio 0000:07:02.1 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:86:02.0 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:86:02.1 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:d8:02.0 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:d8:02.1 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:da:02.0 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:da:02.1 + else #VM $RTE_SDK/usertools/dpdk-devbind.py --bind=igb_uio 0000:00:04.0 diff --git a/fhi_lib/app/gen_test.m b/fhi_lib/app/gen_test.m index b93736c..d8bd859 100644 --- a/fhi_lib/app/gen_test.m +++ b/fhi_lib/app/gen_test.m @@ -16,19 +16,105 @@ %* %*******************************************************************************/ -%Matlab: read bin -%fileID_c = fopen('ant_7.bin','r'); -%ant7_c= fread(fileID_c, [2, 792*14*80], 'integer*2'); -%ant7_c; -%ant7_c=ant7_c.'; - close all; clear all; -ifft_in = load('ifft_in.txt') +%select mu and bw to generate test files +sub6=false; %false +mu=3; % 0,1, or 3 +bw=100; %5,10,20,100 MHz + +nSlots=160; % any 40 and 160 + + % 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz +nNumRbsPerSymF1 = ... +[ + % 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz + [25, 52, 79, 106, 133, 160, 216, 270, 0, 0, 0, 0, 0] % Numerology 0 (15KHz) + [11, 24, 38, 51, 65, 78, 106, 133, 162, 0, 217, 245, 273] % Numerology 1 (30KHz) + [0, 11, 18, 24, 31, 38, 51, 65, 79, 0, 107, 121, 135] % Numerology 2 (60KHz) +]; + +nNumRbsPerSymF2 = ... +[ + % 50Mhz 100MHz 200MHz 400MHz + [66, 132, 264, 0] % Numerology 2 (60KHz) + [32, 66, 132, 264] % Numerology 3 (120KHz) +]; + +if sub6 + disp('Sub6') + if mu < 3 + nNumerology = mu+1; + switch (bw) + case {5} + numRBs = nNumRbsPerSymF1(nNumerology,0+1); + case {10} + numRBs = nNumRbsPerSymF1(nNumerology,1+1); + case {15} + numRBs = nNumRbsPerSymF1(nNumerology,2+1); + case {20} + numRBs = nNumRbsPerSymF1(nNumerology,3+1); + case {25} + numRBs = nNumRbsPerSymF1(nNumerology,4+1); + case {30} + numRBs = nNumRbsPerSymF1(nNumerology,5+1); + case {40} + numRBs = nNumRbsPerSymF1(nNumerology,6+1); + case {50} + numRBs = nNumRbsPerSymF1(nNumerology,7+1); + case {60} + numRBs = nNumRbsPerSymF1(nNumerology,8+1); + case {70} + numRBs = nNumRbsPerSymF1(nNumerology,9+1); + case {80} + numRBs = nNumRbsPerSymF1(nNumerology,10+1); + case {90} + numRBs = nNumRbsPerSymF1(nNumerology,11+1); + case {100} + numRBs = nNumRbsPerSymF1(nNumerology,12+1); + otherwise + disp('Unknown BW && mu') + end + end +else + disp('mmWave') + if (mu >=2) && (mu <= 3) + nNumerology = mu; + switch (bw) + case {50} + numRBs = nNumRbsPerSymF2(nNumerology-1,0+1); + case {100} + numRBs = nNumRbsPerSymF2(nNumerology-1,1+1); + case {200} + numRBs = nNumRbsPerSymF2(nNumerology-1,2+1); + case {400} + numRBs = nNumRbsPerSymF2(nNumerology-1,3+1); + otherwise + disp('Unknown BW && mu') + end + end +end + +if numRBs ==0 + disp('Incorrect Numerology and BW combination.') + return +end + +bw +numRBs +nSlots + +%use file as input +%ifft_in = load('ifft_in.txt') + +%gen IQs +ifft_in = [[1:1:(numRBs*12)]', [1:1:(numRBs*12)]']; + ant_c = ifft_in; -for (i=1:1:80*14-1) - ant_c = [ant_c; ifft_in]; +for (i=1:1:nSlots*14-1) + ifft_in_1 = ifft_in + i; + ant_c = [ant_c; ifft_in_1]; end ant0=ant_c; diff --git a/fhi_lib/app/ifft_in.txt b/fhi_lib/app/ifft_in.txt new file mode 100644 index 0000000..bca8a2f --- /dev/null +++ b/fhi_lib/app/ifft_in.txt @@ -0,0 +1,792 @@ +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,-1 +19,0 +19,-1 +19,-1 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +19,0 +20,0 +20,0 +20,0 +20,0 +20,0 +20,0 +20,0 +20,0 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,1 +20,2 +20,1 +20,1 +20,1 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +20,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,2 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,3 +21,4 +21,4 +22,4 +21,3 +21,3 +21,4 +21,4 +21,4 +21,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,4 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +22,5 +23,5 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,6 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +23,7 +24,7 +24,7 +24,7 +24,7 +24,7 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,8 +24,9 +24,9 +24,9 +24,9 +24,9 +24,9 +24,9 +24,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,9 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,10 +25,11 +25,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,11 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +26,12 +27,12 +27,12 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,13 +27,14 +27,14 +27,14 +27,14 +27,14 +27,14 +27,14 +27,14 +27,14 +27,14 +28,14 +28,14 +28,14 +28,14 +28,14 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,15 +28,16 +28,16 +28,16 +28,16 +29,16 +29,16 +29,16 +29,16 +29,16 +29,16 +29,16 +29,16 +29,17 +29,17 +28,17 +29,17 +29,17 +29,17 +29,17 +29,17 +29,17 +29,17 +29,17 +29,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,18 +30,19 +30,18 +30,19 +30,19 +30,19 +30,19 +30,19 +31,19 +31,19 +31,20 +31,20 +31,20 +31,20 +31,20 +31,20 +31,20 +31,20 +31,20 +31,21 +31,21 +31,21 +31,21 +32,21 +31,21 +32,21 +32,21 +32,21 +32,22 +32,22 +32,22 +32,22 +32,22 +32,22 +32,22 +32,22 +32,23 +32,23 +32,23 +33,23 +33,23 +33,23 +33,23 +33,23 +33,24 +34,24 +33,24 +33,24 +33,24 +33,24 +33,24 +33,24 +34,25 +34,25 +34,25 +34,25 +34,25 +34,25 +34,25 +34,26 +34,26 +34,26 +34,26 +34,26 +35,26 +35,26 +35,26 +35,27 +35,27 +35,27 +35,27 +35,27 +35,28 +35,28 +35,28 +36,28 +36,28 +36,28 +36,29 +36,29 +36,29 +36,29 +36,29 +36,29 +36,30 +36,30 +37,30 +37,30 +37,30 +37,30 +37,31 +37,31 +37,31 +37,31 +37,31 +38,32 +38,32 +38,32 +38,32 +38,32 +38,33 +38,33 +38,33 +38,33 +39,33 +39,34 +39,34 +39,34 +39,34 +39,34 +39,35 +39,35 +40,35 +40,35 +40,35 +40,36 +40,36 +40,36 +40,36 +41,37 +41,37 +41,37 +41,37 +41,38 +41,38 +41,38 +41,38 +42,39 +42,39 +42,39 +42,40 +42,39 +42,40 +43,40 +43,40 +43,41 +43,41 +43,41 +43,42 +44,42 +44,42 +44,42 +44,43 +44,43 +44,43 +45,44 +45,44 +45,44 +45,45 +45,45 +45,45 +46,46 +46,46 +46,46 +46,47 +46,47 +47,47 +47,48 +47,48 +47,48 +47,49 +48,49 +48,49 +48,50 +48,50 +49,51 +49,51 +49,51 +49,52 +49,52 +50,53 +50,53 +50,53 +50,54 +51,54 +51,55 +51,55 +51,56 +52,56 +52,57 +52,57 +53,57 +53,58 +53,58 +53,59 +54,59 +54,60 +54,61 +55,61 +55,62 +56,63 +56,63 +56,64 +57,64 +57,65 +57,66 +58,66 +58,67 +58,67 +59,68 +59,69 +59,69 +60,70 +60,71 +61,71 +61,72 +61,73 +62,74 +62,74 +63,75 +63,76 +64,77 +64,77 +64,78 +65,79 +65,80 +66,81 +66,82 +67,82 +67,83 +68,84 +69,85 +69,86 +70,87 +70,88 +71,89 +71,90 +72,91 +73,92 +73,93 +74,95 +75,96 +75,97 +76,98 +77,99 +77,101 +78,102 +79,103 +80,105 +80,106 +81,107 +82,109 +83,110 +84,112 +85,113 +86,115 +87,117 +88,118 +89,120 +90,122 +91,124 +92,126 +93,128 +94,130 +95,132 +97,134 +98,136 +99,138 +100,140 +102,143 +103,145 +105,148 +106,151 +108,153 +110,156 +111,159 +113,162 +115,165 +117,169 +119,172 +121,176 +123,179 +125,183 +127,187 +130,191 +132,195 +135,200 +138,205 +140,210 +143,215 +146,220 +150,226 +153,232 +157,238 +161,245 +165,252 +169,259 +173,267 +178,275 +183,284 +189,293 +194,303 +201,314 +207,325 +214,337 +222,351 +230,365 +239,380 +248,397 +259,415 +271,435 +283,457 +297,481 +312,507 +330,537 +349,571 +371,608 +395,651 +424,701 +457,758 +496,826 +543,907 +600,1005 +670,1127 +759,1281 +877,1485 +1038,1765 +1275,2175 +1653,2829 +2355,4046 +4111,7086 +16399,28370 +-8177,-14197 +-3262,-5684 +-2033,-3556 +-1475,-2588 +-1156,-2035 +-949,-1678 +-805,-1427 +-698,-1242 +-616,-1100 +-550,-987 +-497,-895 +-454,-819 +-417,-755 +-385,-700 +-358,-653 +-334,-612 +-313,-576 +-295,-544 +-278,-515 +-263,-489 +-250,-466 +-238,-444 +-226,-425 +-216,-408 +-207,-392 +-198,-377 +-190,-363 +-183,-350 +-176,-338 +-169,-327 +-163,-317 +-158,-307 +-153,-298 +-148,-289 +-143,-281 +-138,-273 +-134,-266 +-130,-259 +-127,-253 +-123,-247 +-120,-241 +-116,-235 +-113,-230 +-110,-225 +-108,-220 +-105,-215 +-102,-211 +-100,-206 +-98,-202 +-95,-198 +-93,-195 +-91,-191 +-89,-188 +-87,-184 +-85,-181 +-83,-178 +-82,-175 +-80,-172 +-78,-169 \ No newline at end of file diff --git a/fhi_lib/app/lls-cu/Makefile b/fhi_lib/app/lls-cu/Makefile deleted file mode 100644 index 86aa9cc..0000000 --- a/fhi_lib/app/lls-cu/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -#/****************************************************************************** -#* -#* Copyright (c) 2019 Intel. -#* -#* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 -#* -#* 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. -#* -#*******************************************************************************/ -CC := icc - -ifeq ($(RTE_SDK),) - $(error "Please define RTE_SDK environment variable") -endif - -RTE_TARGET := x86_64-native-linuxapp-icc -RTE_INC := $(RTE_SDK)/$(RTE_TARGET)/include -#include $(RTE_SDK)/mk/rte.vars.mk - -ifeq ($(XRAN_DIR),) - XRAN_DIR=$(PWD)/../.. -endif - -COMMON_SRC=$(XRAN_DIR)/app/common -SRC_SRC=$(XRAN_DIR)/app/src - -ifeq ($(MLOG_DIR),) - MLOG_DIR=$(XRAN_DIR)/../mlog -endif - -APP = sample-lls-cu -SRC = $(COMMON_SRC)/common.c \ - ./sample-lls-cu.c \ - $(SRC_SRC)/config.c - -CFLAGS += -std=gnu11 -Wall -wd9 -Wextra -Werror -I$(XRAN_DIR)/lib/api -I$(COMMON_SRC) -I$(SRC_SRC) -I$(MLOG_DIR)/source -I$(RTE_INC) -ifeq ($(ME),1) - CFLAGS += -DMLOG_ENABLED -endif -ifeq ($(NB),1) - CFLAGS += -DNightly_build -endif - -ifeq ($(DEBUG),1) - CFLAGS += -DDEBUG -O0 -g -else - CFLAGS += -O3 -endif - -ifeq ($(MULTI_SECTION),1) - CFLAGS += -DMULTI_SECTION -endif - -LDFLAGS += -pthread -lrt - -RTE_LIBS = -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,-lrte_flow_classify -Wl,--whole-archive -Wl,-lrte_pipeline -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_table -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_port -Wl,--no-whole-archive -Wl,-lrte_pdump -Wl,-lrte_distributor -Wl,-lrte_ip_frag -Wl,-lrte_meter -Wl,-lrte_lpm -Wl,--whole-archive -Wl,-lrte_acl -Wl,--no-whole-archive -Wl,-lrte_jobstats -Wl,-lrte_metrics -Wl,-lrte_bitratestats -Wl,-lrte_latencystats -Wl,-lrte_power -Wl,-lrte_efd -Wl,-lrte_bpf -Wl,--whole-archive -Wl,-lrte_cfgfile -Wl,-lrte_gro -Wl,-lrte_gso -Wl,-lrte_hash -Wl,-lrte_member -Wl,-lrte_vhost -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_net -Wl,-lrte_ethdev -Wl,-lrte_bbdev -Wl,-lrte_cryptodev -Wl,-lrte_security -Wl,-lrte_compressdev -Wl,-lrte_eventdev -Wl,-lrte_rawdev -Wl,-lrte_timer -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_pci -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_reorder -Wl,-lrte_sched -Wl,-lrte_kni -Wl,-lrte_common_octeontx -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_bus_dpaa -Wl,-lrte_bus_fslmc -Wl,-lrte_mempool_bucket -Wl,-lrte_mempool_stack -Wl,-lrte_mempool_dpaa -Wl,-lrte_mempool_dpaa2 -Wl,-lrte_pmd_af_packet -Wl,-lrte_pmd_ark -Wl,-lrte_pmd_avf -Wl,-lrte_pmd_avp -Wl,-lrte_pmd_axgbe -Wl,-lrte_pmd_bnxt -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_cxgbe -Wl,-lrte_pmd_dpaa -Wl,-lrte_pmd_dpaa2 -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ena -Wl,-lrte_pmd_enic -Wl,-lrte_pmd_fm10k -Wl,-lrte_pmd_failsafe -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_kni -Wl,-lrte_pmd_lio -Wl,-lrte_pmd_nfp -Wl,-lrte_pmd_null -Wl,-lrte_pmd_qede -Wl,-lrte_pmd_ring -Wl,-lrte_pmd_softnic -Wl,-lrte_pmd_tap -Wl,-lrte_pmd_thunderx_nicvf -Wl,-lrte_pmd_vdev_netvsc -Wl,-lrte_pmd_virtio -Wl,-lrte_pmd_vhost -Wl,-lrte_pmd_ifc -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_bus_vmbus -Wl,-lrte_pmd_netvsc -Wl,-lrte_pmd_bbdev_null -Wl,-lrte_pmd_null_crypto -Wl,-lrte_pmd_crypto_scheduler -Wl,-lrte_pmd_dpaa2_sec -Wl,-lrte_pmd_dpaa_sec -Wl,-lrte_pmd_virtio_crypto -Wl,-lrte_pmd_octeontx_zip -Wl,-lrte_pmd_qat -Wl,-lrte_pmd_skeleton_event -Wl,-lrte_pmd_sw_event -Wl,-lrte_pmd_octeontx_ssovf -Wl,-lrte_pmd_dpaa_event -Wl,-lrte_pmd_dpaa2_event -Wl,-lrte_mempool_octeontx -Wl,-lrte_pmd_octeontx -Wl,-lrte_pmd_opdl_event -Wl,-lrte_pmd_skeleton_rawdev -Wl,-lrte_pmd_dpaa2_cmdif -Wl,-lrte_pmd_dpaa2_qdma -Wl,-lrte_bus_ifpga -Wl,-lrte_pmd_ifpga_rawdev -Wl,--no-whole-archive -Wl,-lrt -Wl,-lm -Wl,-lnuma -Wl,-ldl -Wl, - -XRAN_LIB_DIR=$(XRAN_DIR)/lib/bin -LDFLAGS += -L$(XRAN_LIB_DIR) -Wl, -lxran -Wl, -L$(MLOG_DIR)/bin -Wl, $(RTE_LIBS) -ifeq ($(ME),1) - LDFLAGS += -Wl,-lmlog -endif - -OBJ = $(foreach file,$(SRC),$(file:.c=.o)) - - -all: $(APP) install - -$(OBJ): %.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -$(APP): $(OBJ) - $(CC) -o $(APP) $(CFLAGS) $(OBJ) $(LDFLAGS) - -install: $(APP) - @mkdir -p bin - @cp $(APP) ./bin - -clean: - @rm -rf $(APP) $(OBJ) ./bin/$(APP) - -#include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/fhi_lib/app/ru/Makefile b/fhi_lib/app/ru/Makefile deleted file mode 100644 index 29ac054..0000000 --- a/fhi_lib/app/ru/Makefile +++ /dev/null @@ -1,85 +0,0 @@ -#/****************************************************************************** -#* -#* Copyright (c) 2019 Intel. -#* -#* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 -#* -#* 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. -#* -#*******************************************************************************/ -CC := icc - -ifeq ($(RTE_SDK),) - $(error "Please define RTE_SDK environment variable") -endif - -RTE_TARGET := x86_64-native-linuxapp-icc -RTE_INC := $(RTE_SDK)/$(RTE_TARGET)/include -#include $(RTE_SDK)/mk/rte.vars.mk - -ifeq ($(XRAN_DIR),) - XRAN_DIR=$(PWD)/../.. -endif - -COMMON_SRC=$(XRAN_DIR)/app/common -SRC_SRC=$(XRAN_DIR)/app/src - -ifeq ($(MLOG_DIR),) - MLOG_DIR=$(XRAN_DIR)/../mlog -endif -APP = sample-ru -SRC = $(COMMON_SRC)/common.c \ - ../lls-cu/sample-lls-cu.c \ - $(SRC_SRC)/config.c - -CFLAGS += -std=gnu11 -Wall -wd9 -Wextra -Werror -I$(XRAN_DIR)/lib/api -I$(COMMON_SRC) -I$(SRC_SRC) -I$(MLOG_DIR)/source -I$(RTE_INC) -ifeq ($(ME),1) - CFLAGS += -DMLOG_ENABLED -endif - -ifeq ($(DEBUG),1) - CFLAGS += -DDEBUG -O0 -g -else - CFLAGS += -O3 -endif - -ifeq ($(MULTI_SECTION),1) - CFLAGS += -DMULTI_SECTION -endif - -LDFLAGS += -pthread -lrt - -RTE_LIBS = -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,-lrte_flow_classify -Wl,--whole-archive -Wl,-lrte_pipeline -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_table -Wl,--no-whole-archive -Wl,--whole-archive -Wl,-lrte_port -Wl,--no-whole-archive -Wl,-lrte_pdump -Wl,-lrte_distributor -Wl,-lrte_ip_frag -Wl,-lrte_meter -Wl,-lrte_lpm -Wl,--whole-archive -Wl,-lrte_acl -Wl,--no-whole-archive -Wl,-lrte_jobstats -Wl,-lrte_metrics -Wl,-lrte_bitratestats -Wl,-lrte_latencystats -Wl,-lrte_power -Wl,-lrte_efd -Wl,-lrte_bpf -Wl,--whole-archive -Wl,-lrte_cfgfile -Wl,-lrte_gro -Wl,-lrte_gso -Wl,-lrte_hash -Wl,-lrte_member -Wl,-lrte_vhost -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_net -Wl,-lrte_ethdev -Wl,-lrte_bbdev -Wl,-lrte_cryptodev -Wl,-lrte_security -Wl,-lrte_compressdev -Wl,-lrte_eventdev -Wl,-lrte_rawdev -Wl,-lrte_timer -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_pci -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_reorder -Wl,-lrte_sched -Wl,-lrte_kni -Wl,-lrte_common_octeontx -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_bus_dpaa -Wl,-lrte_bus_fslmc -Wl,-lrte_mempool_bucket -Wl,-lrte_mempool_stack -Wl,-lrte_mempool_dpaa -Wl,-lrte_mempool_dpaa2 -Wl,-lrte_pmd_af_packet -Wl,-lrte_pmd_ark -Wl,-lrte_pmd_avf -Wl,-lrte_pmd_avp -Wl,-lrte_pmd_axgbe -Wl,-lrte_pmd_bnxt -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_cxgbe -Wl,-lrte_pmd_dpaa -Wl,-lrte_pmd_dpaa2 -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ena -Wl,-lrte_pmd_enic -Wl,-lrte_pmd_fm10k -Wl,-lrte_pmd_failsafe -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_kni -Wl,-lrte_pmd_lio -Wl,-lrte_pmd_nfp -Wl,-lrte_pmd_null -Wl,-lrte_pmd_qede -Wl,-lrte_pmd_ring -Wl,-lrte_pmd_softnic -Wl,-lrte_pmd_tap -Wl,-lrte_pmd_thunderx_nicvf -Wl,-lrte_pmd_vdev_netvsc -Wl,-lrte_pmd_virtio -Wl,-lrte_pmd_vhost -Wl,-lrte_pmd_ifc -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_bus_vmbus -Wl,-lrte_pmd_netvsc -Wl,-lrte_pmd_bbdev_null -Wl,-lrte_pmd_null_crypto -Wl,-lrte_pmd_crypto_scheduler -Wl,-lrte_pmd_dpaa2_sec -Wl,-lrte_pmd_dpaa_sec -Wl,-lrte_pmd_virtio_crypto -Wl,-lrte_pmd_octeontx_zip -Wl,-lrte_pmd_qat -Wl,-lrte_pmd_skeleton_event -Wl,-lrte_pmd_sw_event -Wl,-lrte_pmd_octeontx_ssovf -Wl,-lrte_pmd_dpaa_event -Wl,-lrte_pmd_dpaa2_event -Wl,-lrte_mempool_octeontx -Wl,-lrte_pmd_octeontx -Wl,-lrte_pmd_opdl_event -Wl,-lrte_pmd_skeleton_rawdev -Wl,-lrte_pmd_dpaa2_cmdif -Wl,-lrte_pmd_dpaa2_qdma -Wl,-lrte_bus_ifpga -Wl,-lrte_pmd_ifpga_rawdev -Wl,--no-whole-archive -Wl,-lrt -Wl,-lm -Wl,-lnuma -Wl,-ldl -Wl, - -XRAN_LIB_DIR=$(XRAN_DIR)/lib/bin -LDFLAGS += -L$(XRAN_LIB_DIR) -Wl, -lxran -Wl, -L$(MLOG_DIR)/bin -Wl, $(RTE_LIBS) -ifeq ($(ME),1) - LDFLAGS += -Wl,-lmlog -endif -OBJ = $(foreach file,$(SRC),$(file:.c=.o)) - - -all: $(APP) install - -$(OBJ): %.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -$(APP): $(OBJ) - $(CC) -o $(APP) $(CFLAGS) $(OBJ) $(LDFLAGS) - -install: $(APP) - @mkdir -p bin - @cp $(APP) ./bin - -clean: - @rm -rf $(APP) $(OBJ) ./bin/$(APP) - -#include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/fhi_lib/app/run_lls-cu.sh b/fhi_lib/app/run_o_du.sh similarity index 85% rename from fhi_lib/app/run_lls-cu.sh rename to fhi_lib/app/run_o_du.sh index cec7e5b..4f6813c 100644 --- a/fhi_lib/app/run_lls-cu.sh +++ b/fhi_lib/app/run_o_du.sh @@ -18,7 +18,6 @@ #* #*******************************************************************************/ - ulimit -c unlimited echo 1 > /proc/sys/kernel/core_uses_pid @@ -29,7 +28,11 @@ if ! mount | grep $huge_folder; then mount none $huge_folder -t hugetlbfs -o rw,mode=0777 fi -./lls-cu/bin/sample-lls-cu config_file_lls_cu.dat 0000:18:02.0 0000:18:02.1 +#40G +./build/sample-app ./usecase/mu3_100mhz/config_file_o_du.dat 0000:d8:02.0 0000:d8:02.1 + +#25G +#./build/sample-app ./usecase/mu0_10mhz/12/config_file_o_du.dat 0000:af:02.0 0000:af:02.1 umount $huge_folder rmdir $huge_folder diff --git a/fhi_lib/app/run_ru.sh b/fhi_lib/app/run_o_ru.sh similarity index 92% rename from fhi_lib/app/run_ru.sh rename to fhi_lib/app/run_o_ru.sh index e84bc0a..e9204f5 100644 --- a/fhi_lib/app/run_ru.sh +++ b/fhi_lib/app/run_o_ru.sh @@ -28,7 +28,8 @@ if ! mount | grep $huge_folder; then mount none $huge_folder -t hugetlbfs -o rw,mode=0777 fi -./ru/bin/sample-ru config_file_ru.dat 0000:18:02.0 0000:18:02.1 +#40G +./build/sample-app ./usecase/mu1_100mhz/config_file_o_ru.dat 0000:d8:02.0 0000:d8:02.1 umount $huge_folder rmdir $huge_folder diff --git a/fhi_lib/app/src/common.c b/fhi_lib/app/src/common.c new file mode 100644 index 0000000..d9b5d0c --- /dev/null +++ b/fhi_lib/app/src/common.c @@ -0,0 +1,668 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +#include +#include +#include +#include +#include + +#include "common.h" +#include "xran_fh_o_du.h" +#include "xran_pkt.h" +#include "xran_pkt_up.h" +#include "xran_cp_api.h" +#include "xran_up_api.h" + +#include "xran_mlog_lnx.h" + +extern enum app_state state; + +int iq_playback_buffer_size_dl = IQ_PLAYBACK_BUFFER_BYTES; +int iq_playback_buffer_size_ul = IQ_PLAYBACK_BUFFER_BYTES; + +uint8_t numCCPorts = 1; +/* Number of antennas supported by front-end */ + +uint8_t num_eAxc = 4; +/* Number of CPRI ports supported by front-end */ + +int16_t *p_tx_play_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t tx_play_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; +int32_t tx_play_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; + +int16_t *p_tx_prach_play_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t tx_prach_play_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; +int32_t tx_prach_play_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; + +int16_t *p_rx_log_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t rx_log_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; +int32_t rx_log_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; + +int16_t *p_prach_log_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t prach_log_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; +int32_t prach_log_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; + +int16_t *p_tx_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t tx_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; + +int16_t *p_rx_buffer[MAX_ANT_CARRIER_SUPPORTED]; +int32_t rx_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; + + +// F1 Tables 38.101-1 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB +uint16_t nNumRbsPerSymF1[3][13] = +{ + // 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz + {25, 52, 79, 106, 133, 160, 216, 270, 0, 0, 0, 0, 0}, // Numerology 0 (15KHz) + {11, 24, 38, 51, 65, 78, 106, 133, 162, 0, 217, 245, 273}, // Numerology 1 (30KHz) + {0, 11, 18, 24, 31, 38, 51, 65, 79, 0, 107, 121, 135} // Numerology 2 (60KHz) +}; + +// F2 Tables 38.101-2 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB +uint16_t nNumRbsPerSymF2[2][4] = +{ + // 50Mhz 100MHz 200MHz 400MHz + {66, 132, 264, 0}, // Numerology 2 (60KHz) + {32, 66, 132, 264} // Numerology 3 (120KHz) +}; + +// 38.211 - Table 4.2.1 +uint16_t nSubCarrierSpacing[5] = +{ + 15, // mu = 0 + 30, // mu = 1 + 60, // mu = 2 + 120, // mu = 3 + 240 // mu = 4 +}; + +// TTI interval in us (slot duration) +uint16_t nTtiInterval[4] = +{ + 1000, // mu = 0 + 500, // mu = 1 + 250, // mu = 2 + 125, // mu = 3 +}; + + +// F1 Tables 38.101-1 Table F.5.3. Window length for normal CP +uint16_t nCpSizeF1[3][13][2] = +{ + // 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz + {{40, 36}, {80, 72}, {120, 108}, {160, 144}, {160, 144}, {240, 216}, {320, 288}, {320, 288}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, // Numerology 0 (15KHz) + {{22, 18}, {44, 36}, {66, 54}, {88, 72}, {88, 72}, {132, 108}, {176, 144}, {176, 144}, {264, 216}, {264, 216}, {352, 288}, {352, 288}, {352, 288}}, // Numerology 1 (30KHz) + { {0, 0}, {26, 18}, {39, 27}, {52, 36}, {52, 36}, {78, 54}, {104, 72}, {104, 72}, {156, 108}, {156, 108}, {208, 144}, {208, 144}, {208, 144}}, // Numerology 2 (60KHz) +}; + +// F2 Tables 38.101-2 Table F.5.3. Window length for normal CP +int16_t nCpSizeF2[2][4][2] = +{ + // 50Mhz 100MHz 200MHz 400MHz + { {0, 0}, {104, 72}, {208, 144}, {416, 288}}, // Numerology 2 (60KHz) + {{68, 36}, {136, 72}, {272, 144}, {544, 288}}, // Numerology 3 (120KHz) +}; + +uint32_t gMaxSlotNum; +uint32_t gNumDLCtx; +uint32_t gNumULCtx; +uint32_t gDLResetAdvance; +uint32_t gDLProcAdvance; +uint32_t gULProcAdvance; + +static uint16_t g_NumSlotTDDLoop[XRAN_MAX_SECTOR_NR] = { XRAN_NUM_OF_SLOT_IN_TDD_LOOP }; +static uint16_t g_NumDLSymSp[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {0}; +static uint16_t g_NumULSymSp[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {0}; +static uint8_t g_SlotType[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {{XRAN_SLOT_TYPE_INVALID}}; +float g_UlRate[XRAN_MAX_SECTOR_NR] = {0.0}; +float g_DlRate[XRAN_MAX_SECTOR_NR] = {0.0}; + +uint32_t app_xran_get_tti_interval(uint8_t nMu) +{ + if (nMu < 4) + { + return nTtiInterval[nMu]; + } + else + { + printf("ERROR: %s Mu[%d] is not valid\n",__FUNCTION__, nMu); + } + + return 0; +} + +uint32_t app_xran_get_scs(uint8_t nMu) +{ + if (nMu <= 3) + { + return nSubCarrierSpacing[nMu]; + } + else + { + printf("ERROR: %s Mu[%d] is not valid\n",__FUNCTION__, nMu); + } + + return 0; +} + + + + +//------------------------------------------------------------------------------------------- +/** @ingroup group_nr5g_source_phy_common + * + * @param[in] nNumerology - Numerology determine sub carrier spacing, Value: 0->4 0: 15khz, 1: 30khz, 2: 60khz 3: 120khz, 4: 240khz + * @param[in] nBandwidth - Carrier bandwidth for in MHz. Value: 5->400 + * @param[in] nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 + * + * @return Number of RBs in cell + * + * @description + * Returns number of RBs based on 38.101-1 and 38.101-2 for the cell + * +**/ +//------------------------------------------------------------------------------------------- +uint16_t app_xran_get_num_rbs(uint32_t nNumerology, uint32_t nBandwidth, uint32_t nAbsFrePointA) +{ + uint32_t error = 1; + uint16_t numRBs = 0; + + if (nAbsFrePointA <= 6000000) + { + // F1 Tables 38.101-1 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB + if (nNumerology < 3) + { + switch(nBandwidth) + { + case PHY_BW_5_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][0]; + error = 0; + break; + case PHY_BW_10_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][1]; + error = 0; + break; + case PHY_BW_15_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][2]; + error = 0; + break; + case PHY_BW_20_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][3]; + error = 0; + break; + case PHY_BW_25_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][4]; + error = 0; + break; + case PHY_BW_30_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][5]; + error = 0; + break; + case PHY_BW_40_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][6]; + error = 0; + break; + case PHY_BW_50_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][7]; + error = 0; + break; + case PHY_BW_60_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][8]; + error = 0; + break; + case PHY_BW_70_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][9]; + error = 0; + break; + case PHY_BW_80_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][10]; + error = 0; + break; + case PHY_BW_90_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][11]; + error = 0; + break; + case PHY_BW_100_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][12]; + error = 0; + break; + default: + error = 1; + break; + } + } + } + else + { + if ((nNumerology >= 2) && (nNumerology <= 3)) + { + // F2 Tables 38.101-2 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB + switch(nBandwidth) + { + case PHY_BW_50_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][0]; + error = 0; + break; + case PHY_BW_100_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][1]; + error = 0; + break; + case PHY_BW_200_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][2]; + error = 0; + break; + case PHY_BW_400_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][3]; + error = 0; + break; + default: + error = 1; + break; + } + } + } + + + if (error) + { + printf("ERROR: %s: nNumerology[%d] nBandwidth[%d] nAbsFrePointA[%d]\n",__FUNCTION__, nNumerology, nBandwidth, nAbsFrePointA); + } + else + { + printf("%s: nNumerology[%d] nBandwidth[%d] nAbsFrePointA[%d] numRBs[%d]\n",__FUNCTION__, nNumerology, nBandwidth, nAbsFrePointA, numRBs); + } + + return numRBs; +} + +//------------------------------------------------------------------------------------------- +/** @ingroup phy_cal_nrarfcn + * + * @param[in] center frequency + * + * @return NR-ARFCN + * + * @description + * This calculates NR-ARFCN value according to center frequency + * +**/ +//------------------------------------------------------------------------------------------- +uint32_t app_xran_cal_nrarfcn(uint32_t nCenterFreq) +{ + uint32_t nDeltaFglobal,nFoffs,nNoffs; + uint32_t nNRARFCN = 0; + + if(nCenterFreq > 0 && nCenterFreq < 3000*1000) + { + nDeltaFglobal = 5; + nFoffs = 0; + nNoffs = 0; + } + else if(nCenterFreq >= 3000*1000 && nCenterFreq < 24250*1000) + { + nDeltaFglobal = 15; + nFoffs = 3000*1000; + nNoffs = 600000; + } + else if(nCenterFreq >= 24250*1000 && nCenterFreq <= 100000*1000) + { + nDeltaFglobal = 60; + nFoffs = 24250080; + nNoffs = 2016667; + } + else + { + printf("@@@@ incorrect center frerquency %d\n",nCenterFreq); + return (0); + } + + nNRARFCN = ((nCenterFreq - nFoffs)/nDeltaFglobal) + nNoffs; + + printf("%s: nCenterFreq[%d] nDeltaFglobal[%d] nFoffs[%d] nNoffs[%d] nNRARFCN[%d]\n", __FUNCTION__, nCenterFreq, nDeltaFglobal, nFoffs, nNoffs, nNRARFCN); + return (nNRARFCN); +} + +int32_t app_xran_slot_limit(int32_t nSfIdx) +{ + while (nSfIdx < 0) { + nSfIdx += gMaxSlotNum; + } + + while (nSfIdx >= gMaxSlotNum) { + nSfIdx -= gMaxSlotNum; + } + + return nSfIdx; +} + +void app_xran_clear_slot_type(uint32_t nPhyInstanceId) +{ + g_UlRate[nPhyInstanceId] = 0.0; + g_DlRate[nPhyInstanceId] = 0.0; + g_NumSlotTDDLoop[nPhyInstanceId] = 1; +} + +int32_t app_xran_set_slot_type(uint32_t nPhyInstanceId, uint32_t nFrameDuplexType, uint32_t nTddPeriod, struct xran_slot_config *psSlotConfig) +{ + uint32_t nSlotNum, nSymNum, nVal, i; + uint32_t numDlSym, numUlSym, numGuardSym; + uint32_t numDlSlots = 0, numUlSlots = 0, numSpDlSlots = 0, numSpUlSlots = 0, numSpSlots = 0; + char sSlotPattern[XRAN_SLOT_TYPE_LAST][10] = {"IN\0", "DL\0", "UL\0", "SP\0", "FD\0"}; + + // nPhyInstanceId Carrier ID + // nFrameDuplexType 0 = FDD 1 = TDD + // nTddPeriod Tdd Periodicity + // psSlotConfig[80] Slot Config Structure for nTddPeriod Slots + + g_UlRate[nPhyInstanceId] = 0.0; + g_DlRate[nPhyInstanceId] = 0.0; + g_NumSlotTDDLoop[nPhyInstanceId] = nTddPeriod; + + for (i = 0; i < XRAN_NUM_OF_SLOT_IN_TDD_LOOP; i++) + { + g_SlotType[nPhyInstanceId][i] = XRAN_SLOT_TYPE_INVALID; + g_NumDLSymSp[nPhyInstanceId][i] = 0; + g_NumULSymSp[nPhyInstanceId][i] = 0; + } + + if (nFrameDuplexType == XRAN_FDD) + { + for (i = 0; i < XRAN_NUM_OF_SLOT_IN_TDD_LOOP; i++) + { + g_SlotType[nPhyInstanceId][i] = XRAN_SLOT_TYPE_FDD; + } + g_NumSlotTDDLoop[nPhyInstanceId] = 1; + g_DlRate[nPhyInstanceId] = 1.0; + g_UlRate[nPhyInstanceId] = 1.0; + } + else + { + for (nSlotNum = 0; nSlotNum < nTddPeriod; nSlotNum++) + { + numDlSym = 0; + numUlSym = 0; + numGuardSym = 0; + for (nSymNum = 0; nSymNum < XRAN_NUM_OF_SYMBOL_PER_SLOT; nSymNum++) + { + switch(psSlotConfig[nSlotNum].nSymbolType[nSymNum]) + { + case XRAN_SYMBOL_TYPE_DL: + numDlSym++; + break; + case XRAN_SYMBOL_TYPE_GUARD: + numGuardSym++; + break; + default: + numUlSym++; + break; + } + } + + // printf("nSlotNum[%d] : numDlSym[%d] numGuardSym[%d] numUlSym[%d]\n", nSlotNum, numDlSym, numGuardSym, numUlSym); + + if ((numUlSym == 0) && (numGuardSym == 0)) + { + g_SlotType[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_DL; + numDlSlots++; + } + else if ((numDlSym == 0) && (numGuardSym == 0)) + { + g_SlotType[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_UL; + numUlSlots++; + } + else + { + g_SlotType[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_SP; + numSpSlots++; + + if (numDlSym) + { + numSpDlSlots++; + g_NumDLSymSp[nPhyInstanceId][nSlotNum] = numDlSym; + } + if (numUlSym) + { + numSpUlSlots++; + g_NumULSymSp[nPhyInstanceId][nSlotNum] = numUlSym; + } + } + + // printf(" numDlSlots[%d] numUlSlots[%d] numSpSlots[%d] numSpDlSlots[%d] numSpUlSlots[%d]\n", numDlSlots, numUlSlots, numSpSlots, numSpDlSlots, numSpUlSlots); + } + + g_DlRate[nPhyInstanceId] = (float)(numDlSlots + numSpDlSlots) / (float)nTddPeriod; + g_UlRate[nPhyInstanceId] = (float)(numUlSlots + numSpUlSlots) / (float)nTddPeriod; + } + + printf("set_slot_type: nPhyInstanceId[%d] nFrameDuplexType[%d], nTddPeriod[%d]\n", + nPhyInstanceId, nFrameDuplexType, nTddPeriod); + + printf("DLRate[%f] ULRate[%f]\n", g_DlRate[nPhyInstanceId], g_UlRate[nPhyInstanceId]); + + nVal = (g_NumSlotTDDLoop[nPhyInstanceId] < 10) ? g_NumSlotTDDLoop[nPhyInstanceId] : 10; + + printf("SlotPattern:\n"); + printf("Slot: "); + for (nSlotNum = 0; nSlotNum < nVal; nSlotNum++) + { + printf("%d ", nSlotNum); + } + printf("\n"); + + printf(" %3d ", 0); + for (nSlotNum = 0, i = 0; nSlotNum < g_NumSlotTDDLoop[nPhyInstanceId]; nSlotNum++) + { + printf("%s ", sSlotPattern[g_SlotType[nPhyInstanceId][nSlotNum]]); + i++; + if ((i == 10) && ((nSlotNum+1) < g_NumSlotTDDLoop[nPhyInstanceId])) + { + printf("\n"); + printf(" %3d ", nSlotNum); + i = 0; + } + } + printf("\n\n"); + + return 0; +} + +int32_t app_xran_get_slot_type(int32_t nCellIdx, int32_t nSlotdx, int32_t nType) +{ + int32_t nSfIdxMod, nSfType, ret = 0; + + nSfIdxMod = app_xran_slot_limit(nSlotdx) % ((g_NumSlotTDDLoop[nCellIdx] > 0) ? g_NumSlotTDDLoop[nCellIdx]: 1); + nSfType = g_SlotType[nCellIdx][nSfIdxMod]; + + if (nSfType == nType) + { + ret = 1; + } + else if (nSfType == XRAN_SLOT_TYPE_SP) + { + if ((nType == XRAN_SLOT_TYPE_DL) && g_NumDLSymSp[nCellIdx][nSfIdxMod]) + { + ret = 1; + } + + if ((nType == XRAN_SLOT_TYPE_UL) && g_NumULSymSp[nCellIdx][nSfIdxMod]) + { + ret = 1; + } + } + else if (nSfType == XRAN_SLOT_TYPE_FDD) + { + ret = 1; + } + + return ret; +} + + + +void sys_save_buf_to_file(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) +{ + if (size) + { + if (filename && bufname) + { + FILE *file; + printf("Storing %s to file %s: ", bufname, filename); + file = fopen(filename, "wb"); + if (file == NULL) + { + printf("can't open file %s!!!", filename); + } + else + { + uint32_t num; + num = fwrite(pBuffer, buffers_num, size, file); + fflush(file); + fclose(file); + printf("from addr (0x%lx) size (%d) bytes num (%d)", (uint64_t)pBuffer, size, num); + } + printf(" \n"); + } + else + { + printf(" the file name, buffer name are not set!!!"); + } + } + else + { + printf(" the %s is free: size = %d bytes!!!", bufname, size); + } +} + +int sys_load_file_to_buff(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) +{ + unsigned int file_size = 0; + int num= 0; + + if (size) + { + if (filename && bufname) + { + FILE *file; + printf("Loading file %s to %s: ", filename, bufname); + file = fopen(filename, "rb"); + + + if (file == NULL) + { + printf("can't open file %s!!!", filename); + exit(-1); + } + else + { + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if ((file_size > size) || (file_size == 0)) + file_size = size; + + printf("Reading IQ samples from file: File Size: %d [Buffer Size: %d]\n", file_size, size); + + num = fread(pBuffer, buffers_num, size, file); + fflush(file); + fclose(file); + printf("from addr (0x%lx) size (%d) bytes num (%d)", (uint64_t)pBuffer, file_size, num); + } + printf(" \n"); + + } + else + { + printf(" the file name, buffer name are not set!!!"); + } + } + else + { + printf(" the %s is free: size = %d bytes!!!", bufname, size); + } + return num; +} + + +void sys_save_buf_to_file_txt(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num) +{ + unsigned int i; + int ret = 0; + if (pBuffer == NULL) + return; + + if (size) + { + if (filename && bufname) + { + FILE *file; + printf("Storing %s to file %s: ", bufname, filename); + file = fopen(filename, "w"); + if (file == NULL) + { + printf("can't open file %s!!!", filename); + exit(-1); + } + else + { + uint32_t num = 0; + + signed short *ptr = (signed short*)pBuffer; + for (i = 0; i < (size/((unsigned int)sizeof(signed short) /** 2 * 2 * 2*/)); i = i + 2) + { +#ifndef CSCOPE_DEBUG + ret = fprintf(file,"%d %d\n", ptr[i], ptr[i + 1]); +#else + ret = fprintf(file,"%d %d ", ptr[i], ptr[i + 1]); + /* I data => Ramp data, from 1 to 792. + Q data => Contains time information of the current symbol: + Bits [15:14] = Antenna-ID + Bits [13:12] = “00” + Bits [11:8] = Subframe-ID + Bits [7:4] = Slot-ID + Bits [3:0] = Symbol-ID */ + fprintf(file, "0x%04x: ant %d Subframe-ID %d Slot-ID %d Symbol-ID %d\n", + ptr[i + 1], (ptr[i + 1]>>14) & 0x3, (ptr[i + 1]>>8) & 0xF, (ptr[i + 1]>>4) & 0xF, (ptr[i + 1]>>0) & 0xF); +#endif + if (ret < 0) + { + printf("fprintf %d\n", ret); + fclose(file); + break; + } + num++; + } + fflush(file); + fclose(file); + printf("from addr (0x%lx) size (%d) IQ num (%d)", (uint64_t)pBuffer, size, num); + } + printf(" \n"); + } + else + { + printf(" the file name, buffer name are not set!!!"); + } + } + else + { + printf(" the %s is free: size = %d bytes!!!", bufname, size); + } +} + diff --git a/fhi_lib/app/common/common.h b/fhi_lib/app/src/common.h similarity index 61% rename from fhi_lib/app/common/common.h rename to fhi_lib/app/src/common.h index fb4400d..8f9d406 100644 --- a/fhi_lib/app/common/common.h +++ b/fhi_lib/app/src/common.h @@ -16,16 +16,23 @@ * *******************************************************************************/ + +#ifndef _XRAN_APP_COMMON_H_ +#define _XRAN_APP_COMMON_H_ + #include #include +#include "xran_fh_o_du.h" #include "xran_pkt_up.h" #include #include -#define APP_LLS_CU 0 -#define APP_RU 1 +#define VERSIONX "#DIRTY#" + +#define APP_O_DU 0 +#define APP_O_RU 1 enum app_state { @@ -33,19 +40,24 @@ enum app_state APP_STOPPED }; -#define NUM_OF_PRB_IN_FULL_BAND (66) +enum nRChBwOptions +{ + PHY_BW_5_0_MHZ = 5, PHY_BW_10_0_MHZ = 10, PHY_BW_15_0_MHZ = 15, PHY_BW_20_0_MHZ = 20, PHY_BW_25_0_MHZ = 25, + PHY_BW_30_0_MHZ = 30, PHY_BW_40_0_MHZ = 40, PHY_BW_50_0_MHZ = 50, PHY_BW_60_0_MHZ = 60, PHY_BW_70_0_MHZ = 70, + PHY_BW_80_0_MHZ = 80, PHY_BW_90_0_MHZ = 90, PHY_BW_100_0_MHZ = 100, PHY_BW_200_0_MHZ = 200, PHY_BW_400_0_MHZ = 400 +}; + #define N_SC_PER_PRB 12 #define N_SYM_PER_SLOT 14 -#define N_FULLBAND_SC (NUM_OF_PRB_IN_FULL_BAND*N_SC_PER_PRB) -#define MAX_ANT_CARRIER_SUPPORTED 16 -// 0.125, just for testing -#define SLOTNUM_PER_SUBFRAME 8 +#define MAX_ANT_CARRIER_SUPPORTED (XRAN_MAX_SECTOR_NR*XRAN_MAX_ANTENNA_NR) + +#define SUBFRAME_DURATION_US 1000 +//#define SLOTNUM_PER_SUBFRAME 8 + #define SUBFRAMES_PER_SYSTEMFRAME 10 -#define PDSCH_PAYLOAD_SIZE (N_FULLBAND_SC*4) -#define NUM_OF_SLOT_IN_TDD_LOOP (80) -#define IQ_PLAYBACK_BUFFER_BYTES (NUM_OF_SLOT_IN_TDD_LOOP*N_SYM_PER_SLOT*N_FULLBAND_SC*4L) -/* PRACH data samples are 32 bits wide, 16bits for I and 16bits for Q. Each packet contains 839 samples. The payload length is 3356 octets.*/ -#define PRACH_PLAYBACK_BUFFER_BYTES (10*839*4L) +#define IQ_PLAYBACK_BUFFER_BYTES (XRAN_NUM_OF_SLOT_IN_TDD_LOOP*N_SYM_PER_SLOT*66*N_SC_PER_PRB*4L) +/* PRACH data samples are 32 bits wide, 16bits for I and 16bits for Q. Each packet contains 839 samples for long sequence or 144*14 (max) for short sequence. The payload length is 3356 octets.*/ +#define PRACH_PLAYBACK_BUFFER_BYTES (144*14*4L) #ifdef _DEBUG #define iAssert(p) if(!(p)){fprintf(stderr,\ @@ -55,19 +67,8 @@ enum app_state #define iAssert(p) #endif /* _DEBUG */ -struct send_symbol_cb_args -{ - struct rb_map *samp_buf; - uint8_t *symb_id; -}; - -struct pkt_dump -{ - int num_samp; - int num_bytes; - uint8_t symb; - struct ecpri_seq_id seq; -} __rte_packed; +extern int iq_playback_buffer_size_dl; +extern int iq_playback_buffer_size_ul; extern uint8_t numCCPorts; /* Number of antennas supported by front-end */ @@ -78,6 +79,10 @@ extern int16_t *p_tx_play_buffer[MAX_ANT_CARRIER_SUPPORTED]; extern int32_t tx_play_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; extern int32_t tx_play_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; +extern int16_t *p_tx_prach_play_buffer[MAX_ANT_CARRIER_SUPPORTED]; +extern int32_t tx_prach_play_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; +extern int32_t tx_prach_play_buffer_position[MAX_ANT_CARRIER_SUPPORTED]; + /* Number of antennas supported by front-end */ extern int16_t *p_rx_log_buffer[MAX_ANT_CARRIER_SUPPORTED]; extern int32_t rx_log_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; @@ -96,4 +101,13 @@ extern int32_t rx_buffer_size[MAX_ANT_CARRIER_SUPPORTED]; void sys_save_buf_to_file_txt(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num); void sys_save_buf_to_file(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num); int sys_load_file_to_buff(char *filename, char *bufname, unsigned char *pBuffer, unsigned int size, unsigned int buffers_num); +uint32_t app_xran_get_scs(uint8_t nMu); +uint16_t app_xran_get_num_rbs(uint32_t nNumerology, uint32_t nBandwidth, uint32_t nAbsFrePointA); +uint32_t app_xran_cal_nrarfcn(uint32_t nCenterFreq); +int32_t app_xran_set_slot_type(uint32_t nPhyInstanceId, uint32_t nFrameDuplexType, + uint32_t nTddPeriod, struct xran_slot_config *psSlotConfig); +uint32_t app_xran_get_tti_interval(uint8_t nMu); + + +#endif /*_XRAN_APP_COMMON_H_*/ diff --git a/fhi_lib/app/src/config.c b/fhi_lib/app/src/config.c index 1564c8d..abe2b48 100644 --- a/fhi_lib/app/src/config.c +++ b/fhi_lib/app/src/config.c @@ -26,42 +26,44 @@ #include "rte_common.h" #include "config.h" +#include "common.h" +#include "debug.h" #include #define MAX_LINE_SIZE 512 /* Configuration file maximum supported line length */ - #define KEY_APP_MODE "appMode" #define KEY_XRAN_MODE "xranMode" #define KEY_MU_NUMBER "mu" +#define KEY_NDLABSFREPOINTA "nDLAbsFrePointA" +#define KEY_NULABSFREPOINTA "nULAbsFrePointA" +#define KEY_NDLBANDWIDTH "nDLBandwidth" +#define KEY_NULBANDWIDTH "nULBandwidth" +#define KEY_NDLFFTSIZE "nDLFftSize" +#define KEY_NULFFTSIZE "nULFftSize" + +#define KEY_NFRAMEDUPLEXTYPE "nFrameDuplexType" +#define KEY_NTDDPERIOD "nTddPeriod" + +#define KEY_SSLOTCONFIG "sSlotConfig" + #define KEY_CC_PER_PORT_NUM "ccNum" #define KEY_ANT_NUM "antNum" #define KEY_TTI_PERIOD "ttiPeriod" + +#define KEY_MTU_SIZE "MTUSize" #define KEY_LLS_CU_MAC "llsCUMac" #define KEY_RU_MAC "ruMac" -#define KEY_FILE_AxC0 "antC0" -#define KEY_FILE_AxC1 "antC1" -#define KEY_FILE_AxC2 "antC2" -#define KEY_FILE_AxC3 "antC3" -#define KEY_FILE_AxC4 "antC4" -#define KEY_FILE_AxC5 "antC5" -#define KEY_FILE_AxC6 "antC6" -#define KEY_FILE_AxC7 "antC7" -#define KEY_FILE_AxC8 "antC8" -#define KEY_FILE_AxC9 "antC9" -#define KEY_FILE_AxC10 "antC10" -#define KEY_FILE_AxC11 "antC11" -#define KEY_FILE_AxC12 "antC12" -#define KEY_FILE_AxC13 "antC13" -#define KEY_FILE_AxC14 "antC14" -#define KEY_FILE_AxC15 "antC15" +#define KEY_FILE_NUMSLOTS "numSlots" +#define KEY_FILE_AxC "antC" +#define KEY_FILE_PRACH_AxC "antPrachC" + #define KEY_PRACH_ENABLE "rachEanble" -#define KEY_PRACH_OFFSET "rachOffset" -#define KEY_PRACH_CFG_IDX "rachCfgIdx" +#define KEY_PRACH_CFGIDX "prachConfigIndex" #define KEY_IQ_SWAP "iqswap" #define KEY_HTONS_SWAP "nebyteorderswap" @@ -89,6 +91,9 @@ #define KEY_CP_VTAG "c_plane_vlan_tag" #define KEY_UP_VTAG "u_plane_vlan_tag" #define KEY_DEBUG_STOP "debugStop" +#define KEY_DEBUG_STOP_CNT "debugStopCount" +#define KEY_BBDEV_MODE "bbdevMode" +#define KEY_DYNA_SEC_ENA "DynamicSectionEna" /** @@ -123,6 +128,65 @@ static int fillConfigStruct(RuntimeConfig *config, const char *key, const char * config->numCC= atoi(value); } else if (strcmp(key, KEY_MU_NUMBER) == 0) { config->mu_number= atoi(value); + printf("mu_number: %d\n",config->mu_number); + } else if (strcmp(key, KEY_NDLABSFREPOINTA) == 0) { + config->nDLAbsFrePointA = atoi(value); + printf("nDLAbsFrePointA: %d\n",config->nDLAbsFrePointA); + } else if (strcmp(key, KEY_NULABSFREPOINTA) == 0) { + config->nULAbsFrePointA = atoi(value); + printf("nULAbsFrePointA: %d\n",config->nULAbsFrePointA); + } else if (strcmp(key, KEY_NDLBANDWIDTH) == 0) { + config->nDLBandwidth = atoi(value); + printf("nDLBandwidth: %d\n",config->nDLBandwidth); + } else if (strcmp(key, KEY_NULBANDWIDTH) == 0) { + config->nULBandwidth = atoi(value); + printf("nULBandwidth: %d\n",config->nULBandwidth); + } else if (strcmp(key, KEY_NDLFFTSIZE) == 0) { + config->nDLFftSize = atoi(value); + printf("nULFftSize: %d\n",config->nDLFftSize); + } else if (strcmp(key, KEY_NULFFTSIZE) == 0) { + config->nULFftSize = atoi(value); + printf("nULFftSize: %d\n",config->nULFftSize); + } else if (strcmp(key, KEY_NFRAMEDUPLEXTYPE) == 0) { + config->nFrameDuplexType = atoi(value); + printf("nFrameDuplexType: %d\n",config->nFrameDuplexType); + } else if (strcmp(key, KEY_NTDDPERIOD) == 0) { + config->nTddPeriod = atoi(value); + printf("nTddPeriod: %d\n",config->nTddPeriod); + if (config->nTddPeriod > XRAN_MAX_TDD_PERIODICITY) + { + printf("nTddPeriod is larger than max allowed, invalid!\n"); + config->nTddPeriod = XRAN_MAX_TDD_PERIODICITY; + } + } else if (strncmp(key, KEY_SSLOTCONFIG, strlen(KEY_SSLOTCONFIG)) == 0) { + unsigned int slot_num = 0; + int i = 0; + sscanf(key,"sSlotConfig%u",&slot_num); + if (slot_num >= config->nTddPeriod){ + printf("slot_num %d exceeds TddPeriod\n",slot_num); + } + else{ + sscanf(value, "%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[0], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[1], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[2], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[3], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[4], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[5], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[6], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[7], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[8], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[9], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[10], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[11], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[12], + (uint32_t*)&config->sSlotConfig[slot_num].nSymbolType[13]); + printf("sSlotConfig%d: ",slot_num); + for (i = 0; i< 14; i++){ + printf("%d ",config->sSlotConfig[slot_num].nSymbolType[i]); + } + printf("\n"); + } } else if (strcmp(key, KEY_ANT_NUM) == 0) { config->numAxc = atoi(value); } else if (strcmp(key, KEY_TTI_PERIOD) == 0) { @@ -131,95 +195,71 @@ static int fillConfigStruct(RuntimeConfig *config, const char *key, const char * config->iqswap = atoi(value); } else if (strcmp(key, KEY_HTONS_SWAP) == 0) { config->nebyteorderswap = atoi(value); + } else if (strcmp(key, KEY_MTU_SIZE) == 0) { + config->mtu = atoi(value); + printf("mtu %d\n", config->mtu); } else if (strcmp(key, KEY_LLS_CU_MAC) == 0) { - sscanf(value, "%02x:%02x:%02x:%02x:%02x:%02x", (uint32_t*)&config->lls_cu_addr.addr_bytes[0], - (uint32_t*)&config->lls_cu_addr.addr_bytes[1], - (uint32_t*)&config->lls_cu_addr.addr_bytes[2], - (uint32_t*)&config->lls_cu_addr.addr_bytes[3], - (uint32_t*)&config->lls_cu_addr.addr_bytes[4], - (uint32_t*)&config->lls_cu_addr.addr_bytes[5]); + sscanf(value, "%02x:%02x:%02x:%02x:%02x:%02x", (uint32_t*)&config->o_du_addr.addr_bytes[0], + (uint32_t*)&config->o_du_addr.addr_bytes[1], + (uint32_t*)&config->o_du_addr.addr_bytes[2], + (uint32_t*)&config->o_du_addr.addr_bytes[3], + (uint32_t*)&config->o_du_addr.addr_bytes[4], + (uint32_t*)&config->o_du_addr.addr_bytes[5]); printf("lls-CU MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", - config->lls_cu_addr.addr_bytes[0], - config->lls_cu_addr.addr_bytes[1], - config->lls_cu_addr.addr_bytes[2], - config->lls_cu_addr.addr_bytes[3], - config->lls_cu_addr.addr_bytes[4], - config->lls_cu_addr.addr_bytes[5]); + config->o_du_addr.addr_bytes[0], + config->o_du_addr.addr_bytes[1], + config->o_du_addr.addr_bytes[2], + config->o_du_addr.addr_bytes[3], + config->o_du_addr.addr_bytes[4], + config->o_du_addr.addr_bytes[5]); } else if (strcmp(key, KEY_RU_MAC) == 0) { - sscanf(value, "%02x:%02x:%02x:%02x:%02x:%02x", (uint32_t*)&config->ru_addr.addr_bytes[0], - (uint32_t*)&config->ru_addr.addr_bytes[1], - (uint32_t*)&config->ru_addr.addr_bytes[2], - (uint32_t*)&config->ru_addr.addr_bytes[3], - (uint32_t*)&config->ru_addr.addr_bytes[4], - (uint32_t*)&config->ru_addr.addr_bytes[5]); + sscanf(value, "%02x:%02x:%02x:%02x:%02x:%02x", (uint32_t*)&config->o_ru_addr.addr_bytes[0], + (uint32_t*)&config->o_ru_addr.addr_bytes[1], + (uint32_t*)&config->o_ru_addr.addr_bytes[2], + (uint32_t*)&config->o_ru_addr.addr_bytes[3], + (uint32_t*)&config->o_ru_addr.addr_bytes[4], + (uint32_t*)&config->o_ru_addr.addr_bytes[5]); printf("RU MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", - config->ru_addr.addr_bytes[0], - config->ru_addr.addr_bytes[1], - config->ru_addr.addr_bytes[2], - config->ru_addr.addr_bytes[3], - config->ru_addr.addr_bytes[4], - config->ru_addr.addr_bytes[5]); - } else if (strcmp(key, KEY_FILE_AxC0) == 0) { - strncpy(&config->ant_file[0][0], value, strlen(value)); - printf("ant0: %s\n",config->ant_file[0]); - } else if (strcmp(key, KEY_FILE_AxC1) == 0) { - strncpy(&config->ant_file[1][0], value, strlen(value)); - printf("ant1: %s\n",config->ant_file[1]); - } else if (strcmp(key, KEY_FILE_AxC2) == 0) { - strncpy(&config->ant_file[2][0], value, strlen(value)); - printf("ant2: %s\n",config->ant_file[2]); - } else if (strcmp(key, KEY_FILE_AxC3) == 0) { - strncpy(&config->ant_file[3][0], value, strlen(value)); - printf("ant3: %s\n",config->ant_file[3]); - } else if (strcmp(key, KEY_FILE_AxC4) == 0) { - strncpy(&config->ant_file[4][0], value, strlen(value)); - printf("ant4: %s\n",config->ant_file[4]); - } else if (strcmp(key, KEY_FILE_AxC5) == 0) { - strncpy(&config->ant_file[5][0], value, strlen(value)); - printf("ant5: %s\n",config->ant_file[5]); - } else if (strcmp(key, KEY_FILE_AxC6) == 0) { - strncpy(&config->ant_file[6][0], value, strlen(value)); - printf("ant6: %s\n",config->ant_file[6]); - } else if (strcmp(key, KEY_FILE_AxC7) == 0) { - strncpy(&config->ant_file[7][0], value, strlen(value)); - printf("ant7: %s\n",config->ant_file[7]); - } else if (strcmp(key, KEY_FILE_AxC8) == 0) { - strncpy(&config->ant_file[8][0], value, strlen(value)); - printf("ant8: %s\n",config->ant_file[8]); - } else if (strcmp(key, KEY_FILE_AxC9) == 0) { - strncpy(&config->ant_file[9][0], value, strlen(value)); - printf("ant9: %s\n",config->ant_file[9]); - } else if (strcmp(key, KEY_FILE_AxC10) == 0) { - strncpy(&config->ant_file[10][0], value, strlen(value)); - printf("ant10: %s\n",config->ant_file[10]); - } else if (strcmp(key, KEY_FILE_AxC11) == 0) { - strncpy(&config->ant_file[11][0], value, strlen(value)); - printf("ant11: %s\n",config->ant_file[11]); - } else if (strcmp(key, KEY_FILE_AxC12) == 0) { - strncpy(&config->ant_file[12][0], value, strlen(value)); - printf("ant12: %s\n",config->ant_file[12]); - } else if (strcmp(key, KEY_FILE_AxC13) == 0) { - strncpy(&config->ant_file[13][0], value, strlen(value)); - printf("ant13: %s\n",config->ant_file[13]); - } else if (strcmp(key, KEY_FILE_AxC14) == 0) { - strncpy(&config->ant_file[14][0], value, strlen(value)); - printf("ant14: %s\n",config->ant_file[14]); - } else if (strcmp(key, KEY_FILE_AxC15) == 0) { - strncpy(&config->ant_file[15][0], value, strlen(value)); - printf("ant15: %s\n",config->ant_file[15]); + config->o_ru_addr.addr_bytes[0], + config->o_ru_addr.addr_bytes[1], + config->o_ru_addr.addr_bytes[2], + config->o_ru_addr.addr_bytes[3], + config->o_ru_addr.addr_bytes[4], + config->o_ru_addr.addr_bytes[5]); + } else if (strcmp(key, KEY_FILE_NUMSLOTS) == 0) { + config->numSlots = atoi(value); + printf("numSlots: %d\n",config->numSlots); + }else if (strncmp(key, KEY_FILE_AxC, strlen(KEY_FILE_AxC)) == 0) { + unsigned int ant_num = 0; + sscanf(key,"antC%02u",&ant_num); + if (ant_num >= MAX_ANT_CARRIER_SUPPORTED) + { + printf("antC%d exceeds max antenna supported\n",ant_num); + } + else{ + strncpy(&config->ant_file[ant_num][0], value, strlen(value)); + printf("antC%d: %s\n",ant_num, config->ant_file[ant_num]); + } } else if (strcmp(key, KEY_PRACH_ENABLE) == 0) { config->enablePrach = atoi(value); printf("Prach enable: %d\n",config->enablePrach); - } else if (strcmp(key, KEY_PRACH_OFFSET ) == 0) { - config->prachOffset = atoi(value); - printf("Prach Offset: %d\n",config->prachOffset); - } else if (strcmp(key, KEY_PRACH_CFG_IDX ) == 0) { + } else if (strcmp(key, KEY_PRACH_CFGIDX) == 0) { config->prachConfigIndex = atoi(value); - printf("Prach Conf Index: %d\n",config->prachConfigIndex); - + printf("Prach config index: %d\n",config->prachConfigIndex); + } else if (strncmp(key, KEY_FILE_PRACH_AxC, strlen(KEY_FILE_PRACH_AxC)) == 0) { + unsigned int ant_num = 0; + sscanf(key,"antPrachC%02u",&ant_num); + if (ant_num >= MAX_ANT_CARRIER_SUPPORTED) + { + printf("antC%d exceeds max antenna supported\n",ant_num); + } + else{ + strncpy(&config->prach_file[ant_num][0], value, strlen(value)); + printf("antPrachC%d: %s\n",ant_num, config->prach_file[ant_num]); + } /* timing */ } else if (strcmp(key, KEY_TADV_CP_DL ) == 0) { config->Tadv_cp_dl = atoi(value); @@ -279,6 +319,15 @@ static int fillConfigStruct(RuntimeConfig *config, const char *key, const char * } else if (strcmp(key, KEY_DEBUG_STOP ) == 0) { config->debugStop = atoi(value); printf("debugStop: %d\n",config->debugStop); + } else if (strcmp(key, KEY_DEBUG_STOP_CNT) == 0) { + config->debugStopCount = atoi(value); + printf("debugStopCount: %d\n",config->debugStopCount); + } else if (strcmp(key, KEY_BBDEV_MODE) == 0) { + config->bbdevMode = atoi(value); + printf("bbdevMode: %d\n",config->debugStopCount); + } else if (strcmp(key, KEY_DYNA_SEC_ENA) == 0) { + config->DynamicSectionEna = atoi(value); + printf("DynamicSectionEna: %d\n",config->DynamicSectionEna); } else if (strcmp(key, KEY_CP_VTAG ) == 0) { config->cp_vlan_tag = atoi(value); printf("cp_vlan_tag: %d\n",config->cp_vlan_tag); @@ -304,11 +353,11 @@ int parseConfigFile(char *filename, RuntimeConfig *config) FILE *file = fopen(filename, "r"); if (NULL == file) { - //log_err("Error while opening config file from: %s", filename); + log_err("Error while opening config file from: %s", filename); return -1; } - init_config(config); +// init_config(config); for (;;) { if (fgets(inputLine, MAX_LINE_SIZE, file) == NULL) { @@ -341,7 +390,7 @@ int parseConfigFile(char *filename, RuntimeConfig *config) key[i] = '\0'; trim(key); if ((i + 1 > inputLen - 1) || (i - 2 > inputLen)) { - //log_err("Parsing config file error at line %d", lineNum); + log_err("Parsing config file error at line %d", lineNum); fclose(file); return -1; } diff --git a/fhi_lib/app/src/config.h b/fhi_lib/app/src/config.h index b2953ee..c223b0b 100644 --- a/fhi_lib/app/src/config.h +++ b/fhi_lib/app/src/config.h @@ -29,6 +29,7 @@ #include #include +#include "xran_fh_o_du.h" /** Run time configuration of application */ typedef struct _RuntimeConfig @@ -36,13 +37,18 @@ typedef struct _RuntimeConfig uint8_t appMode; /**< Application mode: lls-CU or RU */ uint8_t xranMode; /**< xran mode: Categoty A | Category B */ uint8_t numCC; /**< Number of CC per ports supported by RU */ - uint8_t mu_number; /**< Mu numner as per 3GPP */ uint8_t numAxc; /**< Number of Antenna Carriers per CC */ uint32_t ttiPeriod; /**< TTI period */ uint32_t testVect; /**< Test Signal to send */ - struct ether_addr lls_cu_addr; /**< lls-CU Ethernet Mac Address */ - struct ether_addr ru_addr; /**< RU Ethernet Mac Address */ - char ant_file[16][512] /**< file to use for test vector */ ; + struct ether_addr o_du_addr; /**< lls-CU Ethernet Mac Address */ + struct ether_addr o_ru_addr; /**< RU Ethernet Mac Address */ + struct ether_addr tmp_addr; /**< Temp Ethernet Mac Address */ + + uint32_t mtu; /**< maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) */ + int numSlots; /**< number of slots in IQ vector */ + char ant_file[XRAN_MAX_SECTOR_NR*XRAN_MAX_ANTENNA_NR][512] /**< file to use for test vector */ ; + char prach_file[XRAN_MAX_SECTOR_NR*XRAN_MAX_ANTENNA_NR][512] /**< file to use for test vector */ ; /* prach config */ uint8_t enablePrach; /**< enable PRACH */ @@ -76,8 +82,22 @@ typedef struct _RuntimeConfig uint8_t up_vlan_tag; /**< U-plane vlan tag */ int32_t debugStop; + int32_t debugStopCount; + int32_t bbdevMode; + int32_t DynamicSectionEna; + + uint8_t mu_number; /**< Mu numner as per 3GPP */ + uint32_t nDLAbsFrePointA; /**< Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 */ + uint32_t nULAbsFrePointA; /**< Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 */ + uint32_t nDLBandwidth; /**< Carrier bandwidth for in MHz. Value: 5->400 */ + uint32_t nULBandwidth; /**< Carrier bandwidth for in MHz. Value: 5->400 */ + uint32_t nDLFftSize; /**< DL FFT size */ + uint32_t nULFftSize; /**< UL FFT size */ + uint8_t nFrameDuplexType; + uint8_t nTddPeriod; + struct xran_slot_config sSlotConfig[XRAN_MAX_TDD_PERIODICITY]; } RuntimeConfig; /** diff --git a/fhi_lib/app/src/debug.h b/fhi_lib/app/src/debug.h new file mode 100644 index 0000000..d0482a8 --- /dev/null +++ b/fhi_lib/app/src/debug.h @@ -0,0 +1,82 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +/** + * @brief + * @file + * @ingroup + * @author Intel Corporation + **/ + +#ifndef _SAMPLEAPP__DEBUG_H_ +#define _SAMPLEAPP__DEBUG_H_ + +#include + +#include "config.h" + +#define MAX_FILE_NAME_LEN (512) +#define MAX_PATH_NAME_LEN (1024) + +#ifdef _DEBUG + #define log_dbg(fmt, ...) \ + fprintf(stderr, \ + "DEBUG: %s(%d): " fmt "\n", \ + __FILE__, \ + __LINE__, ##__VA_ARGS__) +#else + #define log_dbg(fmt, ...) +#endif + +#if defined(_DEBUG) || defined(_VERBOSE) + #define log_wrn(fmt, ...) \ + fprintf( \ + stderr, \ + "WARNING: %s(%d): " fmt "\n", \ + __FILE__, \ + __LINE__, ##__VA_ARGS__) +#else + #define log_dbg(fmt, ...) + #define log_wrn(fmt, ...) +#endif + + +#define log_err(fmt, ...) \ + fprintf(stderr, \ + "ERROR: %s(%d): " fmt "\n", \ + __FILE__, \ + __LINE__, ##__VA_ARGS__) + + +inline void ShowData(void* ptr, unsigned int size) +{ + uint8_t *d = (uint8_t *)ptr; + unsigned int i; + + for(i = 0; i < size; i++) + { + if ( !(i & 0xf) ) + printf("\n"); + printf("%02x ", d[i]); + } + printf("\n"); +} + + +#endif /* _SAMPLEAPP__DEBUG_H_ */ diff --git a/fhi_lib/app/lls-cu/sample-lls-cu.c b/fhi_lib/app/src/sample-app.c similarity index 52% rename from fhi_lib/app/lls-cu/sample-lls-cu.c rename to fhi_lib/app/src/sample-app.c index 31ffb4b..f45cf94 100644 --- a/fhi_lib/app/lls-cu/sample-lls-cu.c +++ b/fhi_lib/app/src/sample-app.c @@ -16,6 +16,7 @@ * *******************************************************************************/ + #define _GNU_SOURCE #include #include @@ -27,25 +28,24 @@ #include #include #include +#include #include +#include +#include + #include "common.h" #include "config.h" +#include "xran_mlog_lnx.h" -#ifndef MLOG_ENABLED -#include "../../lib/src/mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif - - -#include "xran_fh_lls_cu.h" -//#include "xran_pkt.h" -//#include "xran_up_api.h" +#include "xran_fh_o_du.h" #include "xran_cp_api.h" #include "xran_sync_api.h" #include "xran_mlog_task_id.h" +#define MAX_BBU_POOL_CORE_MASK (4) + + #define SW_FPGA_TOTAL_BUFFER_LEN 4*1024*1024*1024 #define SW_FPGA_SEGMENT_BUFFER_LEN 1*1024*1024*1024 #define SW_FPGA_FH_TOTAL_BUFFER_LEN 1*1024*1024*1024 @@ -67,16 +67,16 @@ static uint64_t tsc_resolution_hz = 0; RuntimeConfig startupConfiguration = {0}; -//FH FPGA buffer +/* buffers size */ uint32_t nFpgaToSW_FTH_RxBufferLen; uint32_t nFpgaToSW_PRACH_RxBufferLen; uint32_t nSW_ToFpga_FTH_TxBufferLen; -static XRANFHINIT xranInit; +static struct xran_fh_init xranInit; void * xranHandle = NULL; -XRANFHCONFIG xranConf; -PXRANFHCONFIG pXranConf = NULL; +struct xran_fh_config xranConf; +struct xran_fh_config *pXranConf = NULL; typedef struct { @@ -100,7 +100,9 @@ typedef struct XranLibConfig }XranLibConfigStruct; typedef enum { XRANFTHTX_OUT = 0, + XRANFTHTX_PRB_MAP_OUT, XRANFTHRX_IN, + XRANFTHRX_PRB_MAP_IN, XRANFTHRACH_IN, MAX_SW_XRAN_INTERFACE_NUM }SWXRANInterfaceTypeEnum; @@ -121,7 +123,7 @@ typedef struct { // -1 means that DL packet to be transmitted is not ready in BS int32_t nSegTransferred; // number of data segments has been transmitted or received struct rte_mbuf *pData[N_MAX_BUFFER_SEGMENT]; // point to DPDK allocated memory pool - XRANBufferListStruct sBufferList; + struct xran_buffer_list sBufferList; } BbuIoBufCtrlStruct; typedef struct { @@ -134,32 +136,31 @@ typedef struct { /* io struct */ BbuIoBufCtrlStruct sFrontHaulTxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + BbuIoBufCtrlStruct sFrontHaulTxPrbMapBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; BbuIoBufCtrlStruct sFrontHaulRxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + BbuIoBufCtrlStruct sFrontHaulRxPrbMapBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; BbuIoBufCtrlStruct sFHPrachRxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; /* buffers lists */ - XRANFlatBufferStruct sFrontHaulTxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; - XRANFlatBufferStruct sFrontHaulRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; - XRANFlatBufferStruct sFHPrachRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulTxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulTxPrbMapBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + struct xran_flat_buffer sFrontHaulRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulRxPrbMapBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + struct xran_flat_buffer sFHPrachRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; void* nInstanceHandle[XRAN_PORTS_NUM][XRAN_MAX_SECTOR_NR]; // instance per sector uint32_t nBufPoolIndex[XRAN_MAX_SECTOR_NR][MAX_SW_XRAN_INTERFACE_NUM]; // every api owns unique buffer pool uint16_t nInstanceNum; - /*subframe type for this TTI: - 0: DL control + DL data - 1: DL control + DL data + UL control - 2: DL control + UL data - 3: DL control + UL data + UL control - */ - uint8_t nSubframeType; - uint64_t nTscTiming[XRAN_N_FE_BUF_LEN]; // records the TSC when a timing packet is received. } BbuXranIoIfStruct; static BbuXranIoIfStruct gsXranIoIf; static XranLibConfigStruct *gpXranLibConfig = NULL; +extern long rx_counter; +extern long tx_counter; + #define CPU_HZ tick_per_usec //us /* Application User space functions */ @@ -180,15 +181,44 @@ static void print_menu() puts("+---------------------------------------+"); } -void xran_fh_rx_callback(void *pCallbackTag, XranStatusInt32 status) +static int32_t get_xran_sfidx(uint8_t nNrOfSlotInSf) +{ + int32_t nSfIdx = -1; + uint32_t nFrameIdx; + uint32_t nSubframeIdx; + uint32_t nSlotIdx; + uint64_t nSecond; + + uint32_t nXranTime = xran_get_slot_idx(&nFrameIdx, &nSubframeIdx, &nSlotIdx, &nSecond); + nSfIdx = nFrameIdx*NUM_OF_SUBFRAME_PER_FRAME*nNrOfSlotInSf + + nSubframeIdx*nNrOfSlotInSf + + nSlotIdx; +#if 0 + printf("\nxranTime is %d, return is %d, radio frame is %d, subframe is %d slot is %d tsc is %llu us", + nXranTime, + nSfIdx, + nFrameIdx, + nSubframeIdx, + nSlotIdx, + __rdtsc()/CPU_HZ); +#endif + + return nSfIdx; +} + +void xran_fh_rx_callback(void *pCallbackTag, xran_status_t status) { uint64_t t1 = MLogTick(); uint32_t mlogVar[10]; uint32_t mlogVarCnt = 0; + uint8_t Numerlogy = xranConf.frame_conf.nNumerology; + uint8_t nNrOfSlotInSf = 1<> 16; /* tti */ mlogVar[mlogVarCnt++] = status & 0xFF; /* sym */ + mlogVar[mlogVarCnt++] = (uint32_t)sfIdx; MLogAddVariables(mlogVarCnt, mlogVar, MLogTick()); rte_pause(); @@ -196,10 +226,18 @@ void xran_fh_rx_callback(void *pCallbackTag, XranStatusInt32 status) return; } -void xran_fh_rx_prach_callback(void *pCallbackTag, XranStatusInt32 status) +void xran_fh_rx_prach_callback(void *pCallbackTag, xran_status_t status) { uint64_t t1 = MLogTick(); + uint32_t mlogVar[10]; + uint32_t mlogVarCnt = 0; + + mlogVar[mlogVarCnt++] = 0xDDDDDDDD; + mlogVar[mlogVarCnt++] = status >> 16; /* tti */ + mlogVar[mlogVarCnt++] = status & 0xFF; /* sym */ + MLogAddVariables(mlogVarCnt, mlogVar, MLogTick()); rte_pause(); + MLogTask(PID_GNB_PRACH_CB, t1, MLogTick()); } @@ -304,12 +342,13 @@ int physide_ul_full_slot_call_back(void * param) int32_t init_xran(void) { BbuXranIoIfStruct *psBbuIo = xran_get_ctx(); - XranStatusInt32 status; + xran_status_t status; int32_t nSectorIndex[XRAN_MAX_SECTOR_NR]; int32_t nSectorNum; int32_t i, j, k, z; void *ptr; + void *mb; uint32_t *u32dptr; uint16_t *u16dptr; uint8_t *u8dptr; @@ -318,23 +357,12 @@ int32_t init_xran(void) XranLibConfigStruct *ptrLibConfig; - XRANBufferListStruct *pFthTxBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; - XRANBufferListStruct *pFthRxBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; - XRANBufferListStruct *pFthRxRachBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; - - FPGAPhaseCompCfg *pPhaseCompDl = NULL; - FPGAPhaseCompCfg *pPhaseCompUl = NULL; - uint32_t nPhaseCompDl,nPhaseCompUl; + struct xran_buffer_list *pFthTxBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; + struct xran_buffer_list *pFthTxPrbMapBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; + struct xran_buffer_list *pFthRxBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; + struct xran_buffer_list *pFthRxPrbMapBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; + struct xran_buffer_list *pFthRxRachBuffer[XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; - -#if 0 - printf("init_xran: nFpgaProbe[%d] nSecNum[%d] nUENum[%d] nTimeAdvance[%d] nEthPorts[%d] nPhaseCompFlag[%d]\n", - psFPGAInitPara->nFpgaProbe, psFPGAInitPara->nSecNum, psFPGAInitPara->nUENum, psFPGAInitPara->nTimeAdvance, psFPGAInitPara->nEthPorts, psFPGAInitPara->nPhaseCompFlag); - for (i = 0; i < nSectorNum; i ++) - { - printf(" [%d]: nDlArfcn[%d] nUlArfcn[%d]\n", i, psFPGAInitPara->nDlArfcn[i], psFPGAInitPara->nUlArfcn[i]); - } -#endif for (nSectorNum = 0; nSectorNum < XRAN_MAX_SECTOR_NR; nSectorNum++) { nSectorIndex[nSectorNum] = nSectorNum; @@ -352,12 +380,15 @@ int32_t init_xran(void) psBbuIo->nInstanceNum = numCCPorts; for (k = 0; k < XRAN_PORTS_NUM; k++) { - status = xran_sector_get_instances (xranHandle, psBbuIo->nInstanceNum,psBbuIo->nInstanceHandle[k]); + status = xran_sector_get_instances (xranHandle, psBbuIo->nInstanceNum,&psBbuIo->nInstanceHandle[k][0]); if (status != XRAN_STATUS_SUCCESS) { printf ("get sector instance failed %d for XRAN nInstanceNum %d\n",k, psBbuIo->nInstanceNum); exit(-1); } + for (i = 0; i < psBbuIo->nInstanceNum; i++){ + printf("%s [%d]: CC %d handle %p\n", __FUNCTION__, k, i, psBbuIo->nInstanceHandle[0][i]); + } } printf("Sucess xran_mm_init \n"); @@ -365,7 +396,7 @@ int32_t init_xran(void) ptrLibConfig = gpXranLibConfig; if (ptrLibConfig) { - #if 0 + #if 0 ptrLibConfig->nDriverCoreId = psBbuIo->nDriverCoreId; ptrLibConfig->pFecInstanceHandles = &(psBbuIo->nInstanceHandle[FPGA_FEC][0]); ptrLibConfig->pFthInstanceHandles = &(psBbuIo->nInstanceHandle[FPGA_FRONTHAUL][0]); @@ -378,7 +409,7 @@ int32_t init_xran(void) { ptrLibConfig->nSectorNum = psFPGAInitPara->nSecNum; } - #endif + #endif } else { @@ -392,7 +423,8 @@ int32_t init_xran(void) for(i = 0; inBufPoolIndex[nSectorIndex[i]][eInterfaceType], + printf("nSectorIndex[%d] = %d\n",i, nSectorIndex[i]); + status = xran_bm_init(psBbuIo->nInstanceHandle[0][i], &psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType], XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, nSW_ToFpga_FTH_TxBufferLen); if(XRAN_STATUS_SUCCESS != status) { @@ -414,13 +446,14 @@ int32_t init_xran(void) psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nElementLenInBytes = nSW_ToFpga_FTH_TxBufferLen; // 14 symbols 3200bytes/symbol psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nNumberOfElements = 1; psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nOffsetInBytes = 0; - status = xran_bm_allocate_buffer(xranHandle,psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr); + status = xran_bm_allocate_buffer(psBbuIo->nInstanceHandle[0][i], psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr, &mb); if(XRAN_STATUS_SUCCESS != status) { printf("Failed at xran_bm_allocate_buffer , status %d\n",status); iAssert(status == XRAN_STATUS_SUCCESS); } psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData = (uint8_t *)ptr; + psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pCtrl = (void *)mb; if(ptr){ u32dptr = (uint32_t*)(ptr); @@ -434,12 +467,58 @@ int32_t init_xran(void) } } } + + /* C-plane DL */ + eInterfaceType = XRANFTHTX_PRB_MAP_OUT; + printf("nSectorIndex[%d] = %d\n",i, nSectorIndex[i]); + status = xran_bm_init(psBbuIo->nInstanceHandle[0][i], &psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType], + XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, sizeof(struct xran_prb_map)); + if(XRAN_STATUS_SUCCESS != status) + { + printf("Failed at xran_bm_init , status %d\n", status); + iAssert(status == XRAN_STATUS_SUCCESS); + } + for(j = 0; j < XRAN_N_FE_BUF_LEN; j++) + { + for(z = 0; z < XRAN_MAX_ANTENNA_NR; z++){ + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].bValid = 0; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &psBbuIo->sFrontHaulTxPrbMapBuffers[j][i][z]; + + { + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nElementLenInBytes = sizeof(struct xran_prb_map); + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nNumberOfElements = 1; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nOffsetInBytes = 0; + status = xran_bm_allocate_buffer(psBbuIo->nInstanceHandle[0][i], psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr, &mb); + if(XRAN_STATUS_SUCCESS != status) + { + printf("Failed at xran_bm_allocate_buffer , status %d\n",status); + iAssert(status == XRAN_STATUS_SUCCESS); + } + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->pData = (uint8_t *)ptr; + psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->pCtrl = (void *)mb; + + if(ptr){ + u32dptr = (uint32_t*)(ptr); + uint8_t *ptr_temp = (uint8_t *)ptr; + memset(u32dptr, 0xCC, sizeof(struct xran_prb_map)); + ptr_temp[0] = j; // TTI + ptr_temp[1] = i; // Sec + ptr_temp[2] = z; // Ant + ptr_temp[3] = k; // sym + } + } + } + } } for(i = 0; inBufPoolIndex[nSectorIndex[i]][eInterfaceType], XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, nSW_ToFpga_FTH_TxBufferLen); + status = xran_bm_init(psBbuIo->nInstanceHandle[0][i], &psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType], XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, nSW_ToFpga_FTH_TxBufferLen); if(XRAN_STATUS_SUCCESS != status) { printf("Failed at xran_bm_init, status %d\n", status); @@ -460,13 +539,14 @@ int32_t init_xran(void) psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nElementLenInBytes = nFpgaToSW_FTH_RxBufferLen; // 1 symbols 3200bytes psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nNumberOfElements = 1; psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nOffsetInBytes = 0; - status = xran_bm_allocate_buffer(xranHandle,psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr); + status = xran_bm_allocate_buffer(psBbuIo->nInstanceHandle[0][i],psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr, &mb); if(XRAN_STATUS_SUCCESS != status) { - printf("Failed at cpa_bb_bm_allocate_buffer , status %d\n",status); + printf("Failed at xran_bm_allocate_buffer , status %d\n",status); iAssert(status == XRAN_STATUS_SUCCESS); } psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData = (uint8_t *)ptr; + psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pCtrl = (void *) mb; if(ptr){ u32dptr = (uint32_t*)(ptr); uint8_t *ptr_temp = (uint8_t *)ptr; @@ -479,13 +559,57 @@ int32_t init_xran(void) } } } + + /* C-plane */ + eInterfaceType = XRANFTHRX_PRB_MAP_IN; + status = xran_bm_init(psBbuIo->nInstanceHandle[0][i], &psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType], + XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, sizeof(struct xran_prb_map)); + if(XRAN_STATUS_SUCCESS != status) + { + printf("Failed at xran_bm_init, status %d\n", status); + iAssert(status == XRAN_STATUS_SUCCESS); + } + + for(j = 0;j < XRAN_N_FE_BUF_LEN; j++) + { + for(z = 0; z < XRAN_MAX_ANTENNA_NR; z++){ + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].bValid = 0; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &psBbuIo->sFrontHaulRxPrbMapBuffers[j][i][z]; + { + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nElementLenInBytes = sizeof(struct xran_prb_map); + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nNumberOfElements = 1; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->nOffsetInBytes = 0; + status = xran_bm_allocate_buffer(psBbuIo->nInstanceHandle[0][i],psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr, &mb); + if(XRAN_STATUS_SUCCESS != status) + { + printf("Failed at xran_bm_allocate_buffer , status %d\n",status); + iAssert(status == XRAN_STATUS_SUCCESS); + } + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->pData = (uint8_t *)ptr; + psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers->pCtrl = (void *)mb; + if(ptr){ + u32dptr = (uint32_t*)(ptr); + uint8_t *ptr_temp = (uint8_t *)ptr; + memset(u32dptr, 0xCC, sizeof(struct xran_prb_map)); + ptr_temp[0] = j; // TTI + ptr_temp[1] = i; // Sec + ptr_temp[2] = z; // Ant + ptr_temp[3] = k; // sym + } + } + } + } } // add prach rx buffer for(i = 0; inBufPoolIndex[nSectorIndex[i]][eInterfaceType],XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, FPGA_TO_SW_PRACH_RX_BUFFER_LEN); + status = xran_bm_init(psBbuIo->nInstanceHandle[0][i],&psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],XRAN_N_FE_BUF_LEN*XRAN_MAX_ANTENNA_NR*XRAN_NUM_OF_SYMBOL_PER_SLOT, FPGA_TO_SW_PRACH_RX_BUFFER_LEN); if(XRAN_STATUS_SUCCESS != status) { printf("Failed at xran_bm_init, status %d\n", status); @@ -500,19 +624,19 @@ int32_t init_xran(void) psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_MAX_ANTENNA_NR; // ant number. psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &psBbuIo->sFHPrachRxBuffers[j][i][z][0]; - //for(k = 0; k< XRAN_NUM_OF_SYMBOL_PER_SLOT; k++) - k = 0; // one PRACH buffer per antenna per slot + for(k = 0; k< XRAN_NUM_OF_SYMBOL_PER_SLOT; k++) { psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nElementLenInBytes = FPGA_TO_SW_PRACH_RX_BUFFER_LEN; psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nNumberOfElements = 1; psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].nOffsetInBytes = 0; - status = xran_bm_allocate_buffer(xranHandle,psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr); + status = xran_bm_allocate_buffer(psBbuIo->nInstanceHandle[0][i],psBbuIo->nBufPoolIndex[nSectorIndex[i]][eInterfaceType],&ptr, &mb); if(XRAN_STATUS_SUCCESS != status) { printf("Failed at xran_bm_allocate_buffer, status %d\n",status); iAssert(status == XRAN_STATUS_SUCCESS); } psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData = (uint8_t *)ptr; + psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pCtrl = (void *)mb; if(ptr){ u32dptr = (uint32_t*)(ptr); memset(u32dptr, 0xCC, FPGA_TO_SW_PRACH_RX_BUFFER_LEN); @@ -528,7 +652,9 @@ int32_t init_xran(void) { for(z = 0; z < XRAN_MAX_ANTENNA_NR; z++){ pFthTxBuffer[i][z][j] = &(psBbuIo->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList); + pFthTxPrbMapBuffer[i][z][j] = &(psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList); pFthRxBuffer[i][z][j] = &(psBbuIo->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList); + pFthRxPrbMapBuffer[i][z][j] = &(psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList); pFthRxRachBuffer[i][z][j] = &(psBbuIo->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList); } } @@ -540,7 +666,9 @@ int32_t init_xran(void) { xran_5g_fronthault_config (psBbuIo->nInstanceHandle[0][i], pFthTxBuffer[i], + pFthTxPrbMapBuffer[i], pFthRxBuffer[i], + pFthRxPrbMapBuffer[i], xran_fh_rx_callback, &pFthRxBuffer[i][0]); } @@ -553,37 +681,13 @@ int32_t init_xran(void) ptrLibConfig->nFhBufIntFlag = 1; } - /*config phase compensation*/ - /* TODO: add phase compensation handling */ - for(i=0; iNRARFCN = 0;//psFPGAInitPara->nDlArfcn[i]; - pPhaseCompDl->phaseFlag = 0;//psFPGAInitPara->nPhaseCompFlag; - pPhaseCompDl->SULFlag = 0; - pPhaseCompDl->SULFreShift = 0; - pPhaseCompDl->rsv = 0; - - pPhaseCompUl = (FPGAPhaseCompCfg *)(&nPhaseCompUl); - pPhaseCompUl->NRARFCN = 0;//psFPGAInitPara->nUlArfcn[i]; - pPhaseCompUl->phaseFlag = 0;// psFPGAInitPara->nPhaseCompFlag; - pPhaseCompUl->SULFlag = 0; - pPhaseCompUl->SULFreShift = 0; - pPhaseCompUl->rsv = 0; - - xran_5g_pre_compenstor_cfg(psBbuIo->nInstanceHandle[0][i],nPhaseCompDl,nPhaseCompUl,i); - - printf("@@@ NB cell %d DL NR-ARFCN %d,DL phase comp flag %d UL NR-ARFCN %d,UL phase comp flag %d \n", - i,pPhaseCompDl->NRARFCN,pPhaseCompDl->phaseFlag, - pPhaseCompUl->NRARFCN,pPhaseCompUl->phaseFlag); - } return status; } int init_xran_iq_content(void) { BbuXranIoIfStruct *psBbuIo = xran_get_ctx(); - XranStatusInt32 status; + xran_status_t status; int32_t nSectorIndex[XRAN_MAX_SECTOR_NR]; int32_t nSectorNum; int32_t cc_id, ant_id, sym_id, tti; @@ -600,6 +704,7 @@ int init_xran_iq_content(void) uint8_t *u8dptr; char *pos = NULL; + struct xran_prb_map *pRbMap = NULL; for (nSectorNum = 0; nSectorNum < XRAN_MAX_SECTOR_NR; nSectorNum++) { @@ -614,17 +719,16 @@ int init_xran_iq_content(void) for(tti = 0; tti < XRAN_N_FE_BUF_LEN; tti ++) { for(ant_id = 0; ant_id < XRAN_MAX_ANTENNA_NR; ant_id++){ for(sym_id = 0; sym_id < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_id++) { - flowId = nSectorNum * ant_id + cc_id; + + flowId = XRAN_MAX_ANTENNA_NR*cc_id + ant_id; if(p_tx_play_buffer[flowId]){ - /* (0-79 slots) 10ms of IQs */ pos = ((char*)p_tx_play_buffer[flowId]) + tx_play_buffer_position[flowId]; - ptr = psBbuIo->sFrontHaulTxBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; - if(ptr){ + if(ptr && pos){ u32dptr = (uint32_t*)(ptr); - rte_memcpy(u32dptr, pos, PDSCH_PAYLOAD_SIZE); + rte_memcpy(u32dptr, pos, pXranConf->nDLRBs*N_SC_PER_PRB*4); #ifdef DEBUG_XRAN_BUFFERS uint8_t *ptr_temp = (uint8_t *)ptr; ptr_temp[0] = tti; // TTI @@ -632,10 +736,52 @@ int init_xran_iq_content(void) ptr_temp[2] = ant_id; // Ant ptr_temp[3] = sym_id; // sym #endif - }else + } else { + exit(-1); printf("ptr ==NULL\n"); - - tx_play_buffer_position[flowId] += PDSCH_PAYLOAD_SIZE; + } + + /* c-plane DL */ + pRbMap = (struct xran_prb_map *) psBbuIo->sFrontHaulTxPrbMapBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers->pData; + if(pRbMap){ + pRbMap->dir = XRAN_DIR_DL; + pRbMap->xran_port = 0; + pRbMap->band_id = 0; + pRbMap->cc_id = cc_id; + pRbMap->ru_port_id = ant_id; + pRbMap->tti_id = tti; + pRbMap->start_sym_id = 0; + pRbMap->nPrbElm = 1; + pRbMap->prbMap[0].nRBStart = 0; + pRbMap->prbMap[0].nRBSize = pXranConf->nDLRBs; + pRbMap->prbMap[0].nBeamIndex = 0; + pRbMap->prbMap[0].compMethod = XRAN_COMPMETHOD_NONE; + }else{ + printf("DL pRbMap ==NULL\n"); + exit(-1); + } + + /* c-plane UL */ + pRbMap = (struct xran_prb_map *) psBbuIo->sFrontHaulRxPrbMapBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers->pData; + if(pRbMap){ + pRbMap->dir = XRAN_DIR_UL; + pRbMap->xran_port = 0; + pRbMap->band_id = 0; + pRbMap->cc_id = cc_id; + pRbMap->ru_port_id = ant_id; + pRbMap->tti_id = tti; + pRbMap->start_sym_id = 0; + pRbMap->nPrbElm = 1; + pRbMap->prbMap[0].nRBStart = 0; + pRbMap->prbMap[0].nRBSize = pXranConf->nULRBs; + pRbMap->prbMap[0].nBeamIndex = 0; + pRbMap->prbMap[0].compMethod = XRAN_COMPMETHOD_NONE; + }else { + printf("UL: pRbMap ==NULL\n"); + exit(-1); + } + + tx_play_buffer_position[flowId] += pXranConf->nDLRBs*N_SC_PER_PRB*4; if(tx_play_buffer_position[flowId] >= tx_play_buffer_size[flowId]) tx_play_buffer_position[flowId] = 0; @@ -644,8 +790,40 @@ int init_xran_iq_content(void) } } } - } + /* prach TX for RU only */ + if(startupConfiguration.appMode == APP_O_RU && startupConfiguration.enablePrach){ + for(ant_id = 0; ant_id < XRAN_MAX_ANTENNA_NR; ant_id++){ + for(sym_id = 0; sym_id < 1; sym_id++) { + flowId = XRAN_MAX_ANTENNA_NR*cc_id + ant_id; + + if(p_tx_prach_play_buffer[flowId]){ + /* (0-79 slots) 10ms of IQs */ + pos = ((char*)p_tx_prach_play_buffer[flowId]); + + ptr = psBbuIo->sFHPrachRxBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; + + if(ptr && pos){ + u32dptr = (uint32_t*)(ptr); + rte_memcpy(u32dptr, pos, PRACH_PLAYBACK_BUFFER_BYTES); +#ifdef DEBUG_XRAN_BUFFERS + uint8_t *ptr_temp = (uint8_t *)ptr; + ptr_temp[0] = tti; // TTI + ptr_temp[1] = cc_id; // Sec + ptr_temp[2] = ant_id; // Ant + ptr_temp[3] = sym_id; // sym +#endif + } else { + exit(-1); + printf("ptr ==NULL\n"); + } + } else { + //printf("flowId %d\n", flowId); + } + } + } + } + } } return 0; @@ -653,7 +831,7 @@ int init_xran_iq_content(void) void stop_xran(void) { - XranStatusInt32 status = 0; + xran_status_t status = 0; SWXRANInterfaceTypeEnum eInterfaceType; free(gpXranLibConfig); @@ -668,35 +846,10 @@ void stop_xran(void) } } -int32_t get_xran_sfidx(uint8_t nNrOfSlotInSf) -{ - int32_t nSfIdx = -1; - uint32_t nFrameIdx; - uint32_t nSubframeIdx; - uint32_t nSlotIdx; - uint64_t nSecond; - - uint32_t nXranTime = xran_get_slot_idx(&nFrameIdx, &nSubframeIdx, &nSlotIdx, &nSecond); - nSfIdx = nFrameIdx*NUM_OF_SUBFRAME_PER_FRAME*nNrOfSlotInSf - + nSubframeIdx*nNrOfSlotInSf - + nSlotIdx; -#if 0 - printf("\nxranTime is %d, return is %d, radio frame is %d, subframe is %d slot is %d tsc is %llu us", - nXranTime, - nSfIdx, - nFrameIdx, - nSubframeIdx, - nSlotIdx, - __rdtsc()/CPU_HZ); -#endif - - return nSfIdx; -} - int get_xran_iq_content(void) { BbuXranIoIfStruct *psBbuIo = xran_get_ctx(); - XranStatusInt32 status; + xran_status_t status; int32_t nSectorIndex[XRAN_MAX_SECTOR_NR]; int32_t nSectorNum; int32_t cc_id, ant_id, sym_id, tti; @@ -727,14 +880,14 @@ int get_xran_iq_content(void) for(tti = 0; tti < XRAN_N_FE_BUF_LEN; tti++) { for(ant_id = 0; ant_id < XRAN_MAX_ANTENNA_NR; ant_id++){ for(sym_id = 0; sym_id < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_id++) { - flowId = nSectorNum * ant_id + cc_id; + flowId = XRAN_MAX_ANTENNA_NR * cc_id + ant_id; if(p_rx_log_buffer[flowId]){ /* (0-79 slots) 10ms of IQs */ pos = ((char*)p_rx_log_buffer[flowId]) + rx_log_buffer_position[flowId]; ptr = psBbuIo->sFrontHaulRxBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; if(ptr){ u32dptr = (uint32_t*)(ptr); - rte_memcpy(pos, u32dptr, PDSCH_PAYLOAD_SIZE); + rte_memcpy(pos, u32dptr, pXranConf->nULRBs*N_SC_PER_PRB*4); #ifdef DEBUG_XRAN_BUFFERS if (pos[0] != tti|| pos[1] != cc_id || @@ -746,7 +899,7 @@ int get_xran_iq_content(void) }else printf("ptr ==NULL\n"); - rx_log_buffer_position[flowId] += PDSCH_PAYLOAD_SIZE; + rx_log_buffer_position[flowId] += pXranConf->nULRBs*N_SC_PER_PRB*4; if(rx_log_buffer_position[flowId] >= rx_log_buffer_size[flowId]) rx_log_buffer_position[flowId] = 0; @@ -754,6 +907,39 @@ int get_xran_iq_content(void) //printf("flowId %d\n", flowId); } } + + /* prach RX for O-DU only */ + if(startupConfiguration.appMode == APP_O_DU){ + flowId = XRAN_MAX_ANTENNA_NR * cc_id + ant_id; + sym_id = 0; + + if(p_prach_log_buffer[flowId]){ + /* (0-79 slots) 10ms of IQs */ + pos = ((char*)p_prach_log_buffer[flowId]) + prach_log_buffer_position[flowId]; + ptr = psBbuIo->sFHPrachRxBbuIoBufCtrl[tti][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; + if(ptr){ + u32dptr = (uint32_t*)(ptr); + rte_memcpy(pos, u32dptr, PRACH_PLAYBACK_BUFFER_BYTES); +#ifdef DEBUG_XRAN_BUFFERS + if (pos[0] != tti|| + pos[1] != cc_id || + pos[2] != ant_id || + pos[3] != sym_id){ + printf("[flowId %d] %d %d %d %d\n", flowId, pos[0], pos[1], pos[2], pos[3]); + } +#endif + }else + printf("ptr ==NULL\n"); + + prach_log_buffer_position[flowId] += PRACH_PLAYBACK_BUFFER_BYTES; + + if(prach_log_buffer_position[flowId] >= prach_log_buffer_size[flowId]) + prach_log_buffer_position[flowId] = 0; + } else { + //printf("flowId %d\n", flowId); + } + } + } } } @@ -761,15 +947,65 @@ int get_xran_iq_content(void) return 0; } +void version_print(void) +{ + char sysversion[100]; + char *compilation_date = __DATE__; + char *compilation_time = __TIME__; + + uint32_t nLen; + uint32_t i; + + snprintf(sysversion, 99, "Version: %s", VERSIONX); + nLen = strlen(sysversion); + + printf("\n\n"); + printf("===========================================================================================================\n"); + printf("SAMPLE-APP VERSION\n"); + printf("===========================================================================================================\n"); + + printf("%s\n", sysversion); + printf("build-date: %s\n", compilation_date); + printf("build-time: %s\n", compilation_time); +} + int main(int argc, char *argv[]) { int i; - int j; + int j, len; int lcore_id = 0; char filename[64]; + uint32_t nCenterFreq; + int32_t xret = 0; + struct stat st = {0}; + uint32_t filenameLength = strlen(argv[1]); + char *pCheckName1 = NULL, *pCheckName2 = NULL; + enum xran_if_state xran_curr_if_state = XRAN_INIT; if (argc == 3) errx(2, "Need two argument - the PCI address of the network port"); + if (filenameLength >= 64) + { + printf("Config file name input is too long, exiting!\n"); + exit(-1); + } + + version_print(); + + //add for Klocworks + len = strlen(argv[1]) + 1; + if (len > (sizeof(filename) - 10)) + len = (sizeof(filename) - 10); + strncpy(filename, argv[1], (sizeof(filename) - 10)); + filename[len] = '\0'; + + pCheckName1 = strstr(filename, "config_file_o_du.dat"); + pCheckName2 = strstr(filename, "config_file_o_ru.dat"); + if ((pCheckName1 == NULL) && (pCheckName2 == NULL)) + { + printf("config file name %s is not valid!\n", filename); + exit(-1); + } if (xran_is_synchronized() != 0) printf("Machine is not synchronized using PTP!\n"); @@ -778,7 +1014,7 @@ int main(int argc, char *argv[]) memset(&startupConfiguration, 0, sizeof(RuntimeConfig)); - if (parseConfigFile(argv[1], &startupConfiguration) != 0) { + if (parseConfigFile(filename, (RuntimeConfig*)&startupConfiguration) != 0) { printf("Configuration file error.\n"); return 0; } @@ -787,21 +1023,34 @@ int main(int argc, char *argv[]) printf("it looks like test vector for antennas were not provided\n"); exit(-1); } + if (startupConfiguration.numCC > XRAN_MAX_SECTOR_NR) + { + printf("Number of cells %d exceeds max number supported %d!\n", startupConfiguration.numCC, XRAN_MAX_SECTOR_NR); + startupConfiguration.numCC = XRAN_MAX_SECTOR_NR; + } numCCPorts = startupConfiguration.numCC; num_eAxc = startupConfiguration.numAxc; printf("numCCPorts %d num_eAxc%d\n", numCCPorts, num_eAxc); - /* Numerology 3 */ - nFpgaToSW_FTH_RxBufferLen = 3328; //3200 * 14; - nFpgaToSW_PRACH_RxBufferLen = 8192; - nSW_ToFpga_FTH_TxBufferLen = 3328; //3200; * 14; + if (startupConfiguration.mu_number <= 1){ + nFpgaToSW_FTH_RxBufferLen = 13168; /* 273*12*4 + 64*/ + nFpgaToSW_PRACH_RxBufferLen = 8192; + nSW_ToFpga_FTH_TxBufferLen = 13168; /* 273*12*4 + 64*/ + } else if (startupConfiguration.mu_number == 3){ + nFpgaToSW_FTH_RxBufferLen = 3328; + nFpgaToSW_PRACH_RxBufferLen = 8192; + nSW_ToFpga_FTH_TxBufferLen = 3328; + } else { + printf("given numerology is not supported %d\n", startupConfiguration.mu_number); + exit(-1); + } - memset(&xranInit, 0, sizeof(XRANFHINIT)); + memset(&xranInit, 0, sizeof(struct xran_fh_init)); - if(startupConfiguration.appMode == APP_LLS_CU) { - printf("set lls-CU\n"); + if(startupConfiguration.appMode == APP_O_DU) { + printf("set O-DU\n"); xranInit.io_cfg.id = 0;//ID_LLS_CU; xranInit.io_cfg.core = 4+1; xranInit.io_cfg.system_core = 0; @@ -809,7 +1058,7 @@ int main(int argc, char *argv[]) xranInit.io_cfg.pkt_aux_core = 0; /* do not start*/ xranInit.io_cfg.timing_core = 4+3; } else { - printf("set RU\n"); + printf("set O-DU\n"); xranInit.io_cfg.id = 1; /* ID_LLS_CU;*/ xranInit.io_cfg.core = 1; xranInit.io_cfg.system_core = 0; @@ -818,8 +1067,7 @@ int main(int argc, char *argv[]) xranInit.io_cfg.timing_core = 3; } - xranInit.llscuId = 0; // for ecpriRtcid/ecpriPcid - xranInit.nSec = 1; // shall be one + xranInit.io_cfg.bbdev_mode = XRAN_BBDEV_NOT_USED; xranInit.eAxCId_conf.mask_cuPortId = 0xf000; xranInit.eAxCId_conf.mask_bandSectorId = 0x0f00; @@ -832,9 +1080,12 @@ int main(int argc, char *argv[]) xranInit.io_cfg.dpdk_dev[XRAN_UP_VF] = argv[2]; xranInit.io_cfg.dpdk_dev[XRAN_CP_VF] = argv[3]; - xranInit.p_lls_cu_addr = (int8_t*)&startupConfiguration.lls_cu_addr; - xranInit.p_ru_addr = (int8_t*)&startupConfiguration.ru_addr; - xranInit.ttiPeriod = startupConfiguration.ttiPeriod; + xranInit.mtu = startupConfiguration.mtu; + + xranInit.p_o_du_addr = (int8_t*)&startupConfiguration.o_du_addr; + xranInit.p_o_ru_addr = (int8_t*)&startupConfiguration.o_ru_addr; + xranInit.filePrefix = "wls"; + xranInit.xranCat = XRAN_CATRGORY_A; xranInit.Tadv_cp_dl = startupConfiguration.Tadv_cp_dl; xranInit.T2a_min_cp_dl = startupConfiguration.T2a_min_cp_dl; @@ -854,17 +1105,30 @@ int main(int argc, char *argv[]) xranInit.Ta4_min = startupConfiguration.Ta4_min; xranInit.Ta4_max = startupConfiguration.Ta4_max; - xranInit.enableCP = startupConfiguration.enableCP; - xranInit.debugStop = startupConfiguration.debugStop; + xranInit.enableCP = startupConfiguration.enableCP; + xranInit.prachEnable = startupConfiguration.enablePrach; + xranInit.debugStop = startupConfiguration.debugStop; + xranInit.debugStopCount = startupConfiguration.debugStopCount; + xranInit.DynamicSectionEna = startupConfiguration.DynamicSectionEna; + xranInit.io_cfg.bbdev_mode = XRAN_BBDEV_NOT_USED; //startupConfiguration.bbdevMode; + + xranInit.cp_vlan_tag = startupConfiguration.cp_vlan_tag; + xranInit.up_vlan_tag = startupConfiguration.up_vlan_tag; - xranInit.cp_vlan_tag = startupConfiguration.cp_vlan_tag; - xranInit.up_vlan_tag = startupConfiguration.up_vlan_tag; + printf("IQ files size is %d slots\n", startupConfiguration.numSlots); + iq_playback_buffer_size_dl = (startupConfiguration.numSlots * N_SYM_PER_SLOT * N_SC_PER_PRB * + app_xran_get_num_rbs(startupConfiguration.mu_number, startupConfiguration.nDLBandwidth, startupConfiguration.nDLAbsFrePointA) + *4L); + + iq_playback_buffer_size_ul = (startupConfiguration.numSlots * N_SYM_PER_SLOT * N_SC_PER_PRB * + app_xran_get_num_rbs(startupConfiguration.mu_number, startupConfiguration.nULBandwidth, startupConfiguration.nULAbsFrePointA) + *4L); for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - p_tx_play_buffer[i] = (int16_t*)malloc(IQ_PLAYBACK_BUFFER_BYTES); - tx_play_buffer_size[i] = (int32_t)IQ_PLAYBACK_BUFFER_BYTES; + p_tx_play_buffer[i] = (int16_t*)malloc(iq_playback_buffer_size_dl); + tx_play_buffer_size[i] = (int32_t)iq_playback_buffer_size_dl; if (p_tx_play_buffer[i] == NULL) exit(-1); @@ -877,11 +1141,27 @@ int main(int argc, char *argv[]) tx_play_buffer_position[i] = 0; } + if (startupConfiguration.appMode == APP_O_RU && startupConfiguration.enablePrach){ + for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { + p_tx_prach_play_buffer[i] = (int16_t*)malloc(PRACH_PLAYBACK_BUFFER_BYTES); + tx_prach_play_buffer_size[i] = (int32_t)PRACH_PLAYBACK_BUFFER_BYTES; + + if (p_tx_prach_play_buffer[i] == NULL) + exit(-1); + + tx_prach_play_buffer_size[i] = sys_load_file_to_buff(startupConfiguration.prach_file[i], + "PRACH IQ Samples in binary format", + (uint8_t*) p_tx_prach_play_buffer[i], + tx_prach_play_buffer_size[i], + 1); + tx_prach_play_buffer_position[i] = 0; + } + } /* log of ul */ for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - p_rx_log_buffer[i] = (int16_t*)malloc(IQ_PLAYBACK_BUFFER_BYTES); - rx_log_buffer_size[i] = (int32_t)IQ_PLAYBACK_BUFFER_BYTES; + p_rx_log_buffer[i] = (int16_t*)malloc(iq_playback_buffer_size_ul); + rx_log_buffer_size[i] = (int32_t)iq_playback_buffer_size_ul; if (p_rx_log_buffer[i] == NULL) exit(-1); @@ -894,8 +1174,8 @@ int main(int argc, char *argv[]) /* log of Prach */ for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - p_prach_log_buffer[i] = (int16_t*)malloc(PRACH_PLAYBACK_BUFFER_BYTES); - prach_log_buffer_size[i] = (int32_t)PRACH_PLAYBACK_BUFFER_BYTES; + p_prach_log_buffer[i] = (int16_t*)malloc(startupConfiguration.numSlots*PRACH_PLAYBACK_BUFFER_BYTES); + prach_log_buffer_size[i] = (int32_t)startupConfiguration.numSlots*PRACH_PLAYBACK_BUFFER_BYTES; if (p_prach_log_buffer[i] == NULL) exit(-1); @@ -904,21 +1184,41 @@ int main(int argc, char *argv[]) prach_log_buffer_position[i] = 0; } + if (stat("./logs", &st) == -1) { + mkdir("./logs", 0777); + } + for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "%s-play_ant%d.txt",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); + sprintf(filename, "./logs/%s-play_ant%d.txt",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); sys_save_buf_to_file_txt(filename, "DL IFFT IN IQ Samples in human readable format", (uint8_t*) p_tx_play_buffer[i], tx_play_buffer_size[i], 1); - sprintf(filename, "%s-play_ant%d.bin",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); + sprintf(filename, "./logs/%s-play_ant%d.bin",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); sys_save_buf_to_file(filename, "DL IFFT IN IQ Samples in binary format", (uint8_t*) p_tx_play_buffer[i], tx_play_buffer_size[i]/sizeof(short), sizeof(short)); + + if (startupConfiguration.appMode == APP_O_RU && startupConfiguration.enablePrach){ + sprintf(filename, "./logs/%s-play_prach_ant%d.txt",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); + sys_save_buf_to_file_txt(filename, + "DL IFFT IN IQ Samples in human readable format", + (uint8_t*) p_tx_prach_play_buffer[i], + tx_prach_play_buffer_size[i], + 1); + + sprintf(filename, "./logs/%s-play_prach_ant%d.bin",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); + sys_save_buf_to_file(filename, + "DL IFFT IN IQ Samples in binary format", + (uint8_t*) p_tx_prach_play_buffer[i], + tx_prach_play_buffer_size[i]/sizeof(short), + sizeof(short)); + } } if (startupConfiguration.iqswap == 1){ for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { @@ -936,12 +1236,30 @@ int main(int argc, char *argv[]) } } } + if (startupConfiguration.appMode == APP_O_RU){ + for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { + printf("PRACH: Swap I and Q to match RU format: [%d]\n",i); + { + /* swap I and Q */ + int32_t j; + signed short *ptr = (signed short *) p_tx_prach_play_buffer[i]; + signed short temp; + + for (j = 0; j < (int32_t)(tx_prach_play_buffer_size[i]/sizeof(short)) ; j = j + 2){ + temp = ptr[j]; + ptr[j] = ptr[j + 1]; + ptr[j + 1] = temp; + } + } + } + } + } #if 0 for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "swap_IQ_play_ant%d.txt", i); + sprintf(filename, "./logs/swap_IQ_play_ant%d.txt", i); sys_save_buf_to_file_txt(filename, "DL IFFT IN IQ Samples in human readable format", (uint8_t*) p_tx_play_buffer[i], @@ -956,12 +1274,21 @@ int main(int argc, char *argv[]) p_tx_play_buffer[i][j] = rte_cpu_to_be_16(p_tx_play_buffer[i][j]); } } + + if (startupConfiguration.appMode == APP_O_RU){ + for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { + printf("PRACH: Convert S16 I and S16 Q to network byte order for XRAN Ant: [%d]\n",i); + for (j = 0; j < tx_prach_play_buffer_size[i]/sizeof(short); j++){ + p_tx_prach_play_buffer[i][j] = rte_cpu_to_be_16(p_tx_prach_play_buffer[i][j]); + } + } + } } #if 0 for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "swap_be_play_ant%d.txt", i); + sprintf(filename, "./logs/swap_be_play_ant%d.txt", i); sys_save_buf_to_file_txt(filename, "DL IFFT IN IQ Samples in human readable format", (uint8_t*) p_tx_play_buffer[i], @@ -970,37 +1297,62 @@ int main(int argc, char *argv[]) } #endif + timer_set_tsc_freq_from_clock(); - xran_init(argc, argv, &xranInit, argv[0], &xranHandle); + xret = xran_init(argc, argv, &xranInit, argv[0], &xranHandle); + if(xret != XRAN_STATUS_SUCCESS){ + printf("xran_init failed %d\n", xret); + exit(-1); + } + if(xranHandle == NULL) exit(1); - memset(&xranConf, 0, sizeof(XRANFHCONFIG)); + memset(&xranConf, 0, sizeof(struct xran_fh_config)); pXranConf = &xranConf; - for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - pXranConf->playback_conf.TxPlayBufAddr[i] = (unsigned long)p_tx_play_buffer[i]; - pXranConf->playback_conf.TxPlayBufSize = tx_play_buffer_size[i]; - } - pXranConf->sector_id = 0; pXranConf->nCC = numCCPorts; pXranConf->neAxc = num_eAxc; - pXranConf->frame_conf.nFrameDuplexType = 1; // TDD - pXranConf->frame_conf.nNumerology = startupConfiguration.mu_number; // 120KHz; same as XRAN_SCS_120KHz? -// pXranConf->frame_conf.nTddPeriod = ; + pXranConf->frame_conf.nFrameDuplexType = startupConfiguration.nFrameDuplexType; + pXranConf->frame_conf.nNumerology = startupConfiguration.mu_number; + pXranConf->frame_conf.nTddPeriod = startupConfiguration.nTddPeriod; - pXranConf->prach_conf.nPrachSubcSpacing = XRAN_SCS_120KHZ; + for (i = 0; i < startupConfiguration.nTddPeriod; i++){ + pXranConf->frame_conf.sSlotConfig[i] = startupConfiguration.sSlotConfig[i]; + } + + pXranConf->prach_conf.nPrachSubcSpacing = startupConfiguration.mu_number; pXranConf->prach_conf.nPrachFreqStart = 0; pXranConf->prach_conf.nPrachFilterIdx = XRAN_FILTERINDEX_PRACH_ABC; - pXranConf->prach_conf.nPrachConfIdx = 81; + pXranConf->prach_conf.nPrachConfIdx = startupConfiguration.prachConfigIndex; pXranConf->prach_conf.nPrachFreqOffset = -792; pXranConf->ru_conf.iqWidth = 16; pXranConf->ru_conf.compMeth = XRAN_COMPMETHOD_NONE; - pXranConf->ru_conf.fftSize = XRAN_FFTSIZE_2048; + pXranConf->ru_conf.fftSize = 0; + while (startupConfiguration.nULFftSize >>= 1) + ++pXranConf->ru_conf.fftSize; + + pXranConf->ru_conf.byteOrder = (startupConfiguration.nebyteorderswap == 1) ? XRAN_NE_BE_BYTE_ORDER : XRAN_CPU_LE_BYTE_ORDER ; + pXranConf->ru_conf.iqOrder = (startupConfiguration.iqswap == 1) ? XRAN_Q_I_ORDER : XRAN_I_Q_ORDER; + + printf("FFT Order %d\n", pXranConf->ru_conf.fftSize); + pXranConf->nDLRBs = app_xran_get_num_rbs(startupConfiguration.mu_number, startupConfiguration.nDLBandwidth, startupConfiguration.nDLAbsFrePointA); + pXranConf->nULRBs = app_xran_get_num_rbs(startupConfiguration.mu_number, startupConfiguration.nULBandwidth, startupConfiguration.nULAbsFrePointA); + + nCenterFreq = startupConfiguration.nDLAbsFrePointA + (((pXranConf->nDLRBs * N_SC_PER_PRB) / 2) * app_xran_get_scs(startupConfiguration.mu_number)); + pXranConf->nDLCenterFreqARFCN = app_xran_cal_nrarfcn(nCenterFreq); + printf("DL center freq %d DL NR-ARFCN %d\n", nCenterFreq, pXranConf->nDLCenterFreqARFCN); + + nCenterFreq = startupConfiguration.nULAbsFrePointA + (((pXranConf->nULRBs * N_SC_PER_PRB) / 2) * app_xran_get_scs(startupConfiguration.mu_number)); + pXranConf->nULCenterFreqARFCN = app_xran_cal_nrarfcn(nCenterFreq); + printf("UL center freq %d UL NR-ARFCN %d\n", nCenterFreq, pXranConf->nULCenterFreqARFCN); + + pXranConf->bbdev_dec = NULL; + pXranConf->bbdev_enc = NULL; if(init_xran() != 0) exit(-1); @@ -1011,31 +1363,62 @@ int main(int argc, char *argv[]) init_xran_iq_content(); - xran_open(xranHandle, pXranConf); + xret = xran_open(xranHandle, pXranConf); + + if(xret != XRAN_STATUS_SUCCESS){ + printf("xran_open failed %d\n", xret); + exit(-1); + } + + sprintf(filename, "mlog-%s", startupConfiguration.appMode == 0 ? "o-du" : "o-ru"); + +// MLogOpen(0, 32, 0, 0xFFFFFFFF, filename); - sprintf(filename, "mlog-%s", startupConfiguration.appMode == 0 ? "lls-cu" : "ru"); + MLogOpen(256, 3, 20000, 0xFFFFFFFF, filename); - MLogOpen(0, 32, 0, 0xFFFFFFFF, filename); puts("----------------------------------------"); printf("MLog Info: virt=0x%016lx size=%d\n", MLogGetFileLocation(), MLogGetFileSize()); puts("----------------------------------------"); + + uint64_t nActiveCoreMask[MAX_BBU_POOL_CORE_MASK] = {0}; + nActiveCoreMask[0] = 1 << xranInit.io_cfg.timing_core; + uint32_t numCarriers = startupConfiguration.numCC; + + MLogAddTestCase(nActiveCoreMask, numCarriers); + + fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); + state = APP_RUNNING; - sleep(6); + printf("Start XRAN traffic\n"); + xran_start(xranHandle); + sleep(3); + print_menu(); for (;;) { - print_menu(); + struct xran_common_counters x_counters; char input[10]; - int sel_opt; -//#ifdef Nightly_build -// sel_opt = 3; -// sleep(10); -//#else - if (NULL == fgets(input, 10, stdin)) { - state = APP_STOPPED; + sleep(1); + xran_curr_if_state = xran_get_if_state(); + if(xran_get_common_counters(xranHandle, &x_counters) == XRAN_STATUS_SUCCESS) { + printf("rx %ld tx %ld [on_time %ld early %ld late %ld corrupt %ld pkt_dupl %ld Total %ld\n", rx_counter, tx_counter, + x_counters.Rx_on_time, + x_counters.Rx_early, + x_counters.Rx_late, + x_counters.Rx_corrupt, + x_counters.Rx_pkt_dupl, + x_counters.Total_msgs_rcvd); + } else { + printf("error xran_get_common_counters\n"); + } + + if (xran_curr_if_state == XRAN_STOPPED){ break; } - sel_opt = atoi(input); -//#endif + if (NULL == fgets(input, 10, stdin)) { + continue; + } + + const int sel_opt = atoi(input); switch (sel_opt) { case 1: xran_start(xranHandle); @@ -1048,18 +1431,10 @@ int main(int argc, char *argv[]) printf("Stop XRAN traffic\n"); state = APP_STOPPED; break; - case 4: -// send_cpmsg_dlul(XRAN_DIR_DL, flowId, -// frame_id, subframe_id, slot_id, -// 0, XRAN_SYMBOLPERSLOT_MAX, NUM_OF_PRB_IN_FULL_BAND, -// beam_id, cc_id, ant_id, -// cp_seq_id_num[XRAN_DIR_DL][ant_id]++); - break; default: puts("Wrong option passed!"); break; } - if (APP_STOPPED == state) break; } @@ -1102,14 +1477,14 @@ int main(int argc, char *argv[]) for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "%s-rx_log_ant%d.txt",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); + sprintf(filename, "./logs/%s-rx_log_ant%d.txt",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); sys_save_buf_to_file_txt(filename, "UL FFT OUT IQ Samples in human readable format", (uint8_t*) p_rx_log_buffer[i], rx_log_buffer_size[i], 1); - sprintf(filename, "%s-rx_log_ant%d.bin",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); + sprintf(filename, "./logs/%s-rx_log_ant%d.bin",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); sys_save_buf_to_file(filename, "UL FFT OUT IQ Samples in binary format", (uint8_t*) p_rx_log_buffer[i], @@ -1117,48 +1492,52 @@ int main(int argc, char *argv[]) sizeof(short)); } - if (startupConfiguration.iqswap == 1){ - for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - printf("PRACH: Swap I and Q to match CPU format: [%d]\n",i); - { - /* swap I and Q */ - int32_t j; - signed short *ptr = (signed short *) p_prach_log_buffer[i]; - signed short temp; - - for (j = 0; j < (int32_t)(prach_log_buffer_size[i]/sizeof(short)) ; j = j + 2){ - temp = ptr[j]; - ptr[j] = ptr[j + 1]; - ptr[j + 1] = temp; + if (startupConfiguration.appMode == APP_O_DU && startupConfiguration.enablePrach){ + if (startupConfiguration.iqswap == 1){ + for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { + printf("PRACH: Swap I and Q to match CPU format: [%d]\n",i); + { + /* swap I and Q */ + int32_t j; + signed short *ptr = (signed short *) p_prach_log_buffer[i]; + signed short temp; + + for (j = 0; j < (int32_t)(prach_log_buffer_size[i]/sizeof(short)) ; j = j + 2){ + temp = ptr[j]; + ptr[j] = ptr[j + 1]; + ptr[j + 1] = temp; + } } } } - } - if (startupConfiguration.nebyteorderswap == 1){ - for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - printf("PRACH: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [%d]\n",i); - for (j = 0; j < prach_log_buffer_size[i]/sizeof(short); j++){ - p_prach_log_buffer[i][j] = rte_be_to_cpu_16(p_prach_log_buffer[i][j]); + + if (startupConfiguration.nebyteorderswap == 1){ + for(i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { + printf("PRACH: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [%d]\n",i); + for (j = 0; j < prach_log_buffer_size[i]/sizeof(short); j++){ + p_prach_log_buffer[i][j] = rte_be_to_cpu_16(p_prach_log_buffer[i][j]); + } } } - } - for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "%s-prach_log_ant%d.txt",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); - sys_save_buf_to_file_txt(filename, - "PRACH FFT OUT IQ Samples in human readable format", - (uint8_t*) p_prach_log_buffer[i], - prach_log_buffer_size[i], - 1); + for (i = 0; i < MAX_ANT_CARRIER_SUPPORTED && i < (uint32_t)(numCCPorts * num_eAxc); i++) { - sprintf(filename, "%s-prach_log_ant%d.bin",((startupConfiguration.appMode == APP_LLS_CU) ? "lls-cu" : "ru"), i); - sys_save_buf_to_file(filename, - "PRACH FFT OUT IQ Samples in binary format", - (uint8_t*) p_prach_log_buffer[i], - prach_log_buffer_size[i]/sizeof(short), - sizeof(short)); + sprintf(filename, "./logs/%s-prach_log_ant%d.txt",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); + sys_save_buf_to_file_txt(filename, + "PRACH FFT OUT IQ Samples in human readable format", + (uint8_t*) p_prach_log_buffer[i], + prach_log_buffer_size[i], + 1); + + sprintf(filename, "./logs/%s-prach_log_ant%d.bin",((startupConfiguration.appMode == APP_O_DU) ? "o-du" : "o-ru"), i); + sys_save_buf_to_file(filename, + "PRACH FFT OUT IQ Samples in binary format", + (uint8_t*) p_prach_log_buffer[i], + prach_log_buffer_size[i]/sizeof(short), + sizeof(short)); + } } return 0; diff --git a/fhi_lib/app/src/xran_mlog_task_id.h b/fhi_lib/app/src/xran_mlog_task_id.h index 23a673c..ed6c11d 100644 --- a/fhi_lib/app/src/xran_mlog_task_id.h +++ b/fhi_lib/app/src/xran_mlog_task_id.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file has the System Debug Trace Logger (Mlog) Task IDs used by PHY * @file mlog_task_id.h @@ -27,6 +26,10 @@ #ifndef _XRAN_TASK_ID_H_ #define _XRAN_TASK_ID_H_ +#ifdef __cplusplus +extern "C" { +#endif + #define RESOURCE_CORE_0 0 #define RESOURCE_CORE_1 1 #define RESOURCE_CORE_2 2 @@ -51,11 +54,14 @@ // XRAN APP //-------------------------------------------------------------------- -#define PID_GNB_PROC_TIMING 902 -#define PID_GNB_PROC_TIMING_TIMEOUT 903 -#define PID_GNB_SYM_CB 904 -#define PID_GNB_PRACH_CB 905 +#define PID_GNB_PROC_TIMING 70 +#define PID_GNB_PROC_TIMING_TIMEOUT 71 +#define PID_GNB_SYM_CB 72 +#define PID_GNB_PRACH_CB 73 +#ifdef __cplusplus +} +#endif #endif /* _XRAN_TASK_ID_H_ */ diff --git a/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_du.dat b/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_du.dat new file mode 100644 index 0000000..6bea1cf --- /dev/null +++ b/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_du.dat @@ -0,0 +1,139 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=12 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app +#ruMac=3c:fd:fe:9e:93:68 #RU PF for tcpdump + +numSlots=40 #number of slots per IQ files + +antC0=./usecase/mu0_10mhz/12/ant_0.bin #CC0 +antC1=./usecase/mu0_10mhz/12/ant_1.bin #CC0 +antC2=./usecase/mu0_10mhz/12/ant_2.bin #CC0 +antC3=./usecase/mu0_10mhz/12/ant_3.bin #CC0 +antC4=./usecase/mu0_10mhz/12/ant_4.bin #CC1 +antC5=./usecase/mu0_10mhz/12/ant_5.bin #CC1 +antC6=./usecase/mu0_10mhz/12/ant_6.bin #CC1 +antC7=./usecase/mu0_10mhz/12/ant_7.bin #CC1 +antC8=./usecase/mu0_10mhz/12/ant_8.bin #CC2 +antC9=./usecase/mu0_10mhz/12/ant_9.bin #CC2 +antC10=./usecase/mu0_10mhz/12/ant_10.bin #CC2 +antC11=./usecase/mu0_10mhz/12/ant_11.bin #CC2 +antC12=./usecase/mu0_10mhz/12/ant_12.bin #CC3 +antC13=./usecase/mu0_10mhz/12/ant_13.bin #CC3 +antC14=./usecase/mu0_10mhz/12/ant_14.bin #CC3 +antC15=./usecase/mu0_10mhz/12/ant_15.bin #CC3 +antC16=./usecase/mu0_10mhz/12/ant_0.bin #CC4 +antC17=./usecase/mu0_10mhz/12/ant_1.bin #CC4 +antC18=./usecase/mu0_10mhz/12/ant_2.bin #CC4 +antC19=./usecase/mu0_10mhz/12/ant_3.bin #CC4 +antC20=./usecase/mu0_10mhz/12/ant_4.bin #CC5 +antC21=./usecase/mu0_10mhz/12/ant_5.bin #CC5 +antC22=./usecase/mu0_10mhz/12/ant_6.bin #CC5 +antC23=./usecase/mu0_10mhz/12/ant_7.bin #CC5 +antC24=./usecase/mu0_10mhz/12/ant_8.bin #CC6 +antC25=./usecase/mu0_10mhz/12/ant_9.bin #CC6 +antC26=./usecase/mu0_10mhz/12/ant_10.bin #CC6 +antC27=./usecase/mu0_10mhz/12/ant_11.bin #CC6 +antC28=./usecase/mu0_10mhz/12/ant_12.bin #CC7 +antC29=./usecase/mu0_10mhz/12/ant_13.bin #CC7 +antC30=./usecase/mu0_10mhz/12/ant_14.bin #CC7 +antC31=./usecase/mu0_10mhz/12/ant_15.bin #CC7 +antC32=./usecase/mu0_10mhz/12/ant_0.bin #CC8 +antC33=./usecase/mu0_10mhz/12/ant_1.bin #CC8 +antC34=./usecase/mu0_10mhz/12/ant_2.bin #CC8 +antC35=./usecase/mu0_10mhz/12/ant_3.bin #CC8 +antC36=./usecase/mu0_10mhz/12/ant_4.bin #CC9 +antC37=./usecase/mu0_10mhz/12/ant_5.bin #CC9 +antC38=./usecase/mu0_10mhz/12/ant_6.bin #CC9 +antC39=./usecase/mu0_10mhz/12/ant_7.bin #CC9 +antC40=./usecase/mu0_10mhz/12/ant_8.bin #CC10 +antC41=./usecase/mu0_10mhz/12/ant_9.bin #CC10 +antC42=./usecase/mu0_10mhz/12/ant_10.bin #CC10 +antC43=./usecase/mu0_10mhz/12/ant_11.bin #CC10 +antC44=./usecase/mu0_10mhz/12/ant_12.bin #CC11 +antC45=./usecase/mu0_10mhz/12/ant_13.bin #CC11 +antC46=./usecase/mu0_10mhz/12/ant_14.bin #CC11 +antC47=./usecase/mu0_10mhz/12/ant_15.bin #CC11 + +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=189 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_ru.dat b/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_ru.dat new file mode 100644 index 0000000..84cff6f --- /dev/null +++ b/fhi_lib/app/usecase/mu0_10mhz/12/config_file_o_ru.dat @@ -0,0 +1,190 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=12 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +#llsCUMac=3c:fd:fe:a8:e0:70 #lls-CU PF for tcpdump +ruMac=00:11:22:33:44:55 #RU VF for RU app + +numSlots=40 #number of slots per IQ files + +antC0=./usecase/mu0_10mhz/12/ant_0.bin #CC0 +antC1=./usecase/mu0_10mhz/12/ant_1.bin #CC0 +antC2=./usecase/mu0_10mhz/12/ant_2.bin #CC0 +antC3=./usecase/mu0_10mhz/12/ant_3.bin #CC0 +antC4=./usecase/mu0_10mhz/12/ant_4.bin #CC1 +antC5=./usecase/mu0_10mhz/12/ant_5.bin #CC1 +antC6=./usecase/mu0_10mhz/12/ant_6.bin #CC1 +antC7=./usecase/mu0_10mhz/12/ant_7.bin #CC1 +antC8=./usecase/mu0_10mhz/12/ant_8.bin #CC2 +antC9=./usecase/mu0_10mhz/12/ant_9.bin #CC2 +antC10=./usecase/mu0_10mhz/12/ant_10.bin #CC2 +antC11=./usecase/mu0_10mhz/12/ant_11.bin #CC2 +antC12=./usecase/mu0_10mhz/12/ant_12.bin #CC3 +antC13=./usecase/mu0_10mhz/12/ant_13.bin #CC3 +antC14=./usecase/mu0_10mhz/12/ant_14.bin #CC3 +antC15=./usecase/mu0_10mhz/12/ant_15.bin #CC3 +antC16=./usecase/mu0_10mhz/12/ant_0.bin #CC4 +antC17=./usecase/mu0_10mhz/12/ant_1.bin #CC4 +antC18=./usecase/mu0_10mhz/12/ant_2.bin #CC4 +antC19=./usecase/mu0_10mhz/12/ant_3.bin #CC4 +antC20=./usecase/mu0_10mhz/12/ant_4.bin #CC5 +antC21=./usecase/mu0_10mhz/12/ant_5.bin #CC5 +antC22=./usecase/mu0_10mhz/12/ant_6.bin #CC5 +antC23=./usecase/mu0_10mhz/12/ant_7.bin #CC5 +antC24=./usecase/mu0_10mhz/12/ant_8.bin #CC6 +antC25=./usecase/mu0_10mhz/12/ant_9.bin #CC6 +antC26=./usecase/mu0_10mhz/12/ant_10.bin #CC6 +antC27=./usecase/mu0_10mhz/12/ant_11.bin #CC6 +antC28=./usecase/mu0_10mhz/12/ant_12.bin #CC7 +antC29=./usecase/mu0_10mhz/12/ant_13.bin #CC7 +antC30=./usecase/mu0_10mhz/12/ant_14.bin #CC7 +antC31=./usecase/mu0_10mhz/12/ant_15.bin #CC7 +antC32=./usecase/mu0_10mhz/12/ant_0.bin #CC8 +antC33=./usecase/mu0_10mhz/12/ant_1.bin #CC8 +antC34=./usecase/mu0_10mhz/12/ant_2.bin #CC8 +antC35=./usecase/mu0_10mhz/12/ant_3.bin #CC8 +antC36=./usecase/mu0_10mhz/12/ant_4.bin #CC9 +antC37=./usecase/mu0_10mhz/12/ant_5.bin #CC9 +antC38=./usecase/mu0_10mhz/12/ant_6.bin #CC9 +antC39=./usecase/mu0_10mhz/12/ant_7.bin #CC9 +antC40=./usecase/mu0_10mhz/12/ant_8.bin #CC10 +antC41=./usecase/mu0_10mhz/12/ant_9.bin #CC10 +antC42=./usecase/mu0_10mhz/12/ant_10.bin #CC10 +antC43=./usecase/mu0_10mhz/12/ant_11.bin #CC10 +antC44=./usecase/mu0_10mhz/12/ant_12.bin #CC11 +antC45=./usecase/mu0_10mhz/12/ant_13.bin #CC11 +antC46=./usecase/mu0_10mhz/12/ant_14.bin #CC11 +antC47=./usecase/mu0_10mhz/12/ant_15.bin #CC11 + +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=189 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +antPrachC0=./usecase/mu0_10mhz/12/ant_0.bin +antPrachC1=./usecase/mu0_10mhz/12/ant_1.bin +antPrachC2=./usecase/mu0_10mhz/12/ant_2.bin +antPrachC3=./usecase/mu0_10mhz/12/ant_3.bin +antPrachC4=./usecase/mu0_10mhz/12/ant_4.bin +antPrachC5=./usecase/mu0_10mhz/12/ant_5.bin +antPrachC6=./usecase/mu0_10mhz/12/ant_6.bin +antPrachC7=./usecase/mu0_10mhz/12/ant_7.bin +antPrachC8=./usecase/mu0_10mhz/12/ant_8.bin +antPrachC9=./usecase/mu0_10mhz/12/ant_9.bin +antPrachC10=./usecase/mu0_10mhz/12/ant_10.bin +antPrachC11=./usecase/mu0_10mhz/12/ant_11.bin +antPrachC12=./usecase/mu0_10mhz/12/ant_12.bin +antPrachC13=./usecase/mu0_10mhz/12/ant_13.bin +antPrachC14=./usecase/mu0_10mhz/12/ant_14.bin +antPrachC15=./usecase/mu0_10mhz/12/ant_15.bin +antPrachC16=./usecase/mu0_10mhz/12/ant_0.bin +antPrachC17=./usecase/mu0_10mhz/12/ant_1.bin +antPrachC18=./usecase/mu0_10mhz/12/ant_2.bin +antPrachC19=./usecase/mu0_10mhz/12/ant_3.bin +antPrachC20=./usecase/mu0_10mhz/12/ant_4.bin +antPrachC21=./usecase/mu0_10mhz/12/ant_5.bin +antPrachC22=./usecase/mu0_10mhz/12/ant_6.bin +antPrachC23=./usecase/mu0_10mhz/12/ant_7.bin +antPrachC24=./usecase/mu0_10mhz/12/ant_8.bin +antPrachC25=./usecase/mu0_10mhz/12/ant_9.bin +antPrachC26=./usecase/mu0_10mhz/12/ant_10.bin +antPrachC27=./usecase/mu0_10mhz/12/ant_11.bin +antPrachC28=./usecase/mu0_10mhz/12/ant_12.bin +antPrachC29=./usecase/mu0_10mhz/12/ant_13.bin +antPrachC30=./usecase/mu0_10mhz/12/ant_14.bin +antPrachC31=./usecase/mu0_10mhz/12/ant_15.bin +antPrachC32=./usecase/mu0_10mhz/12/ant_0.bin +antPrachC33=./usecase/mu0_10mhz/12/ant_1.bin +antPrachC34=./usecase/mu0_10mhz/12/ant_2.bin +antPrachC35=./usecase/mu0_10mhz/12/ant_3.bin +antPrachC36=./usecase/mu0_10mhz/12/ant_4.bin +antPrachC37=./usecase/mu0_10mhz/12/ant_5.bin +antPrachC38=./usecase/mu0_10mhz/12/ant_6.bin +antPrachC39=./usecase/mu0_10mhz/12/ant_7.bin +antPrachC40=./usecase/mu0_10mhz/12/ant_8.bin +antPrachC41=./usecase/mu0_10mhz/12/ant_9.bin +antPrachC42=./usecase/mu0_10mhz/12/ant_10.bin +antPrachC43=./usecase/mu0_10mhz/12/ant_11.bin +antPrachC44=./usecase/mu0_10mhz/12/ant_12.bin +antPrachC45=./usecase/mu0_10mhz/12/ant_13.bin +antPrachC46=./usecase/mu0_10mhz/12/ant_14.bin +antPrachC47=./usecase/mu0_10mhz/12/ant_15.bin + + + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=0 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_10mhz/config_file_o_du.dat b/fhi_lib/app/usecase/mu0_10mhz/config_file_o_du.dat new file mode 100644 index 0000000..7355ab1 --- /dev/null +++ b/fhi_lib/app/usecase/mu0_10mhz/config_file_o_du.dat @@ -0,0 +1,139 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app + +numSlots=40 #number of slots per IQ files + +antC0=./usecase/mu0_10mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_10mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_10mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_10mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_10mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_10mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_10mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_10mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_10mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_10mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_10mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_10mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_10mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_10mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_10mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_10mhz/ant_15.bin #CC3 +antC16=./usecase/mu0_10mhz/ant_0.bin #CC4 +antC17=./usecase/mu0_10mhz/ant_1.bin #CC4 +antC18=./usecase/mu0_10mhz/ant_2.bin #CC4 +antC19=./usecase/mu0_10mhz/ant_3.bin #CC4 +antC20=./usecase/mu0_10mhz/ant_4.bin #CC5 +antC21=./usecase/mu0_10mhz/ant_5.bin #CC5 +antC22=./usecase/mu0_10mhz/ant_6.bin #CC5 +antC23=./usecase/mu0_10mhz/ant_7.bin #CC5 +antC24=./usecase/mu0_10mhz/ant_8.bin #CC6 +antC25=./usecase/mu0_10mhz/ant_9.bin #CC6 +antC26=./usecase/mu0_10mhz/ant_10.bin #CC6 +antC27=./usecase/mu0_10mhz/ant_11.bin #CC6 +antC28=./usecase/mu0_10mhz/ant_12.bin #CC7 +antC29=./usecase/mu0_10mhz/ant_13.bin #CC7 +antC30=./usecase/mu0_10mhz/ant_14.bin #CC7 +antC31=./usecase/mu0_10mhz/ant_15.bin #CC7 +antC32=./usecase/mu0_10mhz/ant_0.bin #CC8 +antC33=./usecase/mu0_10mhz/ant_1.bin #CC8 +antC34=./usecase/mu0_10mhz/ant_2.bin #CC8 +antC35=./usecase/mu0_10mhz/ant_3.bin #CC8 +antC36=./usecase/mu0_10mhz/ant_4.bin #CC9 +antC37=./usecase/mu0_10mhz/ant_5.bin #CC9 +antC38=./usecase/mu0_10mhz/ant_6.bin #CC9 +antC39=./usecase/mu0_10mhz/ant_7.bin #CC9 +antC40=./usecase/mu0_10mhz/ant_8.bin #CC10 +antC41=./usecase/mu0_10mhz/ant_9.bin #CC10 +antC42=./usecase/mu0_10mhz/ant_10.bin #CC10 +antC43=./usecase/mu0_10mhz/ant_11.bin #CC10 +antC44=./usecase/mu0_10mhz/ant_12.bin #CC11 +antC45=./usecase/mu0_10mhz/ant_13.bin #CC11 +antC46=./usecase/mu0_10mhz/ant_14.bin #CC11 +antC47=./usecase/mu0_10mhz/ant_15.bin #CC11 + +rachEanble=1 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=189 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary +bbdevMode=-1 #bbdev mode, -1 = not use bbdev, 0: use software mode, 1: use hardware mode + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_10mhz/config_file_o_ru.dat b/fhi_lib/app/usecase/mu0_10mhz/config_file_o_ru.dat new file mode 100644 index 0000000..d0d8cce --- /dev/null +++ b/fhi_lib/app/usecase/mu0_10mhz/config_file_o_ru.dat @@ -0,0 +1,145 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=10 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app + +numSlots=40 #number of slots per IQ files + +antC0=./usecase/mu0_10mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_10mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_10mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_10mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_10mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_10mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_10mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_10mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_10mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_10mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_10mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_10mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_10mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_10mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_10mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_10mhz/ant_15.bin #CC3 +antC16=./usecase/mu0_10mhz/ant_0.bin #CC4 +antC17=./usecase/mu0_10mhz/ant_1.bin #CC4 +antC18=./usecase/mu0_10mhz/ant_2.bin #CC4 +antC19=./usecase/mu0_10mhz/ant_3.bin #CC4 +antC20=./usecase/mu0_10mhz/ant_4.bin #CC5 +antC21=./usecase/mu0_10mhz/ant_5.bin #CC5 +antC22=./usecase/mu0_10mhz/ant_6.bin #CC5 +antC23=./usecase/mu0_10mhz/ant_7.bin #CC5 +antC24=./usecase/mu0_10mhz/ant_8.bin #CC6 +antC25=./usecase/mu0_10mhz/ant_9.bin #CC6 +antC26=./usecase/mu0_10mhz/ant_10.bin #CC6 +antC27=./usecase/mu0_10mhz/ant_11.bin #CC6 +antC28=./usecase/mu0_10mhz/ant_12.bin #CC7 +antC29=./usecase/mu0_10mhz/ant_13.bin #CC7 +antC30=./usecase/mu0_10mhz/ant_14.bin #CC7 +antC31=./usecase/mu0_10mhz/ant_15.bin #CC7 +antC32=./usecase/mu0_10mhz/ant_0.bin #CC8 +antC33=./usecase/mu0_10mhz/ant_1.bin #CC8 +antC34=./usecase/mu0_10mhz/ant_2.bin #CC8 +antC35=./usecase/mu0_10mhz/ant_3.bin #CC8 +antC36=./usecase/mu0_10mhz/ant_4.bin #CC9 +antC37=./usecase/mu0_10mhz/ant_5.bin #CC9 +antC38=./usecase/mu0_10mhz/ant_6.bin #CC9 +antC39=./usecase/mu0_10mhz/ant_7.bin #CC9 +antC40=./usecase/mu0_10mhz/ant_8.bin #CC10 +antC41=./usecase/mu0_10mhz/ant_9.bin #CC10 +antC42=./usecase/mu0_10mhz/ant_10.bin #CC10 +antC43=./usecase/mu0_10mhz/ant_11.bin #CC10 +antC44=./usecase/mu0_10mhz/ant_12.bin #CC11 +antC45=./usecase/mu0_10mhz/ant_13.bin #CC11 +antC46=./usecase/mu0_10mhz/ant_14.bin #CC11 +antC47=./usecase/mu0_10mhz/ant_15.bin #CC11 + +rachEanble=1 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=189 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +antPrachC0=./usecase/mu0_10mhz/ant_0.bin +antPrachC1=./usecase/mu0_10mhz/ant_1.bin +antPrachC2=./usecase/mu0_10mhz/ant_2.bin +antPrachC3=./usecase/mu0_10mhz/ant_3.bin + + + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_20mhz/config_file_o_du.dat b/fhi_lib/app/usecase/mu0_20mhz/config_file_o_du.dat new file mode 100644 index 0000000..6913532 --- /dev/null +++ b/fhi_lib/app/usecase/mu0_20mhz/config_file_o_du.dat @@ -0,0 +1,105 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=20 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=20 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=2048 +nULFftSize=2048 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app +antC0=./usecase/mu0_20mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_20mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_20mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_20mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_20mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_20mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_20mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_20mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_20mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_20mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_20mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_20mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_20mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_20mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_20mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_20mhz/ant_15.bin #CC3 + +## RACH TODO: update for PRACH +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +#rachOffset=43 # RB offset for prach detection (see RIU spec) +#rachCfgIdx=14 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_20mhz/config_file_o_ru.dat b/fhi_lib/app/usecase/mu0_20mhz/config_file_o_ru.dat new file mode 100644 index 0000000..e6befa1 --- /dev/null +++ b/fhi_lib/app/usecase/mu0_20mhz/config_file_o_ru.dat @@ -0,0 +1,111 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=20 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=20 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=2048 +nULFftSize=2048 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +#llsCUMac=3c:fd:fe:a8:e0:70 +ruMac=00:11:22:33:44:55 #RU VF for RU app +antC0=./usecase/mu0_20mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_20mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_20mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_20mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_20mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_20mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_20mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_20mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_20mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_20mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_20mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_20mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_20mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_20mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_20mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_20mhz/ant_15.bin #CC3 + +## RACH TODO: update for PRACH +rachEanble=1 # Enable (1)| disable (0) PRACH configuration +#rachOffset=43 # RB offset for prach detection (see RIU spec) +prachConfigIndex=189 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +antPrachC0=./usecase/mu0_20mhz/ant_0.bin +antPrachC1=./usecase/mu0_20mhz/ant_1.bin +antPrachC3=./usecase/mu0_20mhz/ant_3.bin +antPrachC4=./usecase/mu0_20mhz/ant_4.bin + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=0 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_5mhz/config_file_o_du.dat b/fhi_lib/app/usecase/mu0_5mhz/config_file_o_du.dat new file mode 100644 index 0000000..5aaf308 --- /dev/null +++ b/fhi_lib/app/usecase/mu0_5mhz/config_file_o_du.dat @@ -0,0 +1,106 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=5 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=5 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=512 +nULFftSize=512 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app +numSlots=40 #number of slots per IQ files +antC0=./usecase/mu0_5mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_5mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_5mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_5mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_5mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_5mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_5mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_5mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_5mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_5mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_5mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_5mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_5mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_5mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_5mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_5mhz/ant_15.bin #CC3 + +## RACH TODO: update for PRACH +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +#rachOffset=43 # RB offset for prach detection (see RIU spec) +#prachConfigIndex=1 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu0_5mhz/config_file_o_ru.dat b/fhi_lib/app/usecase/mu0_5mhz/config_file_o_ru.dat new file mode 100644 index 0000000..1cdde48 --- /dev/null +++ b/fhi_lib/app/usecase/mu0_5mhz/config_file_o_ru.dat @@ -0,0 +1,105 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=0 #15Khz Sub Carrier Spacing +ttiPeriod=1000 # in us TTI period (15Khz default 1000us) +nDLAbsFrePointA=2645460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=2525460 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=5 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=5 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=512 +nULFftSize=512 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=0 #TDD priod e.g. DDDS 4 + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app +antC0=./usecase/mu0_5mhz/ant_0.bin #CC0 +antC1=./usecase/mu0_5mhz/ant_1.bin #CC0 +antC2=./usecase/mu0_5mhz/ant_2.bin #CC0 +antC3=./usecase/mu0_5mhz/ant_3.bin #CC0 +antC4=./usecase/mu0_5mhz/ant_4.bin #CC1 +antC5=./usecase/mu0_5mhz/ant_5.bin #CC1 +antC6=./usecase/mu0_5mhz/ant_6.bin #CC1 +antC7=./usecase/mu0_5mhz/ant_7.bin #CC1 +antC8=./usecase/mu0_5mhz/ant_8.bin #CC2 +antC9=./usecase/mu0_5mhz/ant_9.bin #CC2 +antC10=./usecase/mu0_5mhz/ant_10.bin #CC2 +antC11=./usecase/mu0_5mhz/ant_11.bin #CC2 +antC12=./usecase/mu0_5mhz/ant_12.bin #CC3 +antC13=./usecase/mu0_5mhz/ant_13.bin #CC3 +antC14=./usecase/mu0_5mhz/ant_14.bin #CC3 +antC15=./usecase/mu0_5mhz/ant_15.bin #CC3 + +## RACH TODO: update for PRACH +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +#rachOffset=43 # RB offset for prach detection (see RIU spec) +#prachConfigIndex=1 # PRACH config index as per TS36.211 - Table 5.7.1-2 : PRACH Configuration Index + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=400 #in us +T2a_max_cp_dl=1120 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=400 #in us +T2a_max_cp_ul=1120 #in us + +#Reception Window U-plane +T2a_min_up=200 # in us +T2a_max_up=1120 # in us + +#Transmission Window +Ta3_min=160 #in us +Ta3_max=256 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=560 +T1a_max_cp_dl=800 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=480 +T1a_max_cp_ul=560 + +#U-plane +##Transmission Window +T1a_min_up=280 +T1a_max_up=400 + +#Reception Window +Ta4_min=0 +Ta4_max=360 +########################################################### + diff --git a/fhi_lib/app/usecase/mu1_100mhz/config_file_o_du.dat b/fhi_lib/app/usecase/mu1_100mhz/config_file_o_du.dat new file mode 100644 index 0000000..c343ccf --- /dev/null +++ b/fhi_lib/app/usecase/mu1_100mhz/config_file_o_du.dat @@ -0,0 +1,116 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=1 #30Khz Sub Carrier Spacing + +ttiPeriod=500 # in us TTI period (30Khz default 500us) + +nDLAbsFrePointA=3568160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=3568160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=4096 +nULFftSize=4096 + +nFrameDuplexType=1 # 0 - FDD 1 - TDD +nTddPeriod=10 #[0-9] DDDSUUDDDD, for S itÂ’s 6:4:4 +sSlotConfig0=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig1=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig2=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig3=0,0,0,0,0,0,2,2,2,2,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig4=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig5=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig6=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig7=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig8=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig9=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 # RU VF for RU app +#ruMac=3c:fd:fe:9e:93:68 #RU PF for tcpdump + +numSlots=40 #number of slots per IQ files +antC0=./usecase/mu1_100mhz/ant_0.bin #CC0 +antC1=./usecase/mu1_100mhz/ant_1.bin #CC0 +antC2=./usecase/mu1_100mhz/ant_2.bin #CC0 +antC3=./usecase/mu1_100mhz/ant_3.bin #CC0 +antC4=./usecase/mu1_100mhz/ant_4.bin #CC1 +antC5=./usecase/mu1_100mhz/ant_5.bin #CC1 +antC6=./usecase/mu1_100mhz/ant_6.bin #CC1 +antC7=./usecase/mu1_100mhz/ant_7.bin #CC1 +antC8=./usecase/mu1_100mhz/ant_8.bin #CC2 +antC9=./usecase/mu1_100mhz/ant_9.bin #CC2 +antC10=./usecase/mu1_100mhz/ant_10.bin #CC2 +antC11=./usecase/mu1_100mhz/ant_11.bin #CC2 +antC12=./usecase/mu1_100mhz/ant_12.bin #CC3 +antC13=./usecase/mu1_100mhz/ant_13.bin #CC3 +antC14=./usecase/mu1_100mhz/ant_14.bin #CC3 +antC15=./usecase/mu1_100mhz/ant_15.bin #CC3 + +rachEanble=1 # Enable (1)| disable (0) PRACH configuration + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 # in us + # C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages +#Reception Window C-plane DL +T2a_min_cp_dl=285 # 285.42us +T2a_max_cp_dl=429 # 428.12us + +#Reception Window C-plane UL +T2a_min_cp_ul=285 # 285.42us +T2a_max_cp_ul=429 # 428.12us + +#Reception Window U-plane +T2a_min_up=71 # 71.35in us +T2a_max_up=428 # 428.12us + +#Transmission Window +Ta3_min=20 # in us +Ta3_max=32 # in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=285 +T1a_max_cp_dl=429 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=285 +T1a_max_cp_ul=300 + +#U-plane +##Transmission Window +T1a_min_up=96 #71 + 25 us +T1a_max_up=196 #71 + 25 us + +#Reception Window +Ta4_min=0 # in us +Ta4_max=75 # in us +########################################################### + diff --git a/fhi_lib/app/usecase/mu1_100mhz/config_file_o_ru.dat b/fhi_lib/app/usecase/mu1_100mhz/config_file_o_ru.dat new file mode 100644 index 0000000..bed1922 --- /dev/null +++ b/fhi_lib/app/usecase/mu1_100mhz/config_file_o_ru.dat @@ -0,0 +1,134 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 4) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=1 #30Khz Sub Carrier Spacing + +ttiPeriod=500 # in us TTI period (30Khz default 500us) + +nDLAbsFrePointA=3568160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=3568160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=4096 +nULFftSize=4096 + +nFrameDuplexType=0 # 0 - FDD 1 - TDD +nTddPeriod=10 #[0-9] DDDSUUDDDD, for S itÂ’s 6:4:4 +sSlotConfig0=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig1=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig2=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig3=0,0,0,0,0,0,2,2,2,2,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig4=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig5=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig6=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig7=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig8=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig9=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +#llsCUMac=3c:fd:fe:a8:e0:70 +ruMac=00:11:22:33:44:55 # RU VF for RU app + +numSlots=40 #number of slots per IQ files +antC0=./usecase/mu1_100mhz/ant_0.bin #CC0 +antC1=./usecase/mu1_100mhz/ant_1.bin #CC0 +antC2=./usecase/mu1_100mhz/ant_2.bin #CC0 +antC3=./usecase/mu1_100mhz/ant_3.bin #CC0 +antC4=./usecase/mu1_100mhz/ant_4.bin #CC1 +antC5=./usecase/mu1_100mhz/ant_5.bin #CC1 +antC6=./usecase/mu1_100mhz/ant_6.bin #CC1 +antC7=./usecase/mu1_100mhz/ant_7.bin #CC1 +antC8=./usecase/mu1_100mhz/ant_8.bin #CC2 +antC9=./usecase/mu1_100mhz/ant_9.bin #CC2 +antC10=./usecase/mu1_100mhz/ant_10.bin #CC2 +antC11=./usecase/mu1_100mhz/ant_11.bin #CC2 +antC12=./usecase/mu1_100mhz/ant_12.bin #CC3 +antC13=./usecase/mu1_100mhz/ant_13.bin #CC3 +antC14=./usecase/mu1_100mhz/ant_14.bin #CC3 +antC15=./usecase/mu1_100mhz/ant_15.bin #CC3 + +antPrachC0=./usecase/mu1_100mhz/ant_0.bin #CC0 +antPrachC1=./usecase/mu1_100mhz/ant_1.bin #CC0 +antPrachC2=./usecase/mu1_100mhz/ant_2.bin #CC0 +antPrachC3=./usecase/mu1_100mhz/ant_3.bin #CC0 +antPrachC4=./usecase/mu1_100mhz/ant_4.bin #CC1 +antPrachC5=./usecase/mu1_100mhz/ant_5.bin #CC1 +antPrachC6=./usecase/mu1_100mhz/ant_6.bin #CC1 +antPrachC7=./usecase/mu1_100mhz/ant_7.bin #CC1 +antPrachC8=./usecase/mu1_100mhz/ant_8.bin #CC2 +antPrachC9=./usecase/mu1_100mhz/ant_9.bin #CC2 +antPrachC10=./usecase/mu1_100mhz/ant_10.bin #CC2 +antPrachC11=./usecase/mu1_100mhz/ant_11.bin #CC2 +antPrachC12=./usecase/mu1_100mhz/ant_12.bin #CC3 +antPrachC13=./usecase/mu1_100mhz/ant_13.bin #CC3 +antPrachC14=./usecase/mu1_100mhz/ant_14.bin #CC3 +antPrachC15=./usecase/mu1_100mhz/ant_15.bin #CC3 + +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=1 + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=0 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 # in us + # C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages +#Reception Window C-plane DL +T2a_min_cp_dl=285 # 285.42us +T2a_max_cp_dl=429 # 428.12us + +#Reception Window C-plane UL +T2a_min_cp_ul=285 # 285.42us +T2a_max_cp_ul=429 # 428.12us + +#Reception Window U-plane +T2a_min_up=71 # 71.35in us +T2a_max_up=428 # 428.12us + +#Transmission Window +Ta3_min=20 # in us TODO: update +Ta3_max=32 # in us TODO: update + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=285 +T1a_max_cp_dl=429 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=285 +T1a_max_cp_ul=300 + +#U-plane +##Transmission Window +T1a_min_up=96 #71 + 25 us +T1a_max_up=196 #71 + 25 us + +#Reception Window +Ta4_min=0 # in us TODO: update +Ta4_max=75 # in us TODO: update +########################################################### + diff --git a/fhi_lib/app/usecase/mu3_100mhz/config_file_o_du.dat b/fhi_lib/app/usecase/mu3_100mhz/config_file_o_du.dat new file mode 100644 index 0000000..049aba4 --- /dev/null +++ b/fhi_lib/app/usecase/mu3_100mhz/config_file_o_du.dat @@ -0,0 +1,116 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=0 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=3 #mmWave 120Khz Sub Carrier Spacing +ttiPeriod=125 # in us TTI period (mmWave default 125us) +nDLAbsFrePointA=27968160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=27968160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=1 # 0 - FDD 1 - TDD +nTddPeriod=4 #[0-5] TDD priod e.g. DDDS 4 +sSlotConfig0=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig1=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig2=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig3=0,2,2,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +# not used +#sSlotConfig4=0,2,2,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig5=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig6=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig7=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig8=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig9=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +ruMac=00:11:22:33:44:55 #RU VF for RU app +#ruMac=3c:fd:fe:9e:93:68 #RU PF for tcpdump + +numSlots=40 #number of slots per IQ files +antC0=./usecase/mu3_100mhz/ant_0.bin #CC0 +antC1=./usecase/mu3_100mhz/ant_1.bin #CC0 +antC2=./usecase/mu3_100mhz/ant_2.bin #CC0 +antC3=./usecase/mu3_100mhz/ant_3.bin #CC0 +antC4=./usecase/mu3_100mhz/ant_4.bin #CC1 +antC5=./usecase/mu3_100mhz/ant_5.bin #CC1 +antC6=./usecase/mu3_100mhz/ant_6.bin #CC1 +antC7=./usecase/mu3_100mhz/ant_7.bin #CC1 +antC8=./usecase/mu3_100mhz/ant_8.bin #CC2 +antC9=./usecase/mu3_100mhz/ant_9.bin #CC2 +antC10=./usecase/mu3_100mhz/ant_10.bin #CC2 +antC11=./usecase/mu3_100mhz/ant_11.bin #CC2 +antC12=./usecase/mu3_100mhz/ant_12.bin #CC3 +antC13=./usecase/mu3_100mhz/ant_13.bin #CC3 +antC14=./usecase/mu3_100mhz/ant_14.bin #CC3 +antC15=./usecase/mu3_100mhz/ant_15.bin #CC3 + +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=81 + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=1 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=50 #in us +T2a_max_cp_dl=140 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=50 #in us +T2a_max_cp_ul=140 #in us + +#Reception Window U-plane +T2a_min_up=25 #in us +T2a_max_up=140 #in us + +#Transmission Window +Ta3_min=20 #in us +Ta3_max=32 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=70 +T1a_max_cp_dl=100 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=60 +T1a_max_cp_ul=70 + +#U-plane +##Transmission Window +T1a_min_up=35 +T1a_max_up=50 + +#Reception Window +Ta4_min=0 +Ta4_max=45 +########################################################### + diff --git a/fhi_lib/app/usecase/mu3_100mhz/config_file_o_ru.dat b/fhi_lib/app/usecase/mu3_100mhz/config_file_o_ru.dat new file mode 100644 index 0000000..1959bcc --- /dev/null +++ b/fhi_lib/app/usecase/mu3_100mhz/config_file_o_ru.dat @@ -0,0 +1,122 @@ +####################################################################### +# +# +# +####################################################################### + +# This is simple configuration file. Use '#' sign for comments +appMode=1 # lls-CU(0) | RU(1) +xranMode=0 # Category A (0) (precoder in lls-CU) | Category B (1) (precoder in RU) +ccNum=1 # Number of Componnent Carriers (CC) per ETH port with XRAN protocol (default:1 max: 12) +antNum=4 # Number of Antennas per CC (default: 4) + +##Numerology +mu=3 #mmWave 120Khz Sub Carrier Spacing +ttiPeriod=125 # in us TTI period (mmWave default 125us) +nDLAbsFrePointA=27968160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nULAbsFrePointA=27968160 #nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 +nDLBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nULBandwidth=100 #Carrier bandwidth for in MHz. Value: 5->400 +nDLFftSize=1024 +nULFftSize=1024 + +nFrameDuplexType=1 # 0 - FDD 1 - TDD +nTddPeriod=4 #[0-5] TDD priod e.g. DDDS 4 +sSlotConfig0=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig1=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig2=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +sSlotConfig3=0,2,2,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD + #not used +#sSlotConfig4=0,2,2,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig5=1,1,1,1,1,1,1,1,1,1,1,1,1,1 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig6=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig7=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig8=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD +#sSlotConfig9=0,0,0,0,0,0,0,0,0,0,0,0,0,0 # (0) - DL (1) - UL (2) - GUARD + +MTUSize=9600 #maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + #xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) + +llsCUMac=00:11:22:33:44:66 # asigned MAC of lls-CU VF +#llsCUMac=3c:fd:fe:a8:e0:70 #lls-CU PF for tcpdump +ruMac=00:11:22:33:44:55 #RU VF for RU app + +numSlots=40 #number of slots per IQ files +antC0=./usecase/mu3_100mhz/ant_0.bin #CC0 +antC1=./usecase/mu3_100mhz/ant_1.bin #CC0 +antC2=./usecase/mu3_100mhz/ant_2.bin #CC0 +antC3=./usecase/mu3_100mhz/ant_3.bin #CC0 +antC4=./usecase/mu3_100mhz/ant_4.bin #CC1 +antC5=./usecase/mu3_100mhz/ant_5.bin #CC1 +antC6=./usecase/mu3_100mhz/ant_6.bin #CC1 +antC7=./usecase/mu3_100mhz/ant_7.bin #CC1 +antC8=./usecase/mu3_100mhz/ant_8.bin #CC2 +antC9=./usecase/mu3_100mhz/ant_9.bin #CC2 +antC10=./usecase/mu3_100mhz/ant_10.bin #CC2 +antC11=./usecase/mu3_100mhz/ant_11.bin #CC2 +antC12=./usecase/mu3_100mhz/ant_12.bin #CC3 +antC13=./usecase/mu3_100mhz/ant_13.bin #CC3 +antC14=./usecase/mu3_100mhz/ant_14.bin #CC3 +antC15=./usecase/mu3_100mhz/ant_15.bin #CC3 + +antPrachC0=./usecase/mu1_100mhz/ant_0.bin #CC0 +antPrachC1=./usecase/mu1_100mhz/ant_1.bin #CC0 +antPrachC2=./usecase/mu1_100mhz/ant_2.bin #CC0 +antPrachC3=./usecase/mu1_100mhz/ant_3.bin #CC0 + +rachEanble=0 # Enable (1)| disable (0) PRACH configuration +prachConfigIndex=81 + +## control of IQ byte order +iqswap=0 #do swap of IQ before send buffer to eth +nebyteorderswap=1 #do swap of byte order for each I and Q from CPU byte order to network byte order + +##Debug +debugStop=1 #stop app on 1pps boundary (gps_second % 30) +debugStopCount=0 #if this value is >0 then stop app after x transmission packets, otherwise app will stop at 1pps boundary + +CPenable=0 #(1) C-Plane is enabled| (0) C-Plane is disabled +c_plane_vlan_tag=1 #VLAN Tag used for C-Plane +u_plane_vlan_tag=2 #VLAN Tag used for U-Plane + +##RU Settings +Tadv_cp_dl=25 #in us TODO: update per RU implementation + #C-Plane messages must arrive at the RU some amount of time in advance (Tcp_adv_dl) of the corresponding U-Plane messages + +#Reception Window C-plane DL +T2a_min_cp_dl=50 #in us +T2a_max_cp_dl=140 #in us + +#Reception Window C-plane UL +T2a_min_cp_ul=50 #in us +T2a_max_cp_ul=140 #in us + +#Reception Window U-plane +T2a_min_up=25 #in us +T2a_max_up=140 #in us + +#Transmission Window +Ta3_min=20 #in us +Ta3_max=32 #in us + +########################################################### +##lls-CU Settings +#C-plane +#Transmission Window Fast C-plane DL +T1a_min_cp_dl=70 +T1a_max_cp_dl=100 + +##Transmission Window Fast C-plane UL +T1a_min_cp_ul=60 +T1a_max_cp_ul=70 + +#U-plane +##Transmission Window +T1a_min_up=35 +T1a_max_up=50 + +#Reception Window +Ta4_min=0 +Ta4_max=45 +########################################################### + diff --git a/fhi_lib/build.sh b/fhi_lib/build.sh index 149ad8a..32d8af0 100644 --- a/fhi_lib/build.sh +++ b/fhi_lib/build.sh @@ -19,12 +19,62 @@ XRAN_FH_LIB_DIR=$XRAN_DIR/lib XRAN_FH_APP_DIR=$XRAN_DIR/app +XRAN_FH_TEST_DIR=$XRAN_DIR/test/test_xran +LIBXRANSO=0 +MLOG=0 +COMMAND_LINE= -echo 'Building XRAN Library' -cd $XRAN_FH_LIB_DIR -make clean; make #DEBUG=1 VERBOSE=1 +echo Number of commandline arguments: $# +while [[ $# -ne 0 ]] +do +key="$1" -echo 'Building XRAN Test Application' -cd $XRAN_FH_APP_DIR -make clean; make #DEBUG=1 VERBOSE=1 +#echo Parsing: $key +case $key in + LIBXRANSO) + LIBXRANSO=1 + ;; + MLOG) + MLOG=1 + ;; + xclean) + COMMAND_LINE+=$key + COMMAND_LINE+=" " + ;; + *) + echo $key is unknown command # unknown option + ;; +esac +shift # past argument or value +done + +if [ -z "$MLOG_DIR" ] +then + echo 'MLOG folder is not set. Disable MLOG (MLOG_DIR='$MLOG_DIR')' + MLOG=0 +else + echo 'MLOG folder is set. Enable MLOG (MLOG_DIR='$MLOG_DIR')' + MLOG=1 +fi + +echo 'Building xRAN Library' +echo "LIBXRANSO = ${LIBXRANSO}" +echo "MLOG = ${MLOG}" + +cd $XRAN_FH_LIB_DIR +make $COMMAND_LINE MLOG=${MLOG} LIBXRANSO=${LIBXRANSO} #DEBUG=1 VERBOSE=1 + +echo 'Building xRAN Test Application' +cd $XRAN_FH_APP_DIR +make $COMMAND_LINE MLOG=${MLOG} #DEBUG=1 VERBOSE=1 + +if [ -z ${GTEST_ROOT+x} ]; +then + echo "GTEST_ROOT is not set. Unit tests are not compiled"; +else + echo 'Building xRAN Test Application ('$GTEST_ROOT')' + cd $XRAN_FH_TEST_DIR + make clean; + make +fi diff --git a/fhi_lib/lib/Makefile b/fhi_lib/lib/Makefile index d65eef1..7b33916 100644 --- a/fhi_lib/lib/Makefile +++ b/fhi_lib/lib/Makefile @@ -15,73 +15,191 @@ #* limitations under the License. #* #*******************************************************************************/ -CC := icc -AR := xiar + + +MYCUSTOMTAB=' ' +MYCUSTOMSPACE='============================================================================================' +MYCUSTOMSPACE1='------------------------------------------------------------' + +############################################################## +# Tools configuration +############################################################## +CC := icc +CPP := icpc +AS := as +AR := ar +LD := icc +OBJDUMP := objdump + +ifeq ($(SHELL),cmd.exe) +MD := mkdir.exe -p +CP := cp.exe -f +RM := rm.exe -rf +else +MD := mkdir -p +CP := cp -f +RM := rm -rf +endif + +PROJECT_NAME := libxran +PROJECT_TYPE := lib +PROJECT_DIR := $(XRAN_DIR)/lib +BUILDDIR := ./build +PROJECT_BINARY := $(BUILDDIR)/$(PROJECT_NAME).a ifeq ($(RTE_SDK),) $(error "Please define RTE_SDK environment variable") endif -RTE_TARGET := x86_64-native-linuxapp-icc +RTE_TARGET ?= x86_64-native-linuxapp-gcc RTE_INC := $(RTE_SDK)/$(RTE_TARGET)/include -#include $(RTE_SDK)/mk/rte.vars.mk -API_DIR := $(PWD)/api -SRC_DIR := $(PWD)/src -ETH_DIR := $(PWD)/ethernet +API_DIR := $(PROJECT_DIR)/api +SRC_DIR := $(PROJECT_DIR)/src +ETH_DIR := $(PROJECT_DIR)/ethernet +ifeq ($(MLOG),1) ifeq ($(MLOG_DIR),) MLOG_DIR=$(XRAN_DIR)/../mlog endif - -CFLAGS += -std=gnu11 -Wall -wd9 -Wno-deprecated-declarations -Wextra -Werror -qopt-report-phase:all -qopt-zmm-usage=high \ - -fdata-sections \ - -ffunction-sections \ - -restrict \ - -g \ - -Wall \ - -Wimplicit-function-declaration \ - -Werror \ - -Wextra \ - -no-inline-max-total-size \ - -no-inline-max-size \ - -I$(API_DIR) -I$(ETH_DIR) -I$(MLOG_DIR)/source -I$(RTE_INC) -g -O3 - -ifeq ($(ME),1) - CFLAGS += -DMLOG_ENABLED endif -SRC = $(ETH_DIR)/ethdi.c \ + +CC_SRC = $(ETH_DIR)/ethdi.c \ $(ETH_DIR)/ethernet.c \ $(SRC_DIR)/xran_up_api.c \ $(SRC_DIR)/xran_sync_api.c \ $(SRC_DIR)/xran_timer.c \ $(SRC_DIR)/xran_cp_api.c \ - $(SRC_DIR)/xran_transport.c \ + $(SRC_DIR)/xran_transport.c \ $(SRC_DIR)/xran_common.c \ $(SRC_DIR)/xran_ul_tables.c \ - $(SRC_DIR)/xran_main.c + $(SRC_DIR)/xran_frame_struct.c \ + $(SRC_DIR)/xran_app_frag.c \ + $(SRC_DIR)/xran_main.c + +CPP_SRC = $(SRC_DIR)/xran_compression.cpp + +CC_FLAGS += -std=gnu11 -Wall -Wno-deprecated-declarations \ + -fdata-sections \ + -ffunction-sections \ + -g \ + -Wall \ + -Wimplicit-function-declaration \ + -g -O3 + +CPP_FLAGS := -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D_GNU_SOURCE -D_REENTRANT -pipe -no-prec-div \ + -no-prec-div -fp-model fast=2\ + -no-prec-sqrt -falign-functions=16 -fast-transcendentals \ + -Werror -Wno-unused-variable -std=c++11 -mcmodel=large + +INC := -I$(API_DIR) -I$(ETH_DIR) -I$(SRC_DIR) -I$(RTE_INC) +DEF := +ifeq ($(MLOG),1) + INC += -I$(MLOG_DIR)/source + DEF += -DMLOG_ENABLED +else + DEF += -UMLOG_ENABLED +endif + +AS_FLAGS := +AR_FLAGS := rc + +PROJECT_OBJ_DIR := build/obj -FLEX_C_CRAN_LIB = libxran.a +CC_OBJS := $(patsubst %.c,%.o,$(CC_SRC)) +CPP_OBJS := $(patsubst %.cpp,%.o,$(CPP_SRC)) +AS_OBJS := $(patsubst %.s,%.o,$(AS_SRC)) +OBJS := $(CC_OBJS) $(CPP_OBJS) $(AS_OBJS) $(LIBS) +DIRLIST := $(addprefix $(PROJECT_OBJ_DIR)/,$(sort $(dir $(OBJS)))) -OBJ = $(foreach file,$(SRC),$(file:.c=.o)) +CC_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(CC_OBJS)) +CPP_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(CPP_OBJS)) +AS_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(AS_OBJS)) -all: flex_lib install +CC_FLAGS_FULL := $(CC_FLAGS) $(INC) $(DEF) +CPP_FLAGS_FULL := $(CPP_FLAGS) $(INC) $(DEF) -$(OBJ): %.o: %.c - $(CC) $(CFLAGS) -I$(API_DIR) -c $< -o $@ +AS_FLAGS := $(AS_FLAGS) $(INC) +PROJECT_DEP_FILE := $(PROJECT_OBJ_DIR)/$(PROJECT_NAME).dep -flex_lib:$(FLEX_C_CRAN_LIB) +ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) +GENERATE_DEPS := +else -$(FLEX_C_CRAN_LIB): $(OBJ) - $(AR) rsu $@ $^ +CC_DEPS := $(addprefix __dep__,$(subst ../,__up__,$(CC_SRC))) +CPP_DEPS := $(addprefix __dep__,$(subst ../,__up__,$(CPP_SRC))) +GENERATE_DEPS := generate_deps +endif + +all : welcome_line $(PROJECT_BINARY) + @echo $(PROJECT_BINARY) + +.PHONY : clear_dep +clear_dep: + @$(RM) $(PROJECT_DEP_FILE) + @echo [DEP] $(subst $(PROJECT_OBJ_DIR)/,,$(PROJECT_DEP_FILE)) + +$(CC_DEPS) : + @$(CC) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.c,%.o,$(subst __up__,../,$(subst __dep__,,$@))) $(CC_FLAGS_FULL) >> $(PROJECT_DEP_FILE) + +$(CPP_DEPS) : + @$(CPP) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.cpp,%.o,$(subst __up__,../,$(subst __dep__,,$@))) $(CPP_FLAGS_FULL) >> $(PROJECT_DEP_FILE) + +.PHONY : generate_deps +generate_deps : clear_dep $(CC_DEPS) $(CPP_DEPS) + + +.PHONY : echo_start_build +echo_start_build : + @echo [BUILD] $(PROJECT_TYPE) : $(PROJECT_NAME) + +$(DIRLIST) : + -@$(MD) $@ + +$(CC_OBJTARGETS) : + @echo [CC] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(CC) -c $(CC_FLAGS_FULL) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@)) + +$(CPP_OBJTARGETS) : + @echo [CPP] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(CPP) -c $(CPP_FLAGS_FULL) -o"$@" $(patsubst %.o,%.cpp,$(subst $(PROJECT_OBJ_DIR)/,,$@)) -install: $(FLEX_C_CRAN_LIB) - mkdir -p bin - @mv $(FLEX_C_CRAN_LIB) ./bin +$(AS_OBJTARGETS) : + @echo [AS] $(subst $(PROJECT_OBJ_DIR)/,,$@) + @$(AS) $(AS_FLAGS) -o"$@" $(patsubst %.o,%.s,$(subst $(PROJECT_OBJ_DIR)/,,$@)) +ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) + +include $(PROJECT_DEP_FILE) + +endif + +.PHONY: clean xclean clean: - @rm -rf $(FLEX_C_CRAN_LIB) $(OBJ) ./bin/$(FLEX_C_CRAN_LIB) + @echo [CLEAN] : $(PROJECT_NAME) + @$(RM) $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) + +xclean: clean +ifneq ($(wildcard $(PROJECT_DIR)/$(PROJECT_MAKE)),) + @echo [XCLEAN] : $(PROJECT_NAME) + @$(RM) $(PROJECT_BINARY) $(PROJECT_BINARY_LIB) $(PROJECT_DEP_FILE) +endif + +.PHONY : welcome_line +welcome_line : + @echo $(MYCUSTOMSPACE) + @echo Building $(PROJECT_BINARY) + @echo $(MYCUSTOMTAB)RTE_TARGET = $(RTE_TARGET) + @echo $(MYCUSTOMSPACE) + + +.PHONY : debug release + +debug : all +release : all -#include $(RTE_SDK)/mk/rte.extlib.mk +$(PROJECT_BINARY) : $(DIRLIST) echo_start_build $(GENERATE_DEPS) $(PRE_BUILD) $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) + @echo [AR] $(subst $(BUILDDIR)/,,$@) + @$(AR) $(AR_FLAGS) $@ $(CC_OBJTARGETS) $(CPP_OBJTARGETS) $(AS_OBJTARGETS) diff --git a/fhi_lib/lib/api/xran_compression.hpp b/fhi_lib/lib/api/xran_compression.hpp new file mode 100644 index 0000000..a2ee478 --- /dev/null +++ b/fhi_lib/lib/api/xran_compression.hpp @@ -0,0 +1,82 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + +#pragma once +#include + +// This configuration file sets global constants and macros which are +// of general use throughout the project. + +// All current IA processors of interest align their cache lines on +// this boundary. If the cache alignment for future processors changes +// then the most restrictive alignment should be set. +constexpr unsigned k_cacheByteAlignment = 64; + +// Force the data to which this macro is applied to be aligned on a cache line. +// For example: +// +// CACHE_ALIGNED float data[64]; +#define CACHE_ALIGNED alignas(k_cacheByteAlignment) + +// Hint to the compiler that the data to which this macro is applied +// can be assumed to be aligned to a cache line. This allows the +// compiler to generate improved code by using aligned reads and +// writes. +#define ASSUME_CACHE_ALIGNED(data) __assume_aligned(data, k_cacheByteAlignment); + +/// Intel compiler frequently complains about templates not being declared in an external +/// header. Templates are used throughout this project's source files to define local type-specific +/// versions of functions. Defining every one of these in a header is unnecessary, so the warnings +/// about this are turned off globally. +#pragma warning(disable:1418) +#pragma warning(disable:1419) + + +namespace BlockFloatCompander +{ + /// Compute 32 RB at a time + static constexpr int k_numBitsIQ = 16; + static constexpr int k_numRB = 16; + static constexpr int k_numRE = 12; + static constexpr int k_numREReal = k_numRE * 2; + static constexpr int k_numSampsExpanded = k_numRB * k_numREReal; + static constexpr int k_numSampsCompressed = k_numSampsExpanded + k_numRB; + static constexpr int k_iqWidth = 8; + + + struct CompressedData + { + /// Compressed data + CACHE_ALIGNED int8_t dataCompressed[k_numSampsCompressed]; + }; + + struct ExpandedData + { + /// Expanded data or input data to compressor + CACHE_ALIGNED int16_t dataExpanded[k_numSampsExpanded]; + }; + + void BlockFloatCompress_AVX512(const ExpandedData& dataIn, CompressedData* dataOut); + + void BlockFloatExpand_AVX512(const CompressedData& dataIn, ExpandedData* dataOut); + + void BlockFloatCompress_Basic(const ExpandedData& dataIn, CompressedData* dataOut); + + void BlockFloatExpand_Basic(const CompressedData& dataIn, ExpandedData* dataOut); + +} diff --git a/fhi_lib/lib/api/xran_cp_api.h b/fhi_lib/lib/api/xran_cp_api.h index 9d08e42..9972ea1 100644 --- a/fhi_lib/lib/api/xran_cp_api.h +++ b/fhi_lib/lib/api/xran_cp_api.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the definitions for Control Plane Messages APIs. * @@ -29,19 +28,27 @@ #ifndef _XRAN_CP_API_H_ #define _XRAN_CP_API_H_ -#include "xran_fh_lls_cu.h" +#ifdef __cplusplus +extern "C" { +#endif + + +#include "xran_fh_o_du.h" #include "xran_pkt_cp.h" +#include "xran_transport.h" + +#define XRAN_MAX_SECTIONDB_CTX 2 + +#define XRAN_MAX_NUM_EXTENSIONS 4 /* Maximum number of extensions in a section */ +#define XRAN_MAX_NUM_UE 16 /* Maximum number of UE */ +#define XRAN_MAX_NUM_ANT_BF 64 /* Maximum number of beamforming antenna, + * could be defined as XRAN_MAX_ANTENNA_NR */ +/* Maximum total number of beamforming weights (5.4.7.1.2) */ +#define XRAN_MAX_BFW_N (XRAN_MAX_NUM_ANT_BF*XRAN_MAX_NUM_UE) +#define XRAN_MAX_MODCOMP_ADDPARMS 2 + +#define XRAN_SECTIONEXT_ALIGN 4 /* alignment size in byte for section extension */ -/* Error Codes - * For errors and exceptions, all values will be negative */ -enum xran_errcodes { - XRAN_ERRCODE_OK = 0, - XRAN_ERRCODE_INVALIDPARAM, - XRAN_ERRCODE_OUTOFMEMORY, - XRAN_ERRCODE_FAILTOSEND, - XRAN_ERRCODE_INVALIDPACKET, - XRAN_ERRCODE_MAX - }; /** Control Plane section types, defined in 5.4 Table 5.1 */ enum xran_cp_sectiontype { @@ -107,6 +114,11 @@ enum xran_cp_symbolnuminc { XRAN_SYMBOLNUMBER_INC_MAX }; +/** Macro to convert the number of PRBs as defined in 5.4.5.6 */ +#define XRAN_CONVERT_NUMPRBC(x) ((x) > 255 ? 0 : (x)) + +#define XRAN_CONVERT_IQWIDTH(x) ((x) > 15 ? 0 : (x)) + /** Minimum number of symbols, defined in 5.4.5.7 */ #define XRAN_SYMBOLNUMBER_MIN 1 /** Maximum number of symbols, defined in 5.4.5.7 */ @@ -126,28 +138,131 @@ enum xran_cp_symbolnuminc { #define XRAN_LBTMODE_PARTIAL34 2 #define XRAN_LBTMODE_FULLSTOP 3 + +/** Control Plane section extension commands, defined in 5.4.6 Table 5.13 */ +enum xran_cp_sectionextcmd { + XRAN_CP_SECTIONEXTCMD_0 = 0, /**< Reserved, for future use */ + XRAN_CP_SECTIONEXTCMD_1 = 1, /**< Beamforming weights */ + XRAN_CP_SECTIONEXTCMD_2 = 2, /**< Beamforming attributes */ + XRAN_CP_SECTIONEXTCMD_3 = 3, /**< DL Precoding configuration parameters and indications, not supported */ + XRAN_CP_SECTIONEXTCMD_4 = 4, /**< Modulation compression parameter */ + XRAN_CP_SECTIONEXTCMD_5 = 5, /**< Modulation compression additional scaling parameters */ + XRAN_CP_SECTIONEXTCMD_MAX /* 6~127 reserved for future use */ + }; + +/** Macro to convert bfwIqWidth defined in 5.4.7.1.1, Table 5-15 */ +#define XRAN_CONVERT_BFWIQWIDTH(x) ((x) > 15 ? 0 : (x)) + +/** Beamforming Weights Compression Method 5.4.7.1.1, Table 5-16 */ +enum xran_cp_bfw_compression_method { + XRAN_BFWCOMPMETHOD_NONE = 0, /**< Uncopressed I/Q value */ + XRAN_BFWCOMPMETHOD_BLKFLOAT = 1, /**< I/Q mantissa value */ + XRAN_BFWCOMPMETHOD_BLKSCALE = 2, /**< I/Q scaled value */ + XRAN_BFWCOMPMETHOD_ULAW = 3, /**< compressed I/Q value */ + XRAN_BFWCOMPMETHOD_BEAMSPACE = 4, /**< beamspace I/Q coefficient */ + XRAN_BFWCOMPMETHOD_MAX /* reserved for future methods */ + }; + +/** Beamforming Attributes Bitwidth 5.4.7.2.1 */ +enum xran_cp_bfa_bitwidth { + XRAN_BFABITWIDTH_NO = 0, /**< the filed is no applicable or the default value shall be used */ + XRAN_BFABITWIDTH_2BIT = 1, /**< the filed is 2-bit bitwidth */ + XRAN_BFABITWIDTH_3BIT = 2, /**< the filed is 3-bit bitwidth */ + XRAN_BFABITWIDTH_4BIT = 3, /**< the filed is 4-bit bitwidth */ + XRAN_BFABITWIDTH_5BIT = 4, /**< the filed is 5-bit bitwidth */ + XRAN_BFABITWIDTH_6BIT = 5, /**< the filed is 6-bit bitwidth */ + XRAN_BFABITWIDTH_7BIT = 6, /**< the filed is 7-bit bitwidth */ + XRAN_BFABITWIDTH_8BIT = 7, /**< the filed is 8-bit bitwidth */ + }; + /** * This structure contains the information to generate the section body of C-Plane message */ struct xran_section_info { + uint8_t type; /* type of this section */ /* section type bit- */ /* 0 1 3 5 6 7 length */ - uint16_t id; /* X X X X X 12bits */ - uint8_t rb; /* X X X X X 1bit */ - uint8_t symInc; /* X X X X X 1bit */ - uint16_t startPrbc; /* X X X X X 10bits */ - uint8_t numPrbc; /* X X X X X 8bits */ + uint8_t startSymId; /* X X X X X X 4bits */ uint8_t numSymbol; /* X X X X 4bits */ + uint8_t symInc; /* X X X X X 1bit */ + uint16_t id; /* X X X X X 12bits */ uint16_t reMask; /* X X X X 12bits */ + uint16_t startPrbc; /* X X X X X 10bits */ + uint16_t numPrbc; /* X X X X X 8bits */ /* will be converted to zero if >255 */ + uint8_t rb; /* X X X X X 1bit */ + uint8_t iqWidth; /* X X X 4bits */ + uint8_t compMeth; /* X X X 4bits */ + uint8_t ef; /* X X X X 1bit */ + int32_t freqOffset; /* X 24bits */ uint16_t beamId; /* X X 15bits */ uint16_t ueId; /* X X 15bits */ uint16_t regFactor; /* X 16bits */ - int32_t freqOffset; /* X 24bits */ - uint8_t ef; /* X X X X 1bit */ - - uint8_t type; /* type of this section */ uint16_t pad0; }; + +struct xran_sectionext1_info { + uint16_t bfwNumber; /* number of bf weights in this section */ + uint8_t bfwiqWidth; + uint8_t bfwCompMeth; + uint16_t bfwIQ[XRAN_MAX_BFW_N*2]; /* I/Q pair, max 4KB with 16bits, 16UE and 64ANT */ + union { + uint8_t exponent; + uint8_t blockScaler; + uint8_t compBitWidthShift; + uint8_t activeBeamspaceCoeffMask[XRAN_MAX_BFW_N]; /* ceil(N/8)*8, should be multiple of 8 */ + } bfwCompParam; + }; + +struct xran_sectionext2_info { + uint8_t bfAzPtWidth; /* beamforming zenith beamwidth parameter */ + uint8_t bfAzPt; + uint8_t bfZePtWidth; /* beamforming azimuth beamwidth parameter */ + uint8_t bfZePt; + uint8_t bfAz3ddWidth; /* beamforming zenith pointing parameter */ + uint8_t bfAz3dd; + uint8_t bfZe3ddWidth; /* beamforming azimuth pointing parameter */ + uint8_t bfZe3dd; + + uint8_t bfAzSI; + uint8_t bfZeSI; + }; + +struct xran_sectionext3_info { /* NOT SUPPORTED */ + uint8_t codebookIdx; + uint8_t layerId; + uint8_t numLayers; + uint8_t txScheme; + uint16_t crsReMask; + uint8_t crsShift; + uint8_t crsSymNum; + uint16_t beamIdAP1; + uint16_t beamIdAP2; + uint16_t beamIdAP3; + }; + +struct xran_sectionext4_info { + uint8_t csf; + uint8_t pad0; + uint16_t modCompScaler; + }; + +struct xran_sectionext5_info { + uint8_t num_sets; + struct { + uint16_t csf; +// uint16_t pad0; + uint16_t mcScaleReMask; + uint16_t mcScaleOffset; + } mc[XRAN_MAX_MODCOMP_ADDPARMS]; + }; + +struct xran_sectionext_info { + uint16_t type; + uint16_t len; + void *data; + }; + + /** * This structure contains the information to generate the section header of C-Plane message */ struct xran_cp_header_params { @@ -171,12 +286,15 @@ struct xran_cp_header_params { /** * This structure to hold the information to generate the sections of C-Plane message */ struct xran_section_gen_info { - struct xran_section_info info; /**< The information for section */ + struct xran_section_info info; /**< The information for section */ - uint32_t exDataSize; - /**< Extension or type 6/7 data size, not supported */ - void *exData; - /*(< The pointer to the extension or type 6/7 data, not supported */ + uint32_t exDataSize; /**< The number of Extensions or type 6/7 data */ + /** the array to store section extension */ + struct { + uint16_t type; /**< the type of section extension */ + uint16_t len; /**< length of extension data */ + void *data; /**< pointer to extension data */ + } exData[XRAN_MAX_NUM_EXTENSIONS]; }; /** @@ -192,8 +310,25 @@ struct xran_cp_gen_params { /**< Array of the section information */ }; +/** + * This structure to hold the information of RB allocation from PHY + * to send data for allocated RBs only. */ +struct xran_cp_rbmap_list { + uint16_t grp_id; /**< group id for this entry, reserved for future use */ + + uint8_t sym_start; /**< Start symbol ID */ + uint8_t sym_num; /**< Number of symbols */ + + uint16_t rb_start; /**< Start RB position */ + uint16_t rb_num; /**< Number of RBs */ + + uint16_t beam_id; /**< Bean Index */ + uint8_t comp_meth; /**< Compression method */ + uint8_t pad0; + }; + -uint16_t xran_get_cplength(int cpLength, int uval); +uint16_t xran_get_cplength(int cpLength); int32_t xran_get_freqoffset(int freqOffset, int scs); int xran_prepare_ctrl_pkt(struct rte_mbuf *mbuf, @@ -201,20 +336,29 @@ int xran_prepare_ctrl_pkt(struct rte_mbuf *mbuf, uint8_t CC_ID, uint8_t Ant_ID, uint8_t seq_id); +int xran_parse_cp_pkt(struct rte_mbuf *mbuf, + struct xran_cp_gen_params *result, + struct xran_recv_packet_info *pkt_info); + int xran_cp_init_sectiondb(void *pHandle); int xran_cp_free_sectiondb(void *pHandle); int xran_cp_add_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, - struct xran_section_info *info); + uint8_t ctx_id, struct xran_section_info *info); +int xran_cp_add_multisection_info(void *pHandle, + uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id, + struct xran_cp_gen_params *gen_info); struct xran_section_info *xran_cp_find_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, - uint16_t section_id); + uint8_t ctx_id, uint16_t section_id); struct xran_section_info *xran_cp_iterate_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, uint32_t *next); -int xran_cp_getsize_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id); -int xran_cp_reset_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id); + uint8_t ctx_id, uint32_t *next); +int xran_cp_getsize_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id); +int xran_cp_reset_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id); + +#ifdef __cplusplus +} +#endif #endif /* _XRAN_CP_API_H_ */ diff --git a/fhi_lib/lib/api/xran_fh_lls_cu.h b/fhi_lib/lib/api/xran_fh_o_du.h similarity index 57% rename from fhi_lib/lib/api/xran_fh_lls_cu.h rename to fhi_lib/lib/api/xran_fh_o_du.h index 24bacba..6274662 100644 --- a/fhi_lib/lib/api/xran_fh_lls_cu.h +++ b/fhi_lib/lib/api/xran_fh_o_du.h @@ -18,18 +18,20 @@ /** - * @brief This file provides public interface to XRAN Front Haul layer implementation as defined in the - * XRAN-FH.CUS.0-v02.00 spec. Implementation is specific to lls-CU node - * for 5G NR Radio Access technology + * @brief This file provides public interface to xRAN Front Haul layer implementation as defined in the + * ORAN-WG4.CUS.0-v01.00 spec. Implementation specific to + * Lower Layer Split Central Unit (O-DU): a logical node that includes the eNB/gNB functions as + * listed in section 2.1 split option 7-2x, excepting those functions allocated exclusively to the O-RU. + * The O-DU controls the operation of O-RUs for 5G NR Radio Access technology * - * @file xran_fh_lls_cu.h + * @file xran_fh_o_du.h * @ingroup group_lte_source_xran * @author Intel Corporation * **/ -#ifndef _XRAN_FH_LLS_CU_H_ -#define _XRAN_FH_LLS_CU_H_ +#ifndef _XRAN_FH_O_DU_H_ +#define _XRAN_FH_O_DU_H_ #ifdef __cplusplus extern "C" { @@ -88,31 +90,54 @@ extern "C" { * parameters supplied. This may be because a particular * capability is not supported by the current implementation. */ -/** Macro to calculate TTI number [0:7999] from symbol index [0: 112000-1] used by timing thread */ +#define XRAN_STATUS_INVALID_PACKET (-7) +/**< + * @ingroup xran + * Recevied packet does not have correct format. */ + +/** Macro to calculate TTI number from symbol index used by timing thread */ #define XranGetTtiNum(symIdx, numSymPerTti) (((uint32_t)symIdx / (uint32_t)numSymPerTti)) -/** Macro to calculate Symbol number [0:7] for given slot from symbol index [0: 112000-1] */ +/** Macro to calculate Symbol number for given slot from symbol index */ #define XranGetSymNum(symIdx, numSymPerTti) (((uint32_t)symIdx % (uint32_t)numSymPerTti)) -/** Macro to calculate Frame number [0:99] for given tti [0: 7999] */ +/** Macro to calculate Frame number for given tti */ #define XranGetFrameNum(tti,numSubFramePerSystemFrame, numSlotPerSubFrame) ((uint32_t)tti / ((uint32_t)numSubFramePerSystemFrame * (uint32_t)numSlotPerSubFrame)) -/** Macro to calculate Subframe number [0:9] for given tti [0: 7999] */ +/** Macro to calculate Subframe number for given tti */ #define XranGetSubFrameNum(tti, numSlotPerSubFrame, numSubFramePerSystemFrame) (((uint32_t)tti/(uint32_t)numSlotPerSubFrame) % (uint32_t)numSubFramePerSystemFrame) -/** Macro to calculate Slot number [0:7] for given tti [0: 7999] */ +/** Macro to calculate Slot number */ #define XranGetSlotNum(tti, numSlotPerSfn) ((uint32_t)tti % ((uint32_t)numSlotPerSfn)) -#define XRAN_PORTS_NUM (1) /**< number of XRAN ports supported */ -#define XRAN_N_FE_BUF_LEN (80)/** Number of TTIs (slots) */ -#define XRAN_MAX_SECTOR_NR (4) /**< Max sectors per XRAN port */ -#define XRAN_MAX_ANTENNA_NR (4) /**< Max antenna per port */ -#define XRAN_NUM_OF_SYMBOL_PER_SLOT ( 14 ) /**< Number of symbols per slot */ +#define XRAN_PORTS_NUM (1) /**< number of XRAN ports (aka O-RU devices) supported */ +#define XRAN_N_FE_BUF_LEN (80) /**< Number of TTIs (slots) */ +#define XRAN_MAX_SECTOR_NR (12) /**< Max sectors per XRAN port */ +#define XRAN_MAX_ANTENNA_NR (4) /**< Max antenna per port */ +#define XRAN_NUM_OF_SYMBOL_PER_SLOT (14) /**< Number of symbols per slot */ +#define XRAN_MAX_TDD_PERIODICITY (80) /**< Max TDD pattern period */ +#define XRAN_MAX_CELLS_PER_PORT (XRAN_MAX_SECTOR_NR) /**< Max cells mapped to XRAN port */ +#define XRAN_COMPONENT_CARRIERS_MAX (XRAN_MAX_SECTOR_NR) /**< number of CCs */ +#define XRAN_NUM_OF_ANT_RADIO (XRAN_MAX_SECTOR_NR*XRAN_MAX_ANTENNA_NR) /**< Max Number of Antennas supported for all CC on single XRAN port */ +#define XRAN_MAX_PRBS (275) /**< Max of PRBs per CC per antanna for 5G NR */ -#define XRAN_MAX_CELLS_PER_PORT (4) /**< Max cells mapped to XRAN port */ -#define XRAN_COMPONENT_CARRIERS_MAX XRAN_MAX_SECTOR_NR /**< number of CCs */ -#define XRAN_NUM_OF_ANT_RADIO 16 /**< Max Number of Antennas supported for all CC on single XRAN port */ #define XRAN_MAX_PKT_BURST (448+4) /**< 4x14x8 symbols per ms */ #define XRAN_N_MAX_BUFFER_SEGMENT XRAN_MAX_PKT_BURST /**< Max number of segments per ms */ -#define XRAN_STRICT_PARM_CHECK (1) /**< enable parameter check for C-plane */ +#define XRAN_STRICT_PARM_CHECK (1) /**< enable parameter check for C-plane */ + +/* Slot type definition */ +#define XRAN_SLOT_TYPE_INVALID (0) /**< invalid slot type */ +#define XRAN_SLOT_TYPE_DL (1) /**< DL slot */ +#define XRAN_SLOT_TYPE_UL (2) /**< UL slot */ +#define XRAN_SLOT_TYPE_SP (3) /**< Special slot */ +#define XRAN_SLOT_TYPE_FDD (4) /**< FDD slot */ +#define XRAN_SLOT_TYPE_LAST (5) /**< MAX slot */ + +/* symbol type definition */ +#define XRAN_SYMBOL_TYPE_DL (0) /**< DL symbol */ +#define XRAN_SYMBOL_TYPE_UL (1) /**< UL symbol */ +#define XRAN_SYMBOL_TYPE_GUARD (2) /**< GUARD symbol */ +#define XRAN_SYMBOL_TYPE_FDD (3) /**< FDD symbol */ + +#define XRAN_NUM_OF_SLOT_IN_TDD_LOOP (80)/**< MAX number of slot for TDD repetition */ //#define _XRAN_DEBUG /**< Enable debug log */ //#define _XRAN_VERBOSE /**< Enable verbose log */ @@ -145,6 +170,18 @@ extern "C" { __FILE__, \ __LINE__, ##__VA_ARGS__) +enum XranFrameDuplexType +{ + XRAN_FDD = 0, XRAN_TDD +}; + +enum xran_if_state +{ + XRAN_INIT = 0, + XRAN_RUNNING, + XRAN_STOPPED +}; + /** ****************************************************************************** * @ingroup xran @@ -159,7 +196,7 @@ enum xran_compression_method { XRAN_COMPMETHOD_ULAW = 3, XRAN_COMPMETHOD_MODULATION = 4, XRAN_COMPMETHOD_MAX - }; +}; /** ****************************************************************************** @@ -176,35 +213,23 @@ enum callback_to_phy_id XRAN_CB_MAX /**< max number of callbacks */ }; -typedef int32_t XranStatusInt32; /**< Xran status return value */ +typedef int32_t xran_status_t; /**< Xran status return value */ /** callback function type for Symbol packet */ -typedef void (*XRANFHSYMPROCCB)(void*); +typedef void (*xran_callback_sym_fn)(void*); /** Callback function type for TTI event */ -typedef int (*XRANFHTTIPROCCB)(void* ); +typedef int (*xran_fh_tti_callback_fn)(void*); /** Callback function type packet arrival from transport layer (ETH or IP) */ -typedef void (*XranTransportBlockCallbackFn)(void*, int32_t); +typedef void (*xran_transport_callback_fn)(void*, int32_t); + +/** Callback functions to poll BBdev encoder */ +typedef int16_t (*phy_encoder_poll_fn)(void); + +/** Callback functions to poll BBdev secoder */ +typedef int16_t (*phy_decoder_poll_fn)(void); -/** -* Component Carrier Initialization -*/ -typedef struct tagXRANCCINIT -{ - uint32_t RadioMode; /**< XRAN mode Cat A or Cat B on given CC */ - uint32_t nTxAnt; /**< Number of TX antennas */ - uint32_t nRxAnt; /**< Number of RX antennas */ - uint32_t radiobw; /**< bandwidth id */ - uint32_t dpdk_port; /**< networking layer port id */ - char *dpdk_pcie_eth_dev; /**< pcie device for this cc */ - char *ru_mac_str; /**< mac address of RU */ - uint32_t ulAgc; /**< state of UL AGC (ON/OFF) */ - uint32_t numCell; /**< Number of Cells per port per CC */ - uint32_t phyInsId[XRAN_MAX_CELLS_PER_PORT]; /**< Mapping of Cell ID to CC */ - uint32_t dpdkRxCore; /**< DPDK RX Core */ - uint32_t dpdkTxCore; /**< DPDK TX Core */ -}XRANCCINIT, *PXRANCCINIT; /** XRAN port enum */ enum xran_vf_ports @@ -214,22 +239,47 @@ enum xran_vf_ports XRAN_VF_MAX }; -/** DPDK IO configuration for XRAN layer */ -typedef struct tagXRAN_IO_LOOP_CFG +/** XRAN category enum */ +enum xran_category +{ + XRAN_CATRGORY_A = 0, + XRAN_CATRGORY_B = 1, + XRAN_CATRGORY_MAX +}; + +/** type of beamforming */ +enum xran_beamforming_type { - uint8_t id; - char *dpdk_dev[XRAN_VF_MAX]; - int core; - int system_core; /* Needed as DPDK will change your starting core. */ - int pkt_proc_core; /* Needed for packet processing thread. */ - int pkt_aux_core; /* Needed for debug purposes. */ - int timing_core; /* Needed for getting precise time */ - int port[XRAN_VF_MAX]; /* This is auto-detected, no need to set. */ -}XRAN_IO_LOOP_CFG, *PXRAN_IO_LOOP_CFG; + XRAN_BEAM_ID_BASED = 0, /**< beam index based */ + XRAN_BEAM_WEIGHT, /**< beam forming weights */ + XRAN_BEAM_ATTRIBUTE, /**< beam index based */ +}; -/** XRAN spec section 3.1.3.1.6 ecpriRtcid / ecpriPcid define */ -typedef struct tagXRANEAXCIDCONFIG +/** state of bbdev with xran */ +enum xran_bbdev_init { + XRAN_BBDEV_NOT_USED = -1, /**< BBDEV is disabled */ + XRAN_BBDEV_MODE_HW_OFF = 0, /**< BBDEV is enabled for SW sim mode */ + XRAN_BBDEV_MODE_HW_ON = 1, /**< BBDEV is enable for HW */ + XRAN_BBDEV_MODE_MAX +}; + +/** DPDK IO configuration for XRAN layer */ +struct xran_io_cfg { + uint8_t id; /**< should be (0) for O-DU or (1) O-RU (debug) */ + char *dpdk_dev[XRAN_VF_MAX]; /**< VFs devices */ + char *bbdev_dev[1]; /**< BBDev dev name */ + int32_t bbdev_mode; /**< DPDK for BBDev */ + int32_t core; /**< reservd */ + int32_t system_core; /**< reservd */ + int32_t pkt_proc_core; /**< reservd */ + int32_t pkt_aux_core; /**< reservd */ + int32_t timing_core; /**< core used by xRAN */ + int32_t port[XRAN_VF_MAX]; /**< VFs ports */ +}; + +/** XRAN spec section 3.1.3.1.6 ecpriRtcid / ecpriPcid define */ +struct xran_eaxcid_config { uint16_t mask_cuPortId; /**< Mask CU PortId */ uint16_t mask_bandSectorId; /**< Mask Band */ uint16_t mask_ccId; /**< Mask CC */ @@ -237,35 +287,27 @@ typedef struct tagXRANEAXCIDCONFIG uint8_t bit_cuPortId; /**< bandsectorId + ccId + ruportId */ uint8_t bit_bandSectorId; /**< ccId + ruPortId */ - uint8_t bit_ccId; /**< ruportId */ - uint8_t bit_ruPortId; /**< 0 */ -}XRANEAXCIDCONFIG, *PXRANEAXCIDCONFIG; + uint8_t bit_ccId; /**< ruportId */ + uint8_t bit_ruPortId; /**< 0 */ +}; /** * XRAN Front haul interface initialization settings */ -typedef struct tagXRANFHINIT -{ - uint32_t llscuId; /**< lls-cu ID */ - uint32_t nSec; /**< number of sectors, shall be 1 */ - XRANCCINIT ccCfg[XRAN_COMPONENT_CARRIERS_MAX]; /**< configuration of each CCs */ - XRANEAXCIDCONFIG eAxCId_conf; /**< config of ecpriRtcid/ecpriPcid */ - uint32_t radio_iface; /**< enable/disable radio */ - uint32_t dpdkMasterCore; /**< master core of DPDK */ - uint32_t dpdkMemorySize; /**< huge pages allocation for DPDK */ - uint32_t dpdkIrqMode; /**< DPDK IRQ or PMD mode */ +struct xran_fh_init { + struct xran_io_cfg io_cfg;/**< DPDK IO for XRAN */ + struct xran_eaxcid_config eAxCId_conf; /**< config of ecpriRtcid/ecpriPcid */ + uint32_t dpdkBasebandFecMode; /**< DPDK Baseband FEC device mode (0-SW, 1-HW) */ char *dpdkBasebandDevice; /**< DPDK Baseband device address */ - uint32_t singleThreadTxRx; - uint32_t bbuPoolCores; /**< DPDK cores for BBU pool */ - uint32_t radioEnabled; /**< reserved */ - uint32_t powerSaveEn; /**< reserved */ - char *filePrefix; /**< DPDK prefix */ - XRAN_IO_LOOP_CFG io_cfg;/**< DPDK IO for XRAN */ - uint8_t xranMode; /**< mode: lls-CU or RU */ - int8_t *p_lls_cu_addr; /**< lls-CU Ethernet Mac Address */ - int8_t *p_ru_addr; /**< RU Ethernet Mac Address */ - uint32_t ttiPeriod; /**< TTI period */ + char *filePrefix; /**< DPDK prefix */ + + enum xran_category xranCat; /**< mode: Catergory A or Category B */ + + uint32_t mtu; /**< maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single + xRAN network layer transaction. supported 1500 bytes and 9600 bytes (Jumbo Frame) */ + int8_t *p_o_du_addr; /**< O-DU Ethernet Mac Address */ + int8_t *p_o_ru_addr; /**< O-RU Ethernet Mac Address */ uint16_t Tadv_cp_dl; /**< Table 2 7 : xRAN Delay Management Model Parameters */ uint16_t T2a_min_cp_dl; /**< Table 2 7 : xRAN Delay Management Model Parameters */ @@ -285,158 +327,157 @@ typedef struct tagXRANFHINIT uint16_t Ta4_min; /**< Table 2 7 : xRAN Delay Management Model Parameters */ uint16_t Ta4_max; /**< Table 2 7 : xRAN Delay Management Model Parameters */ - uint8_t enableCP; /**< enable C-plane */ - uint8_t cp_vlan_tag; /**< C-plane vlan tag */ - uint8_t up_vlan_tag; /**< U-plane vlan tag */ - int32_t debugStop; /**< enable auto stop */ -} XRANFHINIT, *PXRANFHINIT; + uint8_t enableCP; /**< enable C-plane */ + uint8_t prachEnable; /**< enable PRACH */ + uint8_t cp_vlan_tag; /**< C-plane vlan tag */ + uint8_t up_vlan_tag; /**< U-plane vlan tag */ + int32_t debugStop; /**< enable auto stop */ + int32_t debugStopCount; /**< enable auto stop after number of Tx packets */ + int32_t DynamicSectionEna; /**< enable dynamic C-Plane section allocation */ +}; -/** XRAN Playback format */ -typedef enum { - XRAN_RADIO_PLAYBACK_TIME_DOMAIN = 0, - XRAN_RADIO_PLAYBACK_FREQ_DOMAIN = 1 -} XranPlaybackFormatEnum; +struct xran_cp_bf_weight{ + int16_t weight[64]; +}; +struct xran_cp_bf_attribute{ + int16_t weight[4]; +}; +struct xran_cp_bf_precoding{ + int16_t weight[4]; +}; + +/** PRB element structure */ +struct xran_prb_elm { + int16_t nRBStart; /**< start RB of RB allocation */ + int16_t nRBSize; /**< number of RBs used */ + int16_t nStartSymb; /**< start symbol ID */ + int16_t numSymb; /**< number of symbol */ + int16_t nBeamIndex; /**< beam index for given PRB */ + int16_t compMethod; /**< compression index for given PRB */ + int16_t BeamFormingType; + union { + struct xran_cp_bf_attribute bf_attribute; + struct xran_cp_bf_precoding bf_precoding; + }; +}; + +/** PRB map structure */ +struct xran_prb_map { + uint8_t dir; /**< DL or UL direction */ + uint8_t xran_port; /**< xran id of given RU [0-(XRAN_PORTS_NUM-1)] */ + uint16_t band_id; /**< xran band id */ + uint16_t cc_id; /**< componnent carrier id [0 - (XRAN_MAX_SECTOR_NR-1)] */ + uint16_t ru_port_id; /**< RU device antenna port id [0 - (XRAN_MAX_ANTENNA_NR-1) */ + uint16_t tti_id; /**< xRAN slot id [0 - (max tti-1)] */ + uint8_t start_sym_id; /**< start symbol Id [0-13] */ + uint8_t bf_weight_update; /**need to update beam weight or not*/ + uint32_t nPrbElm; /**< total number of PRBs for given map [0- (XRAN_MAX_PRBS-1)] */ + struct xran_prb_elm prbMap[XRAN_MAX_PRBS]; + struct xran_cp_bf_weight bf_weight; +}; /* PRACH config required for XRAN based FH */ -typedef struct tagXRANPRACHCONFIG +struct xran_prach_config { - /**** word 5 *****/ /* PRACH config*/ - /** PRACH Configuration Index*/ - uint8_t nPrachConfIdx; - /** PRACH Sub-carrier spacing + uint8_t nPrachConfIdx; /**< PRACH Configuration Index*/ + uint8_t nPrachSubcSpacing; + /**< PRACH Sub-carrier spacing Value:0->1 For below 6GHz the values indicate 15kHz or 30kHz For above 6GHz the values indicate 60kHz or 120kHz*/ - /*PRACH zeroCorrelationZoneConfig */ - uint8_t nPrachSubcSpacing; - /** PRACH zeroCorrelationZoneConfig */ - uint8_t nPrachZeroCorrConf; - /** PRACH restrictedSetConfig */ - uint8_t nPrachRestrictSet; - - /**** word 6 *****/ - /** PRACH Root Sequence Index */ - uint16_t nPrachRootSeqIdx; - /** PRACH prach-frequency-start */ - uint16_t nPrachFreqStart; - - /** PRACH prach-frequency-offset */ - int32_t nPrachFreqOffset; - /** PRACH Filter index */ - uint8_t nPrachFilterIdx; -}XRANPRACHCONFIG, *PXRANPRACHCONFIG; - -/** XRAN front haul playback configuration (not supported in 19.03) */ -typedef struct tagXRANFHPLAYBACK -{ - XranPlaybackFormatEnum TxPlayFormatType; /**< type of play back files [Time|Freq] */ - - unsigned long TxPlayBufAddr[XRAN_NUM_OF_ANT_RADIO]; /**< pointer to buffers to play */ - uint32_t TxPlayBufSize; /**< Buffer size */ - - char* TxPlayFileName[XRAN_NUM_OF_ANT_RADIO]; /**< files to play */ - uint32_t TxPlayFileSize; /**< expected the same size for all Ant */ - -}XRANPLAYBACKCONFIG,*PXRANPLAYBACKCONFIG; - -/** XRAN front haul logging configuration (not supported in 19.03) */ -typedef struct tagXRANFHLOGCONF -{ - /* logging */ - unsigned long TxLogBufAddr; - uint32_t TxLogBufSize; - - unsigned long TxLogIfftInAddr; - uint32_t TxLogIfftInSize; - - unsigned long TxLogIfft1200InAddr; - uint32_t TxLogIfft1200InSize; - - unsigned long RxLogFftOutAddr; - uint32_t RxLogFftOutSize; - - unsigned long RxLogFftOutExpAddr; - uint32_t RxLogFftOutExpSize; - - unsigned long RxLogFftOutGainAddr; - uint32_t RxLogFftOutGainSize; - - unsigned long RxLogBufAddr; - uint32_t RxLogBufSize; - - unsigned long RxLogAlawBufAddr; - uint32_t RxLogAlawBufSize; + uint8_t nPrachZeroCorrConf; /**< PRACH zeroCorrelationZoneConfig */ + uint8_t nPrachRestrictSet; /**< PRACH restrictedSetConfig */ + uint16_t nPrachRootSeqIdx; /**< PRACH Root Sequence Index */ + uint16_t nPrachFreqStart; /**< PRACH prach-frequency-start */ + int32_t nPrachFreqOffset; /**< PRACH prach-frequency-offset */ + uint8_t nPrachFilterIdx; /**< PRACH Filter index */ +}; - unsigned long RxLogPrachBufAddr; - uint32_t RxLogPrachBufSize; +/** XRAN slot configuration */ +struct xran_slot_config { + uint8_t nSymbolType[XRAN_NUM_OF_SYMBOL_PER_SLOT]; /**< Defines the Symbol type for all 14 symbols in a slot. 0: DL, 1: UL, 2: Guard */ + uint8_t reserved[2]; +}; - uint32_t cfg_dl_iq_buf_enabled; - uint32_t cfg_ul_iq_buf_enabled; +/** XRAN front haul frame config */ +struct xran_frame_config { + uint8_t nFrameDuplexType; /**< Frame Duplex type: 0 -> FDD, 1 -> TDD */ + uint8_t nNumerology; /**< Numerology, determine sub carrier spacing, Value: 0->4 + 0: 15khz, 1: 30khz, 2: 60khz + 3: 120khz, 4: 240khz */ + uint8_t nTddPeriod; /**< TDD period */ + struct xran_slot_config sSlotConfig[XRAN_MAX_TDD_PERIODICITY]; + /**< TDD Slot configuration - If nFrameDuplexType = TDD(1), then this config defines the slot config type for each slot.*/ + /* The number of slots need to be equal to nTddPeriod */ +}; -}XRANFHLOGCONF, *PXRANFHLOGCONF; +/** XRAN-PHY interface byte order */ +enum xran_input_byte_order { + XRAN_NE_BE_BYTE_ORDER = 0, /**< Network byte order (Big endian), xRAN lib doesn't do swap */ + XRAN_CPU_LE_BYTE_ORDER /**< CPU byte order (Little endian), xRAN lib does do swap */ +}; -/** XRAN front haul frame config */ -typedef struct tagXRANFRAMECONFIG -{ - /** Frame Duplex type: 0 -> FDD, 1 -> TDD */ - uint8_t nFrameDuplexType; - /** Numerology, determine sub carrier spacing, Value: 0->4 - 0: 15khz, 1: 30khz, 2: 60khz - 3: 120khz, 4: 240khz */ - uint8_t nNumerology; - /** TDD period */ - uint8_t nTddPeriod; -}XRANFRAMECONFIG, *PXRANFRAMECONFIG; - -/** XRAN front haul BBU pooling config */ -typedef struct tagXRANBBUPOOLCONFIG -{ - uint32_t isBbuPool; /**< FH running with BBU pool */ -}XRANBBUPOOLCONFIG, *PXRANBBUPOOLCONFIG; +/** XRAN-PHY interface I and Q order */ +enum xran_input_i_q_order { + XRAN_I_Q_ORDER = 0, /**< I , Q */ + XRAN_Q_I_ORDER /**< Q , I */ +}; /** XRAN front haul IQ compression settings */ -typedef struct tagXRANRUCONFIG -{ +struct xran_ru_config { uint8_t iqWidth; /**< IQ bit width */ uint8_t compMeth; /**< Compression method */ uint8_t fftSize; /**< FFT Size */ -}XRANRUCONFIG, *PXRANRUCONFIG; - -/** XRAN front haul Phase compensation settings */ -typedef struct -{ - uint32_t nSecNum; - uint32_t nPhaseCompFlag; - uint32_t nDlArfcn[XRAN_MAX_SECTOR_NR]; - uint32_t nUlArfcn[XRAN_MAX_SECTOR_NR]; -}XRANPHASECompConfig; + enum xran_input_byte_order byteOrder; /**< Order of bytes in int16_t in buffer. Big or little endian */ + enum xran_input_i_q_order iqOrder; /**< order of IQs in the buffer */ +}; /** * @ingroup xran - *XRAN front haul general configuration */ -typedef struct tagXRANFHCONFIG -{ + * XRAN front haul general configuration */ +struct xran_fh_config { uint32_t dpdk_port; /**< DPDK port number used for FH */ uint32_t sector_id; /**< Band sector ID for FH */ uint32_t nCC; /**< number of Component carriers supported on FH */ - uint32_t neAxc; /**< number of eAxc supported on FH */ - XRANPLAYBACKCONFIG playback_conf;/**< configuration of playback of IQs supported with FH */ - XRANFHLOGCONF log_conf; /**< config of logging functionality supported by FH */ - XRANFHTTIPROCCB ttiCb; /**< call back for TTI event */ + uint32_t neAxc; /**< number of eAxc supported on one CC*/ + uint16_t nDLFftSize; /**< DL FFT size */ + uint16_t nULFftSize; /**< UL FFT size */ + uint16_t nDLRBs; /**< DL PRB */ + uint16_t nULRBs; /**< UL PRB */ + uint32_t nDLAbsFrePointA; /**< Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 */ + uint32_t nULAbsFrePointA; /**< Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 */ + uint32_t nDLCenterFreqARFCN; /**< center frerquency for DL in MHz */ + uint32_t nULCenterFreqARFCN; /**< center frerquency for UL in MHz */ + xran_fh_tti_callback_fn ttiCb; /**< call back for TTI event */ void *ttiCbParam; /**< parameters of call back function */ - XRANPRACHCONFIG prach_conf; /**< PRACH specific configurations for FH */ - XRANFRAMECONFIG frame_conf; /**< frame config */ - XRANBBUPOOLCONFIG bbu_conf; /**< BBU pool config */ - XRANRUCONFIG ru_conf; /**< config of RU as per XRAN spec */ - XRANPHASECompConfig phase_compensation; /**< phase compensation settings */ -}XRANFHCONFIG, *PXRANFHCONFIG; + struct xran_prach_config prach_conf; /**< PRACH specific configurations for FH */ + struct xran_frame_config frame_conf; /**< frame config */ + struct xran_ru_config ru_conf; /**< config of RU as per XRAN spec */ + + phy_encoder_poll_fn bbdev_enc; /**< call back to poll BBDev encoder */ + phy_decoder_poll_fn bbdev_dec; /**< call back to poll BBDev decoder */ + + uint32_t log_level; /**< configuration of log level */ +}; + +/** + * @ingroup xran + * XRAN front haul statistic counters according to Table 7 1 : Common Counters for both DL and UL */ +struct xran_common_counters{ + uint64_t Rx_on_time; /**< Data was received on time (applies to user data reception window) */ + uint64_t Rx_early; /**< Data was received too early (applies to user data reception window) */ + uint64_t Rx_late; /**< Data was received too late (applies to user data reception window) */ + uint64_t Rx_corrupt; /**< Corrupt/Incorrect header packet */ + uint64_t Rx_pkt_dupl; /**< Duplicated packet */ + uint64_t Total_msgs_rcvd; /**< Total messages received (on all links) */ +}; /** * @ingroup xran * CC instance handle pointer type */ -typedef void * XranCcInstanceHandleVoidP; +typedef void * xran_cc_handle_t; /** ***************************************************************************** @@ -448,7 +489,7 @@ typedef void * XranCcInstanceHandleVoidP; * buffer segment may contain several equally sized elements. * *****************************************************************************/ -typedef struct XRANFlatBuffer +struct xran_flat_buffer { uint32_t nElementLenInBytes; /**< The Element length specified in bytes. @@ -465,7 +506,9 @@ typedef struct XRANFlatBuffer /**< The data pointer is a virtual address, however the actual data pointed * to is required to be in contiguous physical memory unless the field requiresPhysicallyContiguousMemory in CpaInstanceInfo is false. */ -} XRANFlatBufferStruct; + void *pCtrl; + /**< pointer to control section coresponding to data buffer */ +}; /** ***************************************************************************** @@ -485,11 +528,11 @@ typedef struct XRANFlatBuffer * the pPrivateMetaData memory. * *****************************************************************************/ -typedef struct XRANBufferList +struct xran_buffer_list { uint32_t nNumBuffers; /**< Number of pointers */ - XRANFlatBufferStruct *pBuffers; + struct xran_flat_buffer *pBuffers; /**< Pointer to an unbounded array containing the number of CpaFlatBuffers * defined by nNumBuffers */ void *pUserData; @@ -501,7 +544,7 @@ typedef struct XRANBufferList * cpaCyBufferListGetMetaSize. If cpaCyBufferListGetMetaSize returns a size * of zero no memory needs to be allocated, and this parameter can be NULL. */ -} XRANBufferListStruct; +}; /** * @ingroup xran @@ -519,7 +562,7 @@ typedef struct XRANBufferList * 0 - on success * Error codes returned via rte_errno */ -int32_t xran_init(int argc, char *argv[], PXRANFHINIT p_xran_fh_init, char *appName, void ** pHandle); +int32_t xran_init(int argc, char *argv[], struct xran_fh_init *p_xran_fh_init, char *appName, void ** pHandle); /** * @ingroup xran @@ -532,13 +575,13 @@ int32_t xran_init(int argc, char *argv[], PXRANFHINIT p_xran_fh_init, char *appN * @param nNumInstances * total number of instances of CC * @param pSectorInstanceHandles - * Pointer to XranCcInstanceHandleVoidP where to store Handle pointer + * Pointer to xran_cc_handle_t where to store Handle pointer * * @return * 0 - on success */ int32_t xran_sector_get_instances (void * pHandle, uint16_t nNumInstances, - XranCcInstanceHandleVoidP * pSectorInstanceHandles); + xran_cc_handle_t * pSectorInstanceHandles); /** * @ingroup xran @@ -587,13 +630,16 @@ int32_t xran_bm_init (void * pHandle, uint32_t * pPoolIndex, uint32_t nNumberOfB * Pointer to XRAN layer handle for given CC * @param nPoolIndex * buffer pool identification - * @param ppVirtAddr + * @param ppData * Pointer to pointer where to store address of new buffer + * @param ppCtrl + * Pointer to pointer where to store address of internal private control information + * * * @return * 0 - on success */ -int32_t xran_bm_allocate_buffer(void * pHandle, uint32_t nPoolIndex, void **ppVirtAddr); +int32_t xran_bm_allocate_buffer(void * pHandle, uint32_t nPoolIndex, void **ppData, void **ppCtrl); /** * @ingroup xran @@ -602,13 +648,15 @@ int32_t xran_bm_allocate_buffer(void * pHandle, uint32_t nPoolIndex, void **ppVi * * @param pHandle * Pointer to XRAN layer handle for given CC - * @param pVirtAddr + * @param pData * Pointer to buffer + * @param pData + * Pointer to internal private control information * * @return * 0 - on success */ -int32_t xran_bm_free_buffer(void * pHandle, void *pVirtAddr); +int32_t xran_bm_free_buffer(void * pHandle, void *pData, void *pCtrl); /** * @ingroup xran @@ -633,9 +681,13 @@ int32_t xran_mm_destroy (void * pHandle); * Pointer to XRAN layer handle for given CC * @param pSrcBuffer * list of memory buffers to use to fetch IQs from PHY to XRAN layer (DL) + * @param pSrcCpBuffer + * list of memory buffers to use to configure C-plane (DL) * @param pDstBuffer * list of memory buffers to use to deliver IQs from XRAN layer to PHY (UL) - * @param XranTransportBlockCallbackFn pCallback + * @param pDstCpBuffer + * list of memory buffers to use to configure C-plane (UL) + * @param xran_transport_callback_fn pCallback * Callback function to call with arrival of all packets for given CC for given symbol * @param pCallbackTag * Parameters of Callback function @@ -644,11 +696,13 @@ int32_t xran_mm_destroy (void * pHandle); * 0 - on success * -1 - on error */ -int32_t xran_5g_fronthault_config (void * pHandle, - XRANBufferListStruct *pSrcBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XRANBufferListStruct *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XranTransportBlockCallbackFn pCallback, - void *pCallbackTag); + int32_t xran_5g_fronthault_config (void * pHandle, + struct xran_buffer_list *pSrcBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pSrcCpBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pDstCpBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + xran_transport_callback_fn pCallback, + void *pCallbackTag); /** * @ingroup xran @@ -659,7 +713,7 @@ int32_t xran_5g_fronthault_config (void * pHandle, * Pointer to XRAN layer handle for given CC * @param pDstBuffer * list of memory buffers to use to deliver PRACH IQs from xran layer to PHY - * @param XranTransportBlockCallbackFn pCallback + * @param xran_transport_callback_fn pCallback * Callback function to call with arrival of PRACH packets for given CC * @param pCallbackTag * Parameters of Callback function @@ -669,30 +723,9 @@ int32_t xran_5g_fronthault_config (void * pHandle, * -1 - on error */ int32_t xran_5g_prach_req (void * pHandle, - XRANBufferListStruct *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XranTransportBlockCallbackFn pCallback, + struct xran_buffer_list *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + xran_transport_callback_fn pCallback, void *pCallbackTag); -/** - * @ingroup xran - * - * Function configures phase compensation for RU via XRAN layer with given handle - * - * @param pHandle - * Pointer to XRAN layer handle for given CC - * @param nTxPhaseCps - * TX(DL) phase compensation settings - * @param nTxPhaseCps - * RX(UL) phase compensation settings - * @param nSectorId - * Sector id to use with given settings - * - * @return - * 0 - on success - */ -int32_t xran_5g_pre_compenstor_cfg(void* pHandle, - uint32_t nTxPhaseCps, - uint32_t nRxPhaseCps, - uint8_t nSectorId); /** * @ingroup xran @@ -701,13 +734,13 @@ int32_t xran_5g_pre_compenstor_cfg(void* pHandle, * * @param pHandle * Pointer to XRAN layer handle for given CC - * @param PXRANFHCONFIG pConf + * @param pointer to struct xran_fh_config pConf * Pointer to XRAN configuration structure with specific settings to use * * @return * 0 - on success */ -int32_t xran_open(void *pHandle, PXRANFHCONFIG pConf); +int32_t xran_open(void *pHandle, struct xran_fh_config* pConf); /** * @ingroup xran @@ -768,7 +801,7 @@ int32_t xran_close(void *pHandle); * 0 - in case of success * -1 - in case of failure */ -int32_t xran_reg_sym_cb(void *pHandle, XRANFHSYMPROCCB symCb, void * symCbParam, uint8_t symb, uint8_t ant); +int32_t xran_reg_sym_cb(void *pHandle, xran_callback_sym_fn symCb, void * symCbParam, uint8_t symb, uint8_t ant); /** * @ingroup xran @@ -791,7 +824,7 @@ int32_t xran_reg_sym_cb(void *pHandle, XRANFHSYMPROCCB symCb, void * symCbParam, * 0 - in case of success * -1 - in case of failure */ -int32_t xran_reg_physide_cb(void *pHandle, XRANFHTTIPROCCB Cb, void *cbParam, int skipTtiNum, enum callback_to_phy_id); +int32_t xran_reg_physide_cb(void *pHandle, xran_fh_tti_callback_fn Cb, void *cbParam, int skipTtiNum, enum callback_to_phy_id); /** * @ingroup xran @@ -815,8 +848,27 @@ int32_t xran_reg_physide_cb(void *pHandle, XRANFHTTIPROCCB Cb, void *cbParam, in */ int32_t xran_get_slot_idx (uint32_t *nFrameIdx, uint32_t *nSubframeIdx, uint32_t *nSlotIdx, uint64_t *nSecond); +/** + * @ingroup xran + * + * Function retrun XRAN layer common counters for given handle + * + * @param pHandle + * Pointer to XRAN layer handle for given CC + * + * @param pStats + * Pointer to pointer of common counter structure + * + * @return + * 0 - on success + */ +int32_t xran_get_common_counters(void *pXranLayerHandle, struct xran_common_counters *pStats); + +enum xran_if_state xran_get_if_state(void); + + #ifdef __cplusplus } #endif -#endif /* _XRAN_FH_LLS_CU_H_*/ +#endif /* _XRAN_FH_O_DU_H_*/ diff --git a/fhi_lib/lib/src/mlog_lnx_xRAN.h b/fhi_lib/lib/api/xran_mlog_lnx.h similarity index 89% rename from fhi_lib/lib/src/mlog_lnx_xRAN.h rename to fhi_lib/lib/api/xran_mlog_lnx.h index 5494045..8629009 100644 --- a/fhi_lib/lib/src/mlog_lnx_xRAN.h +++ b/fhi_lib/lib/api/xran_mlog_lnx.h @@ -16,14 +16,20 @@ * *******************************************************************************/ -#ifndef _MLOG_LNX_XRAN_H_ -#define _MLOG_LNX_XRAN_H_ + +#ifndef _XRAN_MLOG_LNX_H_ +#define _XRAN_MLOG_LNX_H_ #ifdef __cplusplus extern "C" { #endif +#ifdef MLOG_ENABLED +#include +#else + +/* stubs for MLOG functions */ #define MLOG_FALSE ( 0 ) #define MLogOpen(a, b, c, d, e) MLOG_FALSE @@ -47,9 +53,11 @@ extern "C" #define MLogAddTestCase(a, b) MLOG_FALSE #define MLogAddPowerStats(a, b, c, d, e) MLOG_FALSE +#endif /* MLOG_ENABLED */ + #ifdef __cplusplus } #endif /* #ifdef __cplusplus */ -#endif /* #ifndef _MLOG_LNX_H_ */ +#endif /* #ifndef _XRAN_MLOG_LNX_H_ */ diff --git a/fhi_lib/lib/api/xran_pkt.h b/fhi_lib/lib/api/xran_pkt.h index 112ea41..69a17f2 100644 --- a/fhi_lib/lib/api/xran_pkt.h +++ b/fhi_lib/lib/api/xran_pkt.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief Definitions and support functions to process XRAN packet * @file xran_pkt.h @@ -24,7 +23,9 @@ * @author Intel Corporation **/ -/* XRAN-FH.CUS.0-v02.01.03 xRAN Front haul Working Group Control, User and Synchronization Plane Specification */ +/* ORAN-WG4.CUS.0-v01.00 O-RAN Fronthaul Working Group + Control, User and Synchronization Plane Specification +*/ /* * Layer common to data and control packets @@ -33,11 +34,14 @@ #ifndef _XRAN_PKT_H_ #define _XRAN_PKT_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include - /** ***************************************************************************** * @file xran_pkt.h @@ -92,9 +96,9 @@ enum ecpri_msg_type *****************************************************************************/ struct ecpri_seq_id { - uint8_t seq_id:8; /**< Sequence ID */ - uint8_t sub_seq_id:7; /**< Subsequence ID */ - uint8_t e_bit:1; /**< E bit */ + uint8_t seq_id:8; /**< Sequence ID */ + uint8_t sub_seq_id:7; /**< Subsequence ID */ + uint8_t e_bit:1; /**< E bit */ } __rte_packed; @@ -106,54 +110,13 @@ struct ecpri_seq_id * Structure holds common eCPRI header as per * Table 3 1 : eCPRI Transport Header Field Definitions *****************************************************************************/ -struct xran_ecpri_hdr +struct xran_ecpri_cmn_hdr { - uint8_t ecpri_concat:1; /**< This parameter indicates when eCPRI concatenation is in use - (allowing multiple eCPRI messages in a single Ethernet payload). - NOTE: This parameter is part of the eCPRI common header. */ - uint8_t ecpri_resv:3; /**< This parameter is reserved for eCPRI future use. - NOTE: This parameter is part of the eCPRI common header. */ - uint8_t ecpri_ver:4; /**< This parameter indicates the eCPRI protocol version. - NOTE: This parameter is part of the eCPRI common header. */ - uint8_t ecpri_mesg_type:8; /**< This parameter indicates the type of service conveyed by - the message type. NOTE: This parameter is part of the eCPRI - common header. NOTE: In this version of the specification, - only values "0000 0000b" and "0000 0010b" and "0000 0101b" are used. */ - rte_be16_t ecpri_payl_size:16; /**< This parameter is the size in bytes of the payload part - of the corresponding eCPRI message. It does not include any padding bytes - following the eCPRI message. The maximum supported payload size is 216-1, - but the actual size may be further limited by the maximum payload size of - the underlying transport network. NOTE: This parameter is part of the - eCPRI common header. */ - - rte_be16_t ecpri_xtc_id; /**< 3.1.3.1.6 This parameter is a component_eAxC identifier (c_eAxC ID) and - identifies the specific data flow associated with each C-Plane (ecpriRtcid) or - U-Plane (ecpriPcid) message. It is the analog of CPRI's "AxC" (antenna-carrier) - value so is designated here as "eAxC" ("e" for "extended" to accommodate multiple - bands and multiple component carriers). In addition, the "eAxC" is divided into - "component eAxC" parts (c_eAxC) because multiple lls-CU processors may contribute - to a single eAxC and must be identified for correct data routing. */ - - struct ecpri_seq_id ecpri_seq_id; /**< This parameter provides unique message identification and ordering on - two different levels. The first octet of this parameter is the Sequence ID, which is used to identify ordering of - messages within an eAxC message stream. The Sequence ID field increments and wraps independently for each U-Plane - eAxC DL, U-Plane eAxC UL, C-Plane eAxC DL, and C-Plane eAxC UL, even if they share the same eAxC ID. - The Sequence ID is used to verify that all messages are received and also to reorder messages that are received out of order. - The second octet of this parameter is the Subsequence ID. The Subsequence ID is used to verify ordering and implement - reordering when radio-transport-level (eCPRI or IEEE-1914.3) fragmentation occurs. - Radio-transport (eCPRI or IEEE-1914.3) fragmentation is a method of splitting U-plane messages containing one or - more sections whose length exceeds the maximum packet or message length of the underlying protocol. - The Subsequence ID field consists of a 7 bit Subsequence counter and a single bit field, called E-bit. - The Subsequence number increments starting from zero for each fragment of a U-plane message. The E bit - is used to indicate the last message of the radio-transport level fragments. It is always set to zero - except for the last message of the U-plane fragment. In the case of C-plane messages radio-transport - fragmentation is not allowed, therefore the Subsequence ID shall be set to zero, and the E bit set to one. - See Section 3.1.4 for a description of the fragmentation process. - NOTE: As an alternative to radio-transport-level fragmentation, application fragmentation can be implemented. - In this case the application can take the responsibility to ensure all transport messages are not too long - (fit within the necessary transport payload size). When this "application layer fragmentation" is used, - the subsequence identifier shall always be set to "0", and the E-bit set to "1" (See Section 3.1.4). */ - + uint8_t ecpri_concat:1; /**< 3.1.3.1.3 eCPRI concatenation indicator */ + uint8_t ecpri_resv:3; /**< 3.1.3.1.2 eCPRI reserved */ + uint8_t ecpri_ver:4; /**< 3.1.3.1.1 eCPRI protocol revision, defined in XRAN_ECPRI_VER */ + uint8_t ecpri_mesg_type; /**< 3.1.3.1.4 eCPRI message type, defined in ecpri_msg_type */ + uint16_t ecpri_payl_size; /**< 3.1.3.1.5 eCPRI payload size, without common header and any padding bytes */ } __rte_packed; /** @@ -161,15 +124,16 @@ struct xran_ecpri_hdr * @ingroup xran_common_pkt * * @description - * Structure holds complete xran packet header - * 3.1.1 Ethernet Encapsulation + * Structure holds eCPRI transport header as per + * Table 3 1 : eCPRI Transport Header Field Definitions *****************************************************************************/ -struct xran_pkt_hdr +struct xran_ecpri_hdr { - struct ether_hdr eth_hdr; /**< Ethernet Header */ - struct vlan_hdr vlan_hdr; /**< VLAN Header */ - struct xran_ecpri_hdr ecpri_hdr; /**< eCPRI Transport Header */ -}; + struct xran_ecpri_cmn_hdr cmnhdr; + rte_be16_t ecpri_xtc_id; /**< 3.1.3.1.6 real time control data / IQ data transfer message series identifier */ + struct ecpri_seq_id ecpri_seq_id; /**< 3.1.3.1.7 message identifier */ +} __rte_packed; + /** ****************************************************************************** @@ -258,4 +222,22 @@ struct compression_hdr */ } __rte_packed; +/** + ****************************************************************************** + * @ingroup xran_common_pkt + * + * @description + * Structure holds common xran packet header + * 3.1.1 Ethernet Encapsulation + *****************************************************************************/ +struct xran_pkt_comm_hdr +{ + struct ether_hdr eth_hdr; /**< Ethernet Header */ + struct xran_ecpri_hdr ecpri_hdr; /**< eCPRI Transport Header */ +} __rte_packed; + +#ifdef __cplusplus +} +#endif + #endif diff --git a/fhi_lib/lib/api/xran_pkt_cp.h b/fhi_lib/lib/api/xran_pkt_cp.h index 0f0e1ba..28453b6 100644 --- a/fhi_lib/lib/api/xran_pkt_cp.h +++ b/fhi_lib/lib/api/xran_pkt_cp.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the definition of Control Plane Messages * for XRAN Front Haul layer as defined in XRAN-FH.CUS.0-v02.01. @@ -30,6 +29,10 @@ #ifndef _XRAN_PKT_CP_H_ #define _XRAN_PKT_CP_H_ +#ifdef __cplusplus +extern "C" { +#endif + /********************************************************************** * Common structures for C/U-plane @@ -85,7 +88,7 @@ struct xran_cp_radioapp_frameStructure { * Section headers definition for C-Plane. * Section type 6 and 7 are not present since those have different fields. */ -struct xran_cp_radioapp_section_header { // 8bytes, need the conversion for byte order +struct xran_cp_radioapp_section_header { /* 8bytes, need the conversion for byte order */ union { struct { uint32_t reserved:16; @@ -112,7 +115,7 @@ struct xran_cp_radioapp_section_header { // 8bytes, need the conversion for b } s5; } u; - uint32_t numPrbc:8; /**< 5.4.5.6 number of contiguous PRBs per control section */ + uint32_t numPrbc:8; /**< 5.4.5.6 number of contiguous PRBs per control section 0000 0000b = all PRBs */ uint32_t startPrbc:10; /**< 5.4.5.4 starting PRB of control section */ uint32_t symInc:1; /**< 5.4.5.3 symbol number increment command XRAN_SYMBOLNUMBER_xxxx */ uint32_t rb:1; /**< 5.4.5.2 resource block indicator, XRAN_RBIND_xxx */ @@ -120,6 +123,186 @@ struct xran_cp_radioapp_section_header { // 8bytes, need the conversion for b } __attribute__((__packed__)); +/** + * @ingroup xran_cp_pkt + * + * @description + * Beamforming Weights Extension Type(ExtType 1) defined in 5.4.7.1 + * The structure is reordered for byte order conversion. + */ +struct xran_cp_radioapp_section_ext1 { + /* variable length, need to be careful to convert byte order + * - does not need to convert first 3 bytes */ + uint8_t extType:7; /**< 5.4.6.1 extension type */ + uint8_t ef:1; /**< 5.4.6.2 extension flag */ + uint8_t extLen; /**< 5.4.6.3 extension length, in 32bits words */ + uint8_t bfwCompMeth:4; /**< 5.4.7.1.1 Beamforming weight Compression method */ + uint8_t bfwIqWidth:4; /**< 5.4.7.1.1 Beamforming weight IQ bit width */ + + /* + * would be better to use bit manipulation directly to add these parameters + * + * bfwCompParam + * (bfwI, bfwQ)+ + * ...... + * padding for 4-byte alignment + */ + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * Beamforming Attributes Extension Type(ExtType 2) defined in 5.4.7.2 + * The structure is reordered for byte order conversion. + */ +struct xran_cp_radioapp_section_ext2 { + /* variable length, need to be careful to convert byte order + * - first 4 bytes can be converted at once + */ + uint32_t bfZe3ddWidth:3; /**< 5.4.7.2.1 beamforming zenith beamwidth parameter bitwidth, Table 5-21 */ + uint32_t bfAz3ddWidth:3; /**< 5.4.7.2.1 beamforming azimuth beamwidth parameter bitwidth, Table 5-20 */ + uint32_t bfaCompResv1:2; + uint32_t bfZePtWidth:3; /**< 5.4.7.2.1 beamforming zenith pointing parameter bitwidth, Table 5-19 */ + uint32_t bfAzPtWidth:3; /**< 5.4.7.2.1 beamforming azimuth pointing parameter bitwidth, Table 5-18 */ + uint32_t bfaCompResv0:2; + uint32_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint32_t extType:7; /**< 5.4.6.1 extension type */ + uint32_t ef:1; /**< 5.4.6.2 extension flag */ + + /* + * would be better to use bit manipulation directly to add these parameters + * + * bfAzPt: var by bfAzPtWidth + * bfZePt: var by bfZePtWidth + * bfAz3dd: var by bfAz3ddWidth + * bfZe3dd: var by bfZe3ddWidth + * bfAzSI:5 (including zero-padding for unused bits) + * bfZeSI:3 + * padding for 4-byte alignment + * + */ + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * DL Precoding Extension Type(ExtType 3) for first data layer. + * Defined in 5.4.7.3 Table 5-22. + * Only be used for LTE TM2-4 and not for other LTE TMs nor NR. + * The structure is reordered for byte order conversion. Not supported. + */ +struct xran_cp_radioapp_section_ext3_first { + /* 16 bytes, need to convert byte order for two parts + * - 8 / 8 bytes + */ + uint32_t reserved1:8; + uint32_t crsSymNum:4; /**< 5.4.7.3.6 CRS symbol number indication */ + uint32_t reserved0:3; + uint32_t crsShift:1; /**< 5.4.7.3.7 CRS shift used for DL transmission */ + uint32_t crsReMask:12; /**< 5.4.7.3.5 CRS resource element mask */ + uint32_t txScheme:4; /**< 5.4.7.3.3 transmission scheme */ + uint32_t numLayers:4; /**< 5.4.7.3.4 number of layers used for DL transmission */ + uint32_t layerId:4; /**< 5.4.7.3.2 Layer ID for DL transmission */ + uint32_t codebookIndex:8; /**< 5.4.7.3.1 precoder codebook used for transmission */ + uint32_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint32_t extType:7; /**< 5.4.6.1 extension type */ + uint32_t ef:1; /**< 5.4.6.2 extension flag */ + + uint16_t beamIdAP3; /**< 5.4.7.3.10 beam id to be used for antenna port 3 */ + uint16_t beamIdAP2; /**< 5.4.7.3.9 beam id to be used for antenna port 2 */ + uint16_t beamIdAP1; /**< 5.4.7.3.8 beam id to be used for antenna port 1 */ + uint16_t reserved2; + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * DL Precoding Extension Type(ExtType 3) for non-first data layer. + * Defined in 5.4.7.3 Table 5-23. + * Only be used for LTE TM2-4 and not for other LTE TMs nor NR. + * The structure is reordered for byte order conversion. Not supported. + */ +struct xran_cp_radioapp_section_ext3_non_first { + /* 4 bytes, need to convert byte order at once */ + uint32_t numLayers:4; /**< 5.4.7.3.4 number of layers used for DL transmission */ + uint32_t layerId:4; /**< 5.4.7.3.2 Layer ID for DL transmission */ + uint32_t codebookIndex:8; /**< 5.4.7.3.1 precoder codebook used for transmission */ + + uint32_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint32_t extType:7; /**< 5.4.6.1 extension type */ + uint32_t ef:1; /**< 5.4.6.2 extension flag */ + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * Modulation Compression Parameter Extension Type(ExtType 4), 5.4.7.4 + * Only applies to section type 1 and 3. + * The structure is reordered for byte order conversion. + */ +struct xran_cp_radioapp_section_ext4 { + /* 4 bytes, need to convert byte order at once */ + uint32_t modCompScaler:15; /**< 5.4.7.4.2 modulation compression scaler value */ + uint32_t csf:1; /**< 5.4.7.4.1 constellation shift flag */ + + uint32_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint32_t extType:7; /**< 5.4.6.1 extension type */ + uint32_t ef:1; /**< 5.4.6.2 extension flag */ + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * Modulation Compression Additional Parameter Extension Type(ExtType 5) for one scaler value. + * Defined in 5.4.7.5 Table 5-26 + * Only applies to section type 1 3, and 5. + * The structure is reordered for byte order conversion. + */ +struct xran_cp_radioapp_section_ext5_1 { + /* 8 bytes, need to convert byte order at once */ + uint32_t reserved:20; + uint32_t mcScaleOffset:15; /**< 5.4.7.5.3 scaling value for modulation compression */ + uint32_t csf:1; /**< 5.4.7.5.2 constellation shift flag */ + uint32_t mcScaleReMask:12; /**< 5.4.7.5.1 modulation compression power scale RE mask */ + + uint32_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint32_t extType:7; /**< 5.4.6.1 extension type */ + uint32_t ef:1; /**< 5.4.6.2 extension flag */ + } __attribute__((__packed__)); + +/** + * @ingroup xran_cp_pkt + * + * @description + * Modulation Compression Additional Parameter Extension Type(ExtType 5) for two scaler values. + * Defined in 5.4.7.5 Table 5-27 + * Only applies to section type 1 3, and 5. + * The structure is reordered for byte order conversion. + */ +struct xran_cp_radioapp_section_ext5_2 { + /* 12 bytes, need to convert byte order for two parts respectively + * - 2 and 8 bytes, reserved1 would be OK if it is zero + */ + uint16_t extLen:8; /**< 5.4.6.3 extension length, in 32bits words */ + uint16_t extType:7; /**< 5.4.6.1 extension type */ + uint16_t ef:1; /**< 5.4.6.2 extension flag */ + + uint32_t reserved0:8; + uint32_t mcScaleOffset2:15; /**< 5.4.7.5.3 scaling value for modulation compression */ + uint32_t csf2:1; /**< 5.4.7.5.2 constellation shift flag */ + uint32_t mcScaleReMask2:12; /**< 5.4.7.5.1 modulation compression power scale RE mask */ + uint32_t mcScaleOffset1:15; /**< 5.4.7.5.3 scaling value for modulation compression */ + uint32_t csf1:1; /**< 5.4.7.5.2 constellation shift flag */ + uint32_t mcScaleReMask1:12; /**< 5.4.7.5.1 modulation compression power scale RE mask */ + + uint16_t reserved1; + } __attribute__((__packed__)); + /********************************************************** * Scheduling and Beam-forming Commands 5.4.2 **********************************************************/ @@ -283,5 +466,8 @@ struct xran_cp_radioapp_section7_header { // Payload start from here // 5.4.5.16 ~ 5.4.5.32 } __attribute__((__packed__)); +#ifdef __cplusplus +} +#endif #endif /* _XRAN_PKT_CP_H_ */ diff --git a/fhi_lib/lib/api/xran_pkt_up.h b/fhi_lib/lib/api/xran_pkt_up.h index a2399f0..588d3e1 100644 --- a/fhi_lib/lib/api/xran_pkt_up.h +++ b/fhi_lib/lib/api/xran_pkt_up.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief Definitions and support functions to process XRAN packet * @file xran_pkt_up.h @@ -37,6 +36,10 @@ #ifndef _XRAN_PKT_UP_H_ #define _XRAN_PKT_UP_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include "xran_pkt.h" #define IQ_PAIR_NUM_IN_RB 12 @@ -72,7 +75,7 @@ struct data_section_hdr { uint32_t sect_id:12; /**< 5.4.5.1 section identifier */ }; }fields; - } __attribute__((__packed__)); +} __rte_packed; /* @@ -116,7 +119,7 @@ union compression_params { uint8_t compShift:4; uint8_t compBitWidth:4; } uLaw; - } __attribute__((__packed__)); +} __rte_packed; /* @@ -133,6 +136,40 @@ struct rb_map { int16_t i_sample:IQ_BITS; /**< This parameter is the In-phase sample value */ int16_t q_sample:IQ_BITS; /**< This parameter is the Quadrature sample value */ -} __rte_packed;; +} __rte_packed; + +/** + ****************************************************************************** + * @ingroup xran_common_pkt + * + * @description + * Structure holds complete xran u-plane packet header + * 3.1.1 Ethernet Encapsulation + *****************************************************************************/ +struct xran_up_pkt_hdr +{ + struct xran_ecpri_hdr ecpri_hdr; /**< eCPRI Transport Header */ + struct radio_app_common_hdr app_hdr; /**< eCPRI Transport Header */ + struct data_section_hdr data_sec_hdr; +} __rte_packed; + + +/** + ****************************************************************************** + * @ingroup xran_common_pkt + * + * @description + * Structure holds complete ethernet and xran u-plane packet header + * 3.1.1 Ethernet Encapsulation + *****************************************************************************/ +struct eth_xran_up_pkt_hdr +{ + struct ether_hdr eth_hdr; + struct xran_up_pkt_hdr xran_hdr; +}__rte_packed; + +#ifdef __cplusplus +} +#endif #endif diff --git a/fhi_lib/lib/api/xran_sync_api.h b/fhi_lib/lib/api/xran_sync_api.h index 2e98311..3be0dec 100644 --- a/fhi_lib/lib/api/xran_sync_api.h +++ b/fhi_lib/lib/api/xran_sync_api.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides interface to synchronization related APIs (PTP/1588) * for XRAN. @@ -30,6 +29,10 @@ #ifndef _XRAN_SYNC_API_H_ #define _XRAN_SYNC_API_H_ +#ifdef __cplusplus +extern "C" { +#endif + /** * @brief Function checks if machine is synchronized using PTP for Linux * software. @@ -38,4 +41,8 @@ */ int xran_is_synchronized(void); +#ifdef __cplusplus +} +#endif + #endif /* _XRAN_SYNC_API_H_ */ diff --git a/fhi_lib/lib/api/xran_timer.h b/fhi_lib/lib/api/xran_timer.h index d44b5e0..bb4cecd 100644 --- a/fhi_lib/lib/api/xran_timer.h +++ b/fhi_lib/lib/api/xran_timer.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides interface to Timing for XRAN. * @@ -28,6 +27,11 @@ #ifndef _XRAN_TIMER_H #define _XRAN_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -40,8 +44,13 @@ long poll_next_tick(long interval_ns); long sleep_next_tick(long interval); -int timing_set_debug_stop(int value); +int timing_set_debug_stop(int value, int count); int timing_get_debug_stop(void); inline uint64_t timing_get_current_second(void); +int timing_set_numerology(uint8_t value); + +#ifdef __cplusplus +} +#endif #endif diff --git a/fhi_lib/lib/api/xran_transport.h b/fhi_lib/lib/api/xran_transport.h index 6d69c3e..bf88759 100644 --- a/fhi_lib/lib/api/xran_transport.h +++ b/fhi_lib/lib/api/xran_transport.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the definitions for Transport layer (eCPRI) API. * @@ -29,6 +28,10 @@ #ifndef _XRAN_TRANSPORT_H_ #define _XRAN_TRANSPORT_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include #include @@ -39,36 +42,37 @@ struct xran_eaxc_info { uint8_t bandSectorId; uint8_t ccId; uint8_t ruPortId; - }; +}; -/** - * @brief Compose ecpriRtcid/ecpriPcid - * - * @param CU_Port_ID CU Port ID - * @param BanbSector_ID Band Sector ID - * @param CC_ID Component Carrier ID - * @param Ant_ID RU Port ID (antenna ID) - * @return uint16_t composed ecpriRtcid/ecpriPcid - */ -uint16_t xran_compose_cid(uint8_t CU_Port_ID, uint8_t BandSector_ID, uint8_t CC_ID, uint8_t Ant_ID); +struct xran_recv_packet_info { + int ecpri_version; + enum ecpri_msg_type msg_type; + int payload_len; + struct xran_eaxc_info eaxc; + int seq_id; + int subseq_id; + int ebit; +}; -/** - * @brief Decompose ecpriRtcid/ecpriPcid - * - * @param cid composed ecpriRtcid/ecpriPcid (network byte order) - * @param result the pointer of the structure to store decomposed values - * @return none - */ -void xran_decompose_cid(uint16_t cid, struct xran_eaxc_info *result); -/** - * @brief modify the payload size of eCPRI header in xRAN packet - * - * @param mbuf Initialized rte_mbuf packet which has eCPRI header already - * @param size payload size to be updated - * @return none - */ +int xran_get_ecpri_hdr_size(void); void xran_update_ecpri_payload_size(struct rte_mbuf *mbuf, int size); +uint16_t xran_compose_cid(uint8_t CU_Port_ID, uint8_t BandSector_ID, uint8_t CC_ID, uint8_t Ant_ID); +void xran_decompose_cid(uint16_t cid, struct xran_eaxc_info *result); + +int xran_build_ecpri_hdr(struct rte_mbuf *mbuf, + uint8_t CC_ID, uint8_t Ant_ID, + uint8_t seq_id, + struct xran_ecpri_hdr **ecpri_hdr); + +int xran_parse_ecpri_hdr(struct rte_mbuf *mbuf, + struct xran_ecpri_hdr **ecpri_hdr, + struct xran_recv_packet_info *pkt_info); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/fhi_lib/lib/api/xran_up_api.h b/fhi_lib/lib/api/xran_up_api.h index 4665762..bedcce0 100644 --- a/fhi_lib/lib/api/xran_up_api.h +++ b/fhi_lib/lib/api/xran_up_api.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the definitions for User Plane Messages APIs. * @@ -29,14 +28,16 @@ #ifndef _XRAN_UP_API_H_ #define _XRAN_UP_API_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include "xran_pkt.h" #include "xran_pkt_up.h" -#define XRAN_BYTE_ORDER_SWAP - /* * structure used for storing packet parameters needed for generating * a data packet @@ -102,15 +103,26 @@ int xran_extract_iq_samples(struct rte_mbuf *mbuf, uint8_t *subframe_id, uint8_t *slot_id, uint8_t *symb_id, - struct ecpri_seq_id *seq_id); + struct 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 xran_prepare_iq_symbol_portion_no_comp( struct rte_mbuf *mbuf, const void *iq_data_start, + const enum xran_input_byte_order iq_buf_byte_order, const uint32_t iq_data_num_bytes, struct xran_up_pkt_gen_no_compression_params *params, uint8_t CC_ID, uint8_t Ant_ID, - uint8_t seq_id); + uint8_t seq_id, + uint32_t do_copy); + +#ifdef __cplusplus +} +#endif #endif /* _XRAN_UP_API_H_ */ diff --git a/fhi_lib/lib/ethernet/ethdi.c b/fhi_lib/lib/ethernet/ethdi.c index 3664ecc..3f919b4 100644 --- a/fhi_lib/lib/ethernet/ethdi.c +++ b/fhi_lib/lib/ethernet/ethdi.c @@ -24,6 +24,7 @@ * @author Intel Corporation **/ +#define _GNU_SOURCE #include #include #include @@ -63,14 +64,14 @@ #include "ethernet.h" #include "ethdi.h" -#ifndef MLOG_ENABLED -#include "../src/mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif +#include "xran_fh_o_du.h" +#include "xran_mlog_lnx.h" +#include "xran_printf.h" #include "../src/xran_lib_mlog_tasks_id.h" +#define BURST_RX_IO_SIZE 48 + struct xran_ethdi_ctx g_ethdi_ctx = { 0 }; enum xran_if_state xran_if_current_state = XRAN_STOPPED; @@ -102,13 +103,13 @@ int xran_ethdi_mbuf_send_cp(struct rte_mbuf *mb, uint16_t ethertype) res = xran_enqueue_mbuf(mb, ctx->tx_ring[ETHDI_CP_VF]); return res; } - +#if 0 void xran_ethdi_stop_tx() { struct xran_ethdi_ctx *const ctx = xran_ethdi_get_ctx(); rte_timer_stop_sync(&ctx->timer_tx); } - +#endif struct { uint16_t ethertype; @@ -200,22 +201,124 @@ int xran_ethdi_filter_packet(struct rte_mbuf *pkt, uint64_t rx_time) return xran_handle_ether(rte_be_to_cpu_16(eth_hdr->ether_type), pkt, rx_time); } +#if 0 +//------------------------------------------------------------------------------------------- +/** @ingroup xran + * + * @param[in] port - DPDK ETH port id + * + * @return void + * + * @description + * Prints statistics of usage of DPDK port + * +**/ +//------------------------------------------------------------------------------------------- +void xran_ethdi_ports_stats(void) +{ + struct rte_eth_stats stats; + struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx(); + int32_t i = 0; + + for(i = 0; i < ETHDI_VF_MAX; i++){ + /* Get stats (extended stats includes common stats) */ + rte_eth_stats_get(ctx->io_cfg.port[i], &stats); + printf("DPDK stats:\n"); + printf("** Port %hhu **\n", ctx->io_cfg.port[i]); + printf("ierrors:\t%lu\n", stats.ierrors); + printf("oerrors:\t%lu\n", stats.oerrors); + printf("ipackets:\t%lu\n", stats.ipackets); + printf("opackets:\t%lu\n", stats.opackets); + printf("imissed:\t%lu\n", stats.imissed); + printf("rx_nombuf:\t%lu\n", stats.rx_nombuf); + } + return ; +} +#endif +/* Check the link status of all ports in up to 9s, and print them finally */ +static void check_port_link_status(uint8_t portid) +{ +#define CHECK_INTERVAL 100 /* 100ms */ +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ + uint8_t count, all_ports_up, print_flag = 0; + struct rte_eth_link link; + + printf("\nChecking link status"); + fflush(stdout); + for (count = 0; count <= MAX_CHECK_TIME; count++) { + all_ports_up = 1; + memset(&link, 0, sizeof(link)); + rte_eth_link_get_nowait(portid, &link); + + /* print link status if flag set */ + if (print_flag == 1) { + if (link.link_status) + printf("Port %d Link Up - speed %u " + "Mbps - %s\n", (uint8_t)portid, + (unsigned)link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? + ("full-duplex") : ("half-duplex\n")); + else + printf("Port %d Link Down\n", + (uint8_t)portid); + } + /* clear all_ports_up flag if any link down */ + if (link.link_status == ETH_LINK_DOWN) { + all_ports_up = 0; + break; + } + /* after finally printing all link status, get out */ + if (print_flag == 1) + break; + + if (all_ports_up == 0) { + printf("."); + fflush(stdout); + rte_delay_ms(CHECK_INTERVAL); + } + /* set the print_flag if all ports up or timeout */ + if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { + print_flag = 1; + printf(" ... done\n"); + } + } +} int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, int *lcore_id, struct ether_addr *p_lls_cu_addr, struct ether_addr *p_ru_addr, uint16_t cp_vlan, uint16_t up_vlan) { - uint16_t port[2] = {0, 0}; + uint16_t port[2] = {0xffff, 0xffff}; struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx(); int i; char core_mask[20]; - char *argv[] = { name, core_mask, "-m3072", "--proc-type=auto", - "--file-prefix", name, "-w", "0000:00:00.0" }; + char bbdev_wdev[32] = ""; + char bbdev_vdev[32] = ""; + + char *argv[] = { name, "-c ffffffff", "-n2", "--socket-mem=8192", "--proc-type=auto", + "--file-prefix", name, "-w", "0000:00:00.0", bbdev_wdev, bbdev_vdev}; if (io_cfg == NULL) return 0; + if(io_cfg->bbdev_mode != XRAN_BBDEV_NOT_USED){ + printf("BBDEV_FEC_ACCL_NR5G\n"); + if (io_cfg->bbdev_mode == XRAN_BBDEV_MODE_HW_ON){ + // hw-accelerated bbdev + printf("hw-accelerated bbdev %s\n", io_cfg->bbdev_dev[0]); + sprintf(bbdev_wdev, "-w %s", io_cfg->bbdev_dev[0]); + } else if (io_cfg->bbdev_mode == XRAN_BBDEV_MODE_HW_OFF){ + // hw-accelerated bbdev disable + if(io_cfg->bbdev_dev[0]){ + printf("hw-accelerated bbdev disable %s\n", io_cfg->bbdev_dev[0]); + sprintf(bbdev_wdev, "-b %s", io_cfg->bbdev_dev[0]); + } + sprintf(bbdev_wdev, "%s", "--vdev=baseband_turbo_sw"); + } else { + rte_panic("Cannot init DPDK incorrect [bbdev_mode %d]\n", io_cfg->bbdev_mode); + } + } snprintf(core_mask, sizeof(core_mask), "-c%x", (1 << io_cfg->core) | @@ -234,6 +337,14 @@ int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, for (i = 0; i <= ID_BROADCAST; i++) /* Initialize all as broadcast */ memset(&ctx->entities[i], 0xFF, sizeof(ctx->entities[0])); + printf("%s: Calling rte_eal_init:", __FUNCTION__); + for (i = 0; i < RTE_DIM(argv); i++) + { + printf("%s ", argv[i]); + } + printf("\n"); + + /* This will return on system_core, which is not necessarily the * one we're on right now. */ if (rte_eal_init(RTE_DIM(argv), argv) < 0) @@ -241,6 +352,7 @@ int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, xran_init_mbuf_pool(); + /* Timers. */ rte_timer_subsystem_init(); rte_timer_init(&ctx->timer_ping); @@ -253,10 +365,14 @@ int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, if (rte_eal_process_type() == RTE_PROC_PRIMARY) { for (i = 0; i < ETHDI_VF_MAX; i ++){ - if (rte_eth_dev_attach(io_cfg->dpdk_dev[i], &port[i]) != 0 || - rte_eth_dev_count_avail() == 0) - errx(1, "Network port doesn't exist."); - xran_init_port(port[i], p_lls_cu_addr); /* we only have 1 port at this stage */ + if(io_cfg->dpdk_dev[i]){ + if (rte_eth_dev_attach(io_cfg->dpdk_dev[i], &port[i]) != 0 || + rte_eth_dev_count_avail() == 0) + errx(1, "Network port doesn't exist."); + xran_init_port(port[i], p_lls_cu_addr); + } else { + printf("no DPDK port provided\n"); + } if(i==0){ ctx->tx_ring[i] = rte_ring_create("tx_ring_up", NUM_MBUFS, rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ); @@ -272,6 +388,8 @@ int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, ctx->pkt_dump_ring[i] = rte_ring_create("pkt_dump_ring_cp", NUM_MBUFS, rte_lcore_to_socket_id(*lcore_id), RING_F_SC_DEQ); } + if(io_cfg->dpdk_dev[i]) + check_port_link_status(port[i]); } } else { rte_panic("ethdi_dpdk_io_loop() failed to start with RTE_PROC_SECONDARY\n"); @@ -279,15 +397,15 @@ int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, PANIC_ON(ctx->tx_ring == NULL, "failed to allocate tx ring"); PANIC_ON(ctx->rx_ring == NULL, "failed to allocate rx ring"); PANIC_ON(ctx->pkt_dump_ring == NULL, "failed to allocate pkt dumping ring"); - for (i = 0; i < ETHDI_VF_MAX; i++) + for (i = 0; i < ETHDI_VF_MAX; i++){ ctx->io_cfg.port[i] = port[i]; + print_dbg("port_id 0x%04x\n", ctx->io_cfg.port[i]); + } - rte_eth_macaddr_get(port[ETHDI_UP_VF], &ctx->entities[io_cfg->id]); - ether_addr_copy(p_ru_addr, &ctx->entities[ID_RU]); - - /* Start the actual IO thread */ - if (rte_eal_remote_launch(xran_ethdi_dpdk_io_loop, &ctx->io_cfg, *lcore_id)) - rte_panic("ethdi_dpdk_io_loop() failed to start\n"); + if(io_cfg->dpdk_dev[ETHDI_UP_VF]){ + rte_eth_macaddr_get(port[ETHDI_UP_VF], &ctx->entities[io_cfg->id]); + ether_addr_copy(p_ru_addr, &ctx->entities[ID_RU]); + } return 1; } @@ -315,7 +433,66 @@ static inline uint16_t xran_tx_from_ring(int port, struct rte_ring *r) } } +int32_t process_dpdk_io(void) +{ + struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx(); + const struct xran_io_loop_cfg *const cfg = &(xran_ethdi_get_ctx()->io_cfg); + const int port[ETHDI_VF_MAX] = {cfg->port[ETHDI_UP_VF], cfg->port[ETHDI_CP_VF]}; + int port_id = 0; + + for (port_id = 0; port_id < ETHDI_VF_MAX; port_id++){ + struct rte_mbuf *mbufs[BURST_RX_IO_SIZE]; + if(port[port_id] == 0xFF); + /* RX */ + const uint16_t rxed = rte_eth_rx_burst(port[port_id], 0, mbufs, BURST_RX_IO_SIZE); + if (rxed != 0){ + unsigned enq_n = 0; + long t1 = MLogTick(); + enq_n = rte_ring_enqueue_burst(ctx->rx_ring[port_id], (void*)mbufs, rxed, NULL); + if(rxed - enq_n) + rte_panic("error enq\n"); + MLogTask(PID_RADIO_RX_VALIDATE, t1, MLogTick()); + } + + /* TX */ + const uint16_t sent = xran_tx_from_ring(port[port_id], ctx->tx_ring[port_id]); + + if (XRAN_STOPPED == xran_if_current_state) + return -1; + } + + if (XRAN_STOPPED == xran_if_current_state) + return -1; + + return 0; +} +#if 0 +static inline void xran_process_rx_burst(struct rte_mbuf *mbufs[], uint16_t n_mbufs, + uint64_t rx_time) +{ + int i; + + if (!n_mbufs) + return; + + for (i = 0; i < n_mbufs; ++i) + { + if (xran_ethdi_filter_packet(mbufs[i], rx_time) == MBUF_FREE) + rte_pktmbuf_free(mbufs[i]); + } + +#ifdef DPDKIO_LATENCY_DEBUG + struct timeval tv_now, tv_diff; + + gettimeofday(&tv_now, NULL); + if (n_mbufs > 1) + nlog("Warning - received %d mbufs in a row", n_mbufs); + + timersub(&tv_now, &rx_time, &tv_diff); + nlog("rx processing took %d usec", tv_diff.tv_usec); +#endif +} /* * This is the main DPDK-IO loop. @@ -324,53 +501,30 @@ static inline uint16_t xran_tx_from_ring(int port, struct rte_ring *r) */ int xran_ethdi_dpdk_io_loop(void *io_loop_cfg) { - struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx(); - const struct xran_io_loop_cfg *const cfg = io_loop_cfg; - const int port[ETHDI_VF_MAX] = {cfg->port[ETHDI_UP_VF], cfg->port[ETHDI_CP_VF]}; - int port_id = 0; struct sched_param sched_param; int res = 0; + struct xran_ethdi_ctx *ctx = xran_ethdi_get_ctx(); + const struct xran_io_loop_cfg *const cfg = &(xran_ethdi_get_ctx()->io_cfg); + const int port[ETHDI_VF_MAX] = {cfg->port[ETHDI_UP_VF], cfg->port[ETHDI_CP_VF]}; printf("%s [PORT: %d %d] [CPU %2d] [PID: %6d]\n", __FUNCTION__, port[ETHDI_UP_VF], port[ETHDI_CP_VF] , rte_lcore_id(), getpid()); printf("%s [CPU %2d] [PID: %6d]\n", __FUNCTION__, rte_lcore_id(), getpid()); sched_param.sched_priority = XRAN_THREAD_DEFAULT_PRIO; - if ((res = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param))) - { + if ((res = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param))) { printf("priority is not changed: coreId = %d, result1 = %d\n",rte_lcore_id(), res); } - for (;;) { - for (port_id = 0; port_id < ETHDI_VF_MAX; port_id++){ - struct rte_mbuf *mbufs[BURST_SIZE]; - /* RX */ - const uint16_t rxed = rte_eth_rx_burst(port[port_id], 0, mbufs, BURST_SIZE); - if (rxed != 0){ - long t1 = MLogTick(); - rte_ring_enqueue_burst(ctx->rx_ring[port_id], (void*)mbufs, rxed, NULL); - MLogTask(PID_RADIO_RX_VALIDATE, t1, MLogTick()); - } - - /* TX */ - const uint16_t sent = xran_tx_from_ring(port[port_id], ctx->tx_ring[port_id]); - if (rxed | sent) - continue; /* more packets might be waiting in queues */ - - rte_pause(); /* short pause, optimize memory access */ - if (XRAN_STOPPED == xran_if_current_state) - break; - } - - if (XRAN_STOPPED == xran_if_current_state) - break; + for (;;){ + if(process_dpdk_io()!=0) + break; } fflush(stderr); fflush(stdout); puts("IO loop finished"); - //for (port_id = 0; port_id < ETHDI_VF_MAX; port_id++) - // xran_ethdi_port_stats(port[port_id]); - return 0; } +#endif + diff --git a/fhi_lib/lib/ethernet/ethdi.h b/fhi_lib/lib/ethernet/ethdi.h index 3b00e15..e258ac0 100644 --- a/fhi_lib/lib/ethernet/ethdi.h +++ b/fhi_lib/lib/ethernet/ethdi.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file has all definitions for the Ethernet Data Interface Layer * @file ethdi.h @@ -37,6 +36,7 @@ extern "C" { #include #include "ethernet.h" +#include "xran_fh_o_du.h" #define XRAN_THREAD_DEFAULT_PRIO (98) @@ -53,14 +53,6 @@ extern "C" { #define TX_TIMER_INTERVAL ((rte_get_timer_hz() / 1000000000L)*interval_us*1000) /* nanosec */ #define TX_RX_LOOP_TIME rte_get_timer_hz() / 1 -extern enum xran_if_state xran_if_current_state; - -enum xran_if_state -{ - XRAN_RUNNING, - XRAN_STOPPED -}; - enum xran_ping_states { PING_IDLE, @@ -79,6 +71,8 @@ struct xran_io_loop_cfg { uint8_t id; char *dpdk_dev[ETHDI_VF_MAX]; + char *bbdev_dev[1]; + int bbdev_mode; int core; int system_core; /* Needed as DPDK will change your starting core. */ int pkt_proc_core; /* Needed for packet processing thread. */ @@ -132,6 +126,7 @@ enum { MBUF_FREE }; +extern enum xran_if_state xran_if_current_state; extern uint8_t ping_dst_id; extern struct ether_addr entities_addrs[]; @@ -154,12 +149,16 @@ int xran_register_ethertype_handler(uint16_t ethertype, ethertype_handler callba int xran_ethdi_init_dpdk_io(char *name, const struct xran_io_loop_cfg *io_cfg, int *lcore_id, struct ether_addr *p_lls_cu_addr, struct ether_addr *p_ru_addr, uint16_t cp_vlan, uint16_t up_vlan); -int xran_ethdi_dpdk_io_loop(void *); struct rte_mbuf *xran_ethdi_mbuf_alloc(void); int xran_ethdi_mbuf_send(struct rte_mbuf *mb, uint16_t ethertype); int xran_ethdi_mbuf_send_cp(struct rte_mbuf *mb, uint16_t ethertype); +#if 0 void xran_ethdi_stop_tx(void); +void xran_ethdi_ports_stats(void); +int xran_ethdi_dpdk_io_loop(void *); +#endif int xran_ethdi_filter_packet(struct rte_mbuf *pkt, uint64_t rx_time); +int32_t process_dpdk_io(void); #ifdef __cplusplus diff --git a/fhi_lib/lib/ethernet/ethernet.c b/fhi_lib/lib/ethernet/ethernet.c index ebf997c..8a298b1 100644 --- a/fhi_lib/lib/ethernet/ethernet.c +++ b/fhi_lib/lib/ethernet/ethernet.c @@ -68,10 +68,15 @@ #include "ethdi.h" /* Our mbuf pools. */ -struct rte_mempool *_eth_mbuf_pool; -struct rte_mempool *_eth_mbuf_pool_rx; -struct rte_mempool *_eth_mbuf_pool_small; -struct rte_mempool *_eth_mbuf_pool_big; +struct rte_mempool *_eth_mbuf_pool = NULL; +struct rte_mempool *_eth_mbuf_pool_inderect = NULL; +struct rte_mempool *_eth_mbuf_pool_rx = NULL; +struct rte_mempool *_eth_mbuf_pool_small = NULL; +struct rte_mempool *_eth_mbuf_pool_big = NULL; + +struct rte_mempool *socket_direct_pool = NULL; +struct rte_mempool *socket_indirect_pool = NULL; + /* * Make sure the ring indexes are big enough to cover buf space x2 @@ -90,12 +95,66 @@ static struct { #define RINGSIZE sizeof(io_ring.buf) #define RINGMASK (RINGSIZE - 1) +int __xran_delayed_msg(const char *fmt, ...) +{ +#if 0 + va_list ap; + int msg_len; + char localbuf[RINGSIZE]; + ring_idx old_head, new_head; + ring_idx copy_len; + + /* first prep a copy of the message on the local stack */ + va_start(ap, fmt); + msg_len = vsnprintf(localbuf, RINGSIZE, fmt, ap); + va_end(ap); + + /* atomically reserve space in the ring */ + for (;;) { + old_head = io_ring.head; /* snapshot head */ + /* free always within range of [0, RINGSIZE] - proof by induction */ + const ring_idx free = RINGSIZE - (old_head - io_ring.tail); + + copy_len = RTE_MIN(msg_len, free); + if (copy_len <= 0) + return 0; /* vsnprintf error or ringbuff full. Drop log. */ + + new_head = old_head + copy_len; + RTE_ASSERT((ring_idx)(new_head - io_ring.tail) <= RINGSIZE); + + if (likely(__atomic_compare_exchange_n(&io_ring.head, &old_head, + new_head, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))) + break; + } + + /* Now copy data in at ease. */ + const int copy_start = (old_head & RINGMASK); + if (copy_start < (new_head & RINGMASK)) /* no wrap */ + memcpy(io_ring.buf + copy_start, localbuf, copy_len); + else { /* wrap-around */ + const int chunk_len = RINGSIZE - copy_start; + + memcpy(io_ring.buf + copy_start, localbuf, chunk_len); + memcpy(io_ring.buf, localbuf + chunk_len, copy_len - chunk_len); + } + + /* wait for previous writes to complete before updating read_head. */ + while (io_ring.read_head != old_head) + rte_pause(); + io_ring.read_head = new_head; + + + return copy_len; + #endif + return 0; +} /* * Display part of the message stored in the ring buffer. * Might require multiple calls to print the full message. * Will return 0 when nothing left to print. */ +#if 0 int xran_show_delayed_message(void) { ring_idx tail = io_ring.tail; @@ -122,7 +181,7 @@ int xran_show_delayed_message(void) return written; /* next invocation will print the rest if any */ } - +#endif void xran_init_mbuf_pool(void) { @@ -130,6 +189,10 @@ void xran_init_mbuf_pool(void) if (rte_eal_process_type() == RTE_PROC_PRIMARY) { _eth_mbuf_pool = rte_pktmbuf_pool_create("mempool", NUM_MBUFS, MBUF_CACHE, 0, MBUF_POOL_ELEMENT, rte_socket_id()); +#ifdef XRAN_ATTACH_MBUF + _eth_mbuf_pool_inderect = rte_pktmbuf_pool_create("mempool_indirect", NUM_MBUFS, + MBUF_CACHE, 0, MBUF_POOL_ELEMENT, rte_socket_id());*/ +#endif _eth_mbuf_pool_rx = rte_pktmbuf_pool_create("mempool_rx", NUM_MBUFS, MBUF_CACHE, 0, MBUF_POOL_ELEMENT, rte_socket_id()); _eth_mbuf_pool_small = rte_pktmbuf_pool_create("mempool_small", @@ -138,25 +201,37 @@ void xran_init_mbuf_pool(void) NUM_MBUFS_BIG, 0, 0, MBUF_POOL_ELM_BIG, rte_socket_id()); } else { _eth_mbuf_pool = rte_mempool_lookup("mempool"); + _eth_mbuf_pool_inderect = rte_mempool_lookup("mempool_indirect"); _eth_mbuf_pool_rx = rte_mempool_lookup("mempool_rx"); _eth_mbuf_pool_small = rte_mempool_lookup("mempool_small"); _eth_mbuf_pool_big = rte_mempool_lookup("mempool_big"); } if (_eth_mbuf_pool == NULL) rte_panic("Cannot create mbuf pool: %s\n", rte_strerror(rte_errno)); +#ifdef XRAN_ATTACH_MBUF + if (_eth_mbuf_pool_inderect == NULL) + rte_panic("Cannot create mbuf pool: %s\n", rte_strerror(rte_errno)); +#endif if (_eth_mbuf_pool_rx == NULL) rte_panic("Cannot create mbuf pool: %s\n", rte_strerror(rte_errno)); if (_eth_mbuf_pool_small == NULL) rte_panic("Cannot create small mbuf pool: %s\n", rte_strerror(rte_errno)); if (_eth_mbuf_pool_big == NULL) rte_panic("Cannot create big mbuf pool: %s\n", rte_strerror(rte_errno)); + + if (socket_direct_pool == NULL) + socket_direct_pool = _eth_mbuf_pool; + + if (socket_indirect_pool == NULL) + socket_indirect_pool = _eth_mbuf_pool_inderect; } /* Init NIC port, then start the port */ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) { - char buf[ETHER_ADDR_FMT_SIZE]; - struct ether_addr eth_addr; + static uint16_t nb_rxd = BURST_SIZE; + static uint16_t nb_txd = BURST_SIZE; + struct ether_addr addr; struct rte_eth_rxmode rxmode = { .split_hdr_size = 0, .max_rx_pkt_len = MAX_RX_LEN, @@ -169,8 +244,6 @@ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) .rxmode = rxmode, .txmode = txmode }; - struct ether_addr pDstEthAddr; - struct rte_eth_rxconf rxq_conf; struct rte_eth_txconf txq_conf; @@ -179,35 +252,13 @@ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) const char *drv_name = ""; int sock_id = rte_eth_dev_socket_id(p_id); - // ether_format_addr(buf, sizeof(buf), p_lls_cu_addr); - // printf("port %d set mac address %s\n", p_id, buf); - // rte_eth_dev_default_mac_addr_set(p_id, p_lls_cu_addr); - rte_eth_dev_info_get(p_id, &dev_info); if (dev_info.driver_name) drv_name = dev_info.driver_name; printf("initializing port %d for TX, drv=%s\n", p_id, drv_name); - /* In order to receive packets from any server need to add broad case address - * for the port*/ - pDstEthAddr.addr_bytes[0] = 0xFF; - pDstEthAddr.addr_bytes[1] = 0xFF; - pDstEthAddr.addr_bytes[2] = 0xFF; - - pDstEthAddr.addr_bytes[3] = 0xFF; - pDstEthAddr.addr_bytes[4] = 0xFF; - pDstEthAddr.addr_bytes[5] = 0xFF; - - rte_eth_macaddr_get(p_id, ð_addr); - ether_format_addr(buf, sizeof(buf), ð_addr); - printf("port %d mac address %s\n", p_id, buf); - - struct ether_addr addr; rte_eth_macaddr_get(p_id, &addr); -// rte_eth_dev_mac_addr_add(p_id, &pDstEthAddr,1); - // rte_eth_dev_mac_addr_add(p_id, &addr, 1); - printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", (unsigned)p_id, @@ -219,9 +270,18 @@ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) if (ret < 0) rte_panic("Cannot configure port %u (%d)\n", p_id, ret); + ret = rte_eth_dev_adjust_nb_rx_tx_desc(p_id, &nb_rxd,&nb_txd); + + if (ret < 0) { + printf("\n"); + rte_exit(EXIT_FAILURE, "Cannot adjust number of " + "descriptors: err=%d, port=%d\n", ret, p_id); + } + printf("Port %u: nb_rxd %d nb_txd %d\n", p_id, nb_rxd, nb_txd); + /* Init RX queues */ rxq_conf = dev_info.default_rxconf; - ret = rte_eth_rx_queue_setup(p_id, 0, BURST_SIZE, + ret = rte_eth_rx_queue_setup(p_id, 0, nb_rxd, sock_id, &rxq_conf, _eth_mbuf_pool_rx); if (ret < 0) rte_panic("Cannot init RX for port %u (%d)\n", @@ -229,7 +289,7 @@ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) /* Init TX queues */ txq_conf = dev_info.default_txconf; - ret = rte_eth_tx_queue_setup(p_id, 0, BURST_SIZE, sock_id, &txq_conf); + ret = rte_eth_tx_queue_setup(p_id, 0, nb_txd, sock_id, &txq_conf); if (ret < 0) rte_panic("Cannot init TX for port %u (%d)\n", p_id, ret); @@ -239,9 +299,10 @@ void xran_init_port(int p_id, struct ether_addr *p_lls_cu_addr) if (ret < 0) rte_panic("Cannot start port %u (%d)\n", p_id, ret); - rte_eth_promiscuous_enable(p_id); +// rte_eth_promiscuous_enable(p_id); } +#if 0 void xran_memdump(void *addr, int len) { int i; @@ -260,9 +321,8 @@ void xran_memdump(void *addr, int len) #endif } - /* Prepend ethernet header, possibly vlan tag. */ -void xran_add_eth_hdr_vlan(struct ether_addr *dst, uint16_t ethertype, struct rte_mbuf *mb, uint16_t vlan_tci) +void xran_add_eth_hdr(struct ether_addr *dst, uint16_t ethertype, struct rte_mbuf *mb) { /* add in the ethernet header */ struct ether_hdr *const h = (void *)rte_pktmbuf_prepend(mb, sizeof(*h)); @@ -286,12 +346,25 @@ void xran_add_eth_hdr_vlan(struct ether_addr *dst, uint16_t ethertype, struct rt } #endif #ifdef VLAN_SUPPORT - mb->vlan_tci = vlan_tci; - dlog("Inserting vlan tag of %d", vlan_tci); + mb->vlan_tci = FLEXRAN_UP_VLAN_TAG; + dlog("Inserting vlan tag of %d", FLEXRAN_UP_VLAN_TAG); rte_vlan_insert(&mb); #endif } +int xran_send_mbuf(struct ether_addr *dst, struct rte_mbuf *mb) +{ + xran_add_eth_hdr(dst, ETHER_TYPE_ETHDI, mb); + + if (rte_eth_tx_burst(mb->port, 0, &mb, 1) == 1) + return 1; + + elog("packet sending failed on port %d", mb->port); + rte_pktmbuf_free(mb); + + return 0; /* fail */ +} + int xran_send_message_burst(int dst_id, int pkt_type, void *body, int len) { struct rte_mbuf *mbufs[BURST_SIZE]; @@ -355,3 +428,38 @@ int xran_send_message_burst(int dst_id, int pkt_type, void *body, int len) return 1; } + +#endif + +/* Prepend ethernet header, possibly vlan tag. */ +void xran_add_eth_hdr_vlan(struct ether_addr *dst, uint16_t ethertype, struct rte_mbuf *mb, uint16_t vlan_tci) +{ + /* add in the ethernet header */ + struct ether_hdr *h = (struct ether_hdr *)rte_pktmbuf_mtod(mb, struct ether_hdr*); + + PANIC_ON(h == NULL, "mbuf prepend of ether_hdr failed"); + + /* Fill in the ethernet header. */ + rte_eth_macaddr_get(mb->port, &h->s_addr); /* set source addr */ + h->d_addr = *dst; /* set dst addr */ + h->ether_type = rte_cpu_to_be_16(ethertype); /* ethertype too */ + +#if defined(DPDKIO_DEBUG) && DPDKIO_DEBUG > 1 + { + char dst[ETHER_ADDR_FMT_SIZE] = "(empty)"; + char src[ETHER_ADDR_FMT_SIZE] = "(empty)"; + + nlog("*** packet for TX below (len %d) ***", rte_pktmbuf_pkt_len(mb)); + ether_format_addr(src, sizeof(src), &h->s_addr); + ether_format_addr(dst, sizeof(dst), &h->d_addr); + nlog("src: %s dst: %s ethertype: %.4X", src, dst, ethertype); + } +#endif +#ifdef VLAN_SUPPORT + mb->vlan_tci = vlan_tci; + dlog("Inserting vlan tag of %d", vlan_tci); + rte_vlan_insert(&mb); +#endif +} + + diff --git a/fhi_lib/lib/ethernet/ethernet.h b/fhi_lib/lib/ethernet/ethernet.h index b22aed8..f3efe7b 100644 --- a/fhi_lib/lib/ethernet/ethernet.h +++ b/fhi_lib/lib/ethernet/ethernet.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file has all definitions for the Ethernet Data Interface Layer * @file ethernet.h @@ -35,7 +34,7 @@ extern "C" { #include #include -#define BURST_SIZE 64 +#define BURST_SIZE 4096 //#define VLAN_SUPPORT #define FLEXRAN_UP_VLAN_TAG 2 @@ -44,7 +43,7 @@ extern "C" { #define ETHER_TYPE_SYNC 0xBEFE #define ETHER_TYPE_START_TX 0xCEFE -#define NUM_MBUFS 262144 +#define NUM_MBUFS 65536 #define MBUF_CACHE 256 #define MBUF_POOL_ELM_SMALL 1500 /* regular ethernet MTU, most compatible */ @@ -64,6 +63,8 @@ extern "C" { extern struct rte_mempool *_eth_mbuf_pool; extern struct rte_mempool *_eth_mbuf_pool_small; extern struct rte_mempool *_eth_mbuf_pool_big; +extern struct rte_mempool *socket_direct_pool; +extern struct rte_mempool *socket_indirect_pool; /* Do NOT change the order of this enum and below * - need to be in sync with the table of handlers in testue.c */ @@ -121,23 +122,25 @@ void xran_init_mbuf_pool(void); void xran_init_port(int port, struct ether_addr *p_lls_cu_addr); void xran_add_eth_hdr_vlan(struct ether_addr *dst, uint16_t ethertype, struct rte_mbuf *mb, uint16_t vlan_tci); -int xran_send_mbuf(struct ether_addr *dst, struct rte_mbuf *mb); - -int xran_send_message_burst(int dst_id, int pkt_type, void *body, int len); +#if 0 void xran_memdump(void *addr, int len); - +void xran_add_eth_hdr(struct ether_addr *dst, uint16_t ethertype, struct rte_mbuf *); +int xran_send_mbuf(struct ether_addr *dst, struct rte_mbuf *mb); +int xran_send_message_burst(int dst_id, int pkt_type, void *body, int len); +int xran_show_delayed_message(void); +#endif /* * Print a message after all critical processing done. * Mt-safe. 4 variants - normal, warning, error and debug log. */ - -#define nlog(m, ...) -#define delayed_message /* this is the old alias for this function */ -#define wlog(m, ...) -#define elog(m, ...) +int __xran_delayed_msg(const char *fmt, ...); +#define nlog(m, ...) __xran_delayed_msg("%s(): " m "\n", __FUNCTION__, ##__VA_ARGS__) +#define delayed_message nlog /* this is the old alias for this function */ +#define wlog(m, ...) nlog("WARNING: " m, ##__VA_ARGS__) +#define elog(m, ...) nlog("ERROR: " m, ##__VA_ARGS__) #ifdef DEBUG -# define dlog(m, ...) +# define dlog(m, ...) nlog("DEBUG: " m, ##__VA_ARGS__) #else # define dlog(m, ...) #endif diff --git a/fhi_lib/lib/src/xran_app_frag.c b/fhi_lib/lib/src/xran_app_frag.c new file mode 100644 index 0000000..5b1dc47 --- /dev/null +++ b/fhi_lib/lib/src/xran_app_frag.c @@ -0,0 +1,259 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +/** + * @brief xRAN application frgamentation for U-plane packets + * + * @file xran_app_frag.c + * @ingroup group_source_xran + * @author Intel Corporation + **/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "xran_app_frag.h" +#include "xran_cp_api.h" +#include "xran_pkt_up.h" +#include "xran_printf.h" +#include "xran_common.h" + +/* Fragment alignment */ +#define XRAN_PAYLOAD_RB_ALIGN (N_SC_PER_PRB*(IQ_BITS/8)*2) /**< at least 12*4=48 bytes per one RB */ + +static inline void __fill_xranhdr_frag(struct xran_up_pkt_hdr *dst, + const struct xran_up_pkt_hdr *src, uint16_t rblen_bytes, + uint16_t rboff_bytes, struct xran_section_info *sectinfo, uint32_t mf, uint8_t *seqid) +{ + struct data_section_hdr loc_data_sec_hdr; + struct xran_ecpri_hdr loc_ecpri_hdr; + + rte_memcpy(dst, src, sizeof(*dst)); + + if(dst->ecpri_hdr.ecpri_seq_id.seq_id != *seqid - 1){ + /* not first fragment, incease seq id */ + dst->ecpri_hdr.ecpri_seq_id.seq_id = *seqid++; + } + + loc_data_sec_hdr.fields.all_bits = rte_be_to_cpu_32(dst->data_sec_hdr.fields.all_bits); + + /* update RBs */ + loc_data_sec_hdr.fields.start_prbu = sectinfo->startPrbc + rboff_bytes/(N_SC_PER_PRB*(IQ_BITS/8*2)); + loc_data_sec_hdr.fields.num_prbu = rblen_bytes/(N_SC_PER_PRB*(IQ_BITS/8*2)); + + print_dbg("sec [%d %d] pkt [%d %d] rboff_bytes %d rblen_bytes %d\n",sectinfo->startPrbc, sectinfo->numPrbc, loc_data_sec_hdr.fields.start_prbu, loc_data_sec_hdr.fields.num_prbu, + rboff_bytes, rblen_bytes); + + dst->data_sec_hdr.fields.all_bits = rte_cpu_to_be_32(loc_data_sec_hdr.fields.all_bits); + + /* update length */ + dst->ecpri_hdr.cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(sizeof(struct radio_app_common_hdr) + + sizeof(struct data_section_hdr) + rblen_bytes + xran_get_ecpri_hdr_size()); +} + + +static inline void __free_fragments(struct rte_mbuf *mb[], uint32_t num) +{ + uint32_t i; + for (i = 0; i != num; i++) + rte_pktmbuf_free(mb[i]); +} + +/** + * XRAN fragmentation. + * + * This function implements the application fragmentation of XRAN packets. + * + * @param pkt_in + * The input packet. + * @param pkts_out + * Array storing the output fragments. + * @param mtu_size + * Size in bytes of the Maximum Transfer Unit (MTU) for the outgoing XRAN + * datagrams. This value includes the size of the XRAN headers. + * @param pool_direct + * MBUF pool used for allocating direct buffers for the output fragments. + * @param pool_indirect + * MBUF pool used for allocating indirect buffers for the output fragments. + * @return + * Upon successful completion - number of output fragments placed + * in the pkts_out array. + * Otherwise - (-1) * . + */ +int32_t +xran_app_fragment_packet(struct rte_mbuf *pkt_in, /* eth hdr is prepended */ + struct rte_mbuf **pkts_out, + uint16_t nb_pkts_out, + uint16_t mtu_size, + struct rte_mempool *pool_direct, + struct rte_mempool *pool_indirect, + struct xran_section_info *sectinfo, + uint8_t *seqid) +{ + struct rte_mbuf *in_seg = NULL; + uint32_t out_pkt_pos = 0, in_seg_data_pos = 0; + uint32_t more_in_segs; + uint16_t fragment_offset, frag_size; + uint16_t frag_bytes_remaining; + struct eth_xran_up_pkt_hdr *in_hdr; + struct xran_up_pkt_hdr *in_hdr_xran; + + /* + * Ensure the XRAN payload length of all fragments is aligned to a + * multiple of 48 bytes (1 RB with IQ of 16 bits each) + */ + frag_size = ((mtu_size - sizeof(struct eth_xran_up_pkt_hdr) - RTE_PKTMBUF_HEADROOM)/XRAN_PAYLOAD_RB_ALIGN)*XRAN_PAYLOAD_RB_ALIGN; + + + print_dbg("frag_size %d\n",frag_size); + + in_hdr = rte_pktmbuf_mtod(pkt_in, struct eth_xran_up_pkt_hdr *); + + in_hdr_xran = &in_hdr->xran_hdr; + + /* Check that pkts_out is big enough to hold all fragments */ + if (unlikely(frag_size * nb_pkts_out < + (uint16_t)(pkt_in->pkt_len - sizeof (struct xran_up_pkt_hdr)))){ + print_err("-EINVAL\n"); + return -EINVAL; + } + + in_seg = pkt_in; + in_seg_data_pos = sizeof(struct eth_xran_up_pkt_hdr); + out_pkt_pos = 0; + fragment_offset = 0; + + more_in_segs = 1; + while (likely(more_in_segs)) { + struct rte_mbuf *out_pkt = NULL, *out_seg_prev = NULL; + uint32_t more_out_segs; + struct xran_up_pkt_hdr *out_hdr; + + /* Allocate direct buffer */ + out_pkt = rte_pktmbuf_alloc(pool_direct); + if (unlikely(out_pkt == NULL)) { + print_err("pool_direct -ENOMEM\n"); + __free_fragments(pkts_out, out_pkt_pos); + return -ENOMEM; + } + + print_dbg("[%d] out_pkt %p\n",more_in_segs, out_pkt); + + /* Reserve space for the XRAN header that will be built later */ + //out_pkt->data_len = sizeof(struct xran_up_pkt_hdr); + //out_pkt->pkt_len = sizeof(struct xran_up_pkt_hdr); + if(rte_pktmbuf_append(out_pkt, sizeof(struct xran_up_pkt_hdr)) ==NULL){ + rte_panic("sizeof(struct xran_up_pkt_hdr)"); + } + frag_bytes_remaining = frag_size; + + out_seg_prev = out_pkt; + more_out_segs = 1; + while (likely(more_out_segs && more_in_segs)) { + uint32_t len; +#ifdef XRAN_ATTACH_MBUF + struct rte_mbuf *out_seg = NULL; + + /* Allocate indirect buffer */ + print_dbg("Allocate indirect buffer \n"); + out_seg = rte_pktmbuf_alloc(pool_indirect); + if (unlikely(out_seg == NULL)) { + print_err("pool_indirect -ENOMEM\n"); + rte_pktmbuf_free(out_pkt); + __free_fragments(pkts_out, out_pkt_pos); + return -ENOMEM; + } + + print_dbg("[%d %d] out_seg %p\n",more_out_segs, more_in_segs, out_seg); + out_seg_prev->next = out_seg; + out_seg_prev = out_seg; + + /* Prepare indirect buffer */ + rte_pktmbuf_attach(out_seg, in_seg); +#endif + len = frag_bytes_remaining; + if (len > (in_seg->data_len - in_seg_data_pos)) { + len = in_seg->data_len - in_seg_data_pos; + } +#ifdef XRAN_ATTACH_MBUF + out_seg->data_off = in_seg->data_off + in_seg_data_pos; + out_seg->data_len = (uint16_t)len; + out_pkt->pkt_len = (uint16_t)(len + + out_pkt->pkt_len); + out_pkt->nb_segs += 1; +#else +{ + char* pChar = rte_pktmbuf_mtod(in_seg, char*); + void *iq_src = (pChar + in_seg_data_pos); + void *iq_dst = rte_pktmbuf_append(out_pkt, len); + + print_dbg("rte_pktmbuf_attach\n"); + if(iq_src && iq_dst) + rte_memcpy(iq_dst, iq_src, len); + else + print_err("iq_src %p iq_dst %p\n len %d room %d\n", iq_src, iq_dst, len, rte_pktmbuf_tailroom(out_pkt)); +} +#endif + in_seg_data_pos += len; + frag_bytes_remaining -= len; + + /* Current output packet (i.e. fragment) done ? */ + if (unlikely(frag_bytes_remaining == 0)) + more_out_segs = 0; + + /* Current input segment done ? */ + if (unlikely(in_seg_data_pos == in_seg->data_len)) { + in_seg = in_seg->next; + in_seg_data_pos = 0; + + if (unlikely(in_seg == NULL)) + more_in_segs = 0; + } + } + + /* Build the XRAN header */ + print_dbg("Build the XRAN header\n"); + out_hdr = rte_pktmbuf_mtod(out_pkt, struct xran_up_pkt_hdr *); + + __fill_xranhdr_frag(out_hdr, in_hdr_xran, + (uint16_t)out_pkt->pkt_len - sizeof(struct xran_up_pkt_hdr), + fragment_offset, sectinfo, more_in_segs, seqid); + + fragment_offset = (uint16_t)(fragment_offset + + out_pkt->pkt_len - sizeof(struct xran_up_pkt_hdr)); + + //out_pkt->l3_len = sizeof(struct xran_up_pkt_hdr); + + /* Write the fragment to the output list */ + pkts_out[out_pkt_pos] = out_pkt; + print_dbg("out_pkt_pos %d data_len %d pkt_len %d\n", out_pkt_pos, out_pkt->data_len, out_pkt->pkt_len); + out_pkt_pos ++; + //rte_pktmbuf_dump(stdout, out_pkt, 96); + } + + return out_pkt_pos; +} + + diff --git a/fhi_lib/lib/src/xran_app_frag.h b/fhi_lib/lib/src/xran_app_frag.h new file mode 100644 index 0000000..399f630 --- /dev/null +++ b/fhi_lib/lib/src/xran_app_frag.h @@ -0,0 +1,61 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +/** + * @brief Header file for functions to perform application level fragmentation + * + * @file xran_app_frag.h + * @ingroup group_source_xran + * @author Intel Corporation + **/ + +#ifndef _XRAN_APP_FRAG_ +#define _XRAN_APP_FRAG_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include "xran_fh_o_du.h" +#include "xran_cp_api.h" + +int32_t xran_app_fragment_packet(struct rte_mbuf *pkt_in, /* eth hdr is prepended */ + struct rte_mbuf **pkts_out, + uint16_t nb_pkts_out, + uint16_t mtu_size, + struct rte_mempool *pool_direct, + struct rte_mempool *pool_indirect, + struct xran_section_info *sectinfo, + uint8_t *seqid); + +#ifdef __cplusplus +} +#endif + +#endif /* _XRAN_APP_FRAG_ */ + diff --git a/fhi_lib/lib/src/xran_common.c b/fhi_lib/lib/src/xran_common.c index 67079e2..d72f27a 100644 --- a/fhi_lib/lib/src/xran_common.c +++ b/fhi_lib/lib/src/xran_common.c @@ -16,9 +16,6 @@ * *******************************************************************************/ -#ifndef _XRAN_COMMON_ -#define _XRAN_COMMON_ - /** * @brief XRAN layer common functionality for both lls-CU and RU as well as C-plane and * U-plane @@ -37,19 +34,53 @@ #include "ethdi.h" #include "xran_pkt.h" #include "xran_pkt_up.h" -#include "xran_cp_api.h" #include "xran_up_api.h" +#include "xran_lib_mlog_tasks_id.h" + #include "../src/xran_printf.h" +#include +#include "xran_mlog_lnx.h" -#ifndef MLOG_ENABLED -#include "mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif +#define MBUFS_CNT 16 -#define MBUFS_CNT 256 +extern long interval_us; extern int xran_process_rx_sym(void *arg, + struct rte_mbuf *mbuf, + void *iq_data_start, + uint16_t size, + uint8_t CC_ID, + uint8_t Ant_ID, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id, + uint32_t *mb_free); + + +extern int xran_process_prach_sym(void *arg, + struct rte_mbuf *mbuf, + void *iq_data_start, + uint16_t size, + uint8_t CC_ID, + uint8_t Ant_ID, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id); + +extern int32_t xran_pkt_validate(void *arg, + struct rte_mbuf *mbuf, void *iq_data_start, uint16_t size, uint8_t CC_ID, @@ -57,8 +88,16 @@ extern int xran_process_rx_sym(void *arg, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint8_t symb_id); + uint8_t symb_id, + struct ecpri_seq_id *seq_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id); +long rx_counter = 0; +long tx_counter = 0; int process_mbuf(struct rte_mbuf *pkt) { @@ -66,7 +105,8 @@ int process_mbuf(struct rte_mbuf *pkt) struct ecpri_seq_id seq; static int symbol_total_bytes = 0; int num_bytes = 0; - struct xran_ethdi_ctx *const ctx = xran_ethdi_get_ctx(); + struct xran_device_ctx * p_x_ctx = xran_dev_get_ctx(); + uint8_t CC_ID = 0; uint8_t Ant_ID = 0; uint8_t frame_id = 0; @@ -74,6 +114,21 @@ int process_mbuf(struct rte_mbuf *pkt) uint8_t slot_id = 0; uint8_t symb_id = 0; + uint16_t num_prbu; + uint16_t start_prbu; + uint16_t sym_inc; + uint16_t rb; + uint16_t sect_id; + void *pHandle = NULL; + uint8_t num_eAxc = xran_get_num_eAxc(pHandle); + int ret = MBUF_FREE; + uint32_t mb_free = 0; + int32_t valid_res = 0; + + + if(p_x_ctx->xran2phy_mem_ready == 0) + return MBUF_FREE; + num_bytes = xran_extract_iq_samples(pkt, &iq_samp_buf, &CC_ID, @@ -82,30 +137,104 @@ int process_mbuf(struct rte_mbuf *pkt) &subframe_id, &slot_id, &symb_id, - &seq); - if (num_bytes <= 0) - return -1; + &seq, + &num_prbu, + &start_prbu, + &sym_inc, + &rb, + §_id); + + if (num_bytes <= 0){ + print_err("num_bytes is wrong [%d]\n", num_bytes); + return MBUF_FREE; + } - symbol_total_bytes += num_bytes; - - if (seq.e_bit == 1) { - print_dbg("Completed receiving symbol %d, size=%d bytes\n", - symb_id, symbol_total_bytes); - - if (symbol_total_bytes) - xran_process_rx_sym(NULL, - iq_samp_buf, - symbol_total_bytes, - CC_ID, - Ant_ID, - frame_id, - subframe_id, - slot_id, - symb_id); - symbol_total_bytes = 0; + valid_res = xran_pkt_validate(NULL, + pkt, + iq_samp_buf, + num_bytes, + CC_ID, + Ant_ID, + frame_id, + subframe_id, + slot_id, + symb_id, + &seq, + num_prbu, + start_prbu, + sym_inc, + rb, + sect_id); + + if(valid_res != 0) { + print_err("valid_res is wrong [%d] ant %u (%u : %u : %u : %u) seq %u num_bytes %d\n", valid_res, Ant_ID, frame_id, subframe_id, slot_id, symb_id, seq.seq_id, num_bytes); + return MBUF_FREE; } - return 0; + if (Ant_ID >= num_eAxc && p_x_ctx->fh_init.prachEnable) // PRACH packet has ruportid = num_eAxc + ant_id + { + Ant_ID -= num_eAxc; + if (seq.e_bit == 1) { + + print_dbg("Completed receiving PRACH symbol %d, size=%d bytes\n", + symb_id, num_bytes); + + xran_process_prach_sym(NULL, + pkt, + iq_samp_buf, + num_bytes, + CC_ID, + Ant_ID, + frame_id, + subframe_id, + slot_id, + symb_id, + num_prbu, + start_prbu, + sym_inc, + rb, + sect_id); + } + else { + print_dbg("Transport layer fragmentation (eCPRI) is not supported\n"); + } + } else { + symbol_total_bytes += num_bytes; + + if (seq.e_bit == 1) { + print_dbg("Completed receiving symbol %d, size=%d bytes\n", + symb_id, symbol_total_bytes); + + if (symbol_total_bytes){ + int res = xran_process_rx_sym(NULL, + pkt, + iq_samp_buf, + symbol_total_bytes, + CC_ID, + Ant_ID, + frame_id, + subframe_id, + slot_id, + symb_id, + num_prbu, + start_prbu, + sym_inc, + rb, + sect_id, + &mb_free); + if(res == symbol_total_bytes) + ret = mb_free; + else + print_err("res != symbol_total_bytes\n"); + } + symbol_total_bytes = 0; + } + else { + print_dbg("Transport layer fragmentation (eCPRI) is not supported\n"); + } + } + + return ret; } static int set_iq_bit_width(uint8_t iq_bit_width, struct data_section_compression_hdr *compr_hdr) @@ -120,9 +249,11 @@ static int set_iq_bit_width(uint8_t iq_bit_width, struct data_section_compressio } /* Send a single 5G symbol over multiple packets */ -int send_symbol_ex(enum xran_pkt_dir direction, +int32_t prepare_symbol_ex(enum xran_pkt_dir direction, uint16_t section_id, + struct rte_mbuf *mb, struct rb_map *data, + const enum xran_input_byte_order iq_buf_byte_order, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, @@ -131,13 +262,20 @@ int send_symbol_ex(enum xran_pkt_dir direction, int prb_num, uint8_t CC_ID, uint8_t RU_Port_ID, - uint8_t seq_id) + uint8_t seq_id, + uint32_t do_copy) { - const int n_bytes = prb_num * N_SC_PER_PRB * sizeof(struct rb_map); - int sent; + int32_t n_bytes = ((prb_num == 0) ? MAX_N_FULLBAND_SC : prb_num) * N_SC_PER_PRB * sizeof(struct rb_map); + + int32_t prep_bytes; + + int16_t nPktSize = sizeof(struct ether_hdr) + sizeof(struct xran_ecpri_hdr) + + sizeof(struct radio_app_common_hdr)+ sizeof(struct data_section_hdr) + n_bytes; uint32_t off; struct xran_up_pkt_gen_no_compression_params xp = { 0 }; + n_bytes = RTE_MIN(n_bytes, XRAN_MAX_MBUF_LEN); + /* radio app header */ xp.app_params.data_direction = direction; xp.app_params.payl_ver = 1; @@ -159,24 +297,98 @@ int send_symbol_ex(enum xran_pkt_dir direction, /* network byte order */ xp.sec_hdr.fields.all_bits = rte_cpu_to_be_32(xp.sec_hdr.fields.all_bits); - struct rte_mbuf *mb = xran_ethdi_mbuf_alloc(); - if (mb == NULL){ MLogPrint(NULL); errx(1, "out of mbufs after %d packets", 1); } - sent = xran_prepare_iq_symbol_portion_no_comp(mb, + prep_bytes = xran_prepare_iq_symbol_portion_no_comp(mb, data, + iq_buf_byte_order, n_bytes, &xp, CC_ID, RU_Port_ID, - seq_id); - if (sent <= 0) + seq_id, + do_copy); + if (prep_bytes <= 0) errx(1, "failed preparing symbol"); - xran_ethdi_mbuf_send(mb, ETHER_TYPE_ECPRI); + rte_pktmbuf_pkt_len(mb) = nPktSize; + rte_pktmbuf_data_len(mb) = nPktSize; + +#ifdef DEBUG + printf("Symbol %2d prep_bytes (%d packets, %d bytes)\n", symbol_no, i, n_bytes); +#endif + + return prep_bytes; +} + +/* Send a single 5G symbol over multiple packets */ +int send_symbol_ex(enum xran_pkt_dir direction, + uint16_t section_id, + struct rte_mbuf *mb, + struct rb_map *data, + const enum xran_input_byte_order iq_buf_byte_order, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symbol_no, + int prb_start, + int prb_num, + uint8_t CC_ID, + uint8_t RU_Port_ID, + uint8_t seq_id) +{ + uint32_t do_copy = 0; + int32_t n_bytes = ((prb_num == 0) ? MAX_N_FULLBAND_SC : prb_num) * N_SC_PER_PRB * sizeof(struct rb_map); + + if (mb == NULL){ + char * pChar = NULL; + mb = xran_ethdi_mbuf_alloc(); /* will be freede by ETH */ + if(mb == NULL){ + MLogPrint(NULL); + errx(1, "out of mbufs after %d packets", 1); + } + pChar = rte_pktmbuf_append(mb, sizeof(struct xran_ecpri_hdr)+ sizeof(struct radio_app_common_hdr)+ sizeof(struct data_section_hdr) + n_bytes); + if(pChar == NULL){ + MLogPrint(NULL); + errx(1, "incorrect mbuf size %d packets", 1); + } + pChar = rte_pktmbuf_prepend(mb, sizeof(struct ether_hdr)); + if(pChar == NULL){ + MLogPrint(NULL); + errx(1, "incorrect mbuf size %d packets", 1); + } + do_copy = 1; /* new mbuf hence copy of IQs */ + }else { + rte_pktmbuf_refcnt_update(mb, 1); /* make sure eth won't free our mbuf */ + } + + int32_t sent = prepare_symbol_ex(direction, + section_id, + mb, + data, + iq_buf_byte_order, + frame_id, + subframe_id, + slot_id, + symbol_no, + prb_start, + prb_num, + CC_ID, + RU_Port_ID, + seq_id, + do_copy); + + if(sent){ + tx_counter++; + xran_ethdi_mbuf_send(mb, ETHER_TYPE_ECPRI); + } else { + + } + + #ifdef DEBUG printf("Symbol %2d sent (%d packets, %d bytes)\n", symbol_no, i, n_bytes); @@ -185,82 +397,97 @@ int send_symbol_ex(enum xran_pkt_dir direction, return sent; } -int send_cpmsg_dlul(void *pHandle, enum xran_pkt_dir dir, - uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint8_t startsym, uint8_t numsym, int prb_num, - uint16_t beam_id, - uint8_t cc_id, uint8_t ru_port_id, - uint8_t seq_id) +int send_cpmsg(void *pHandle, struct rte_mbuf *mbuf,struct xran_cp_gen_params *params, + struct xran_section_gen_info *sect_geninfo, uint8_t cc_id, uint8_t ru_port_id, uint8_t seq_id) { - struct xran_cp_gen_params params; - struct xran_section_gen_info sect_geninfo[XRAN_MAX_NUM_SECTIONS]; - struct rte_mbuf *mbuf; - int ret, nsection, i; - - - params.dir = dir; - params.sectionType = XRAN_CP_SECTIONTYPE_1; // Most DL/UL Radio Channels - params.hdr.filterIdx = XRAN_FILTERINDEX_STANDARD; - params.hdr.frameId = frame_id; - params.hdr.subframeId = subframe_id; - params.hdr.slotId = slot_id; - params.hdr.startSymId = startsym; // start Symbol ID - params.hdr.iqWidth = xran_get_conf_iqwidth(pHandle); - params.hdr.compMeth = xran_get_conf_compmethod(pHandle); + int ret = 0, nsection, i; + uint8_t frame_id = params->hdr.frameId; + uint8_t subframe_id = params->hdr.subframeId; + uint8_t slot_id = params->hdr.slotId; + uint8_t dir = params->dir; + + nsection = params->numSections; + + /* add in the ethernet header */ + struct ether_hdr *const h = (void *)rte_pktmbuf_prepend(mbuf, sizeof(*h)); + xran_ethdi_mbuf_send_cp(mbuf, ETHER_TYPE_ECPRI); + tx_counter++; + for(i=0; idir = dir; + params->sectionType = XRAN_CP_SECTIONTYPE_1; // Most DL/UL Radio Channels + params->hdr.filterIdx = XRAN_FILTERINDEX_STANDARD; + params->hdr.frameId = frame_id; + params->hdr.subframeId = subframe_id; + params->hdr.slotId = slot_id; + params->hdr.startSymId = startsym; // start Symbol ID + params->hdr.iqWidth = xran_get_conf_iqwidth(pHandle); + params->hdr.compMeth = comp_method; nsection = 0; - sect_geninfo[nsection].info.type = params.sectionType; + sect_geninfo[nsection].info.type = params->sectionType; // for database + sect_geninfo[nsection].info.startSymId = params->hdr.startSymId; // for database + sect_geninfo[nsection].info.iqWidth = params->hdr.iqWidth; // for database + sect_geninfo[nsection].info.compMeth = params->hdr.compMeth; // for database sect_geninfo[nsection].info.id = xran_alloc_sectionid(pHandle, dir, cc_id, ru_port_id, slot_id); sect_geninfo[nsection].info.rb = XRAN_RBIND_EVERY; - sect_geninfo[nsection].info.symInc = XRAN_SYMBOLNUMBER_NOTINC; - sect_geninfo[nsection].info.startPrbc = 0; - sect_geninfo[nsection].info.numPrbc = NUM_OF_PRB_IN_FULL_BAND, + sect_geninfo[nsection].info.symInc = symInc; + sect_geninfo[nsection].info.startPrbc = prb_start; + sect_geninfo[nsection].info.numPrbc = prb_num; sect_geninfo[nsection].info.numSymbol = numsym; sect_geninfo[nsection].info.reMask = 0xfff; sect_geninfo[nsection].info.beamId = beam_id; - sect_geninfo[nsection].info.ef = 0; // no extension + sect_geninfo[nsection].info.ef = 0; sect_geninfo[nsection].exDataSize = 0; - sect_geninfo[nsection].exData = NULL; +// sect_geninfo[nsection].exData = NULL; nsection++; - params.numSections = nsection; - params.sections = sect_geninfo; + params->numSections = nsection; + params->sections = sect_geninfo; - mbuf = xran_ethdi_mbuf_alloc(); if(unlikely(mbuf == NULL)) { print_err("Alloc fail!\n"); return (-1); - } + } - ret = xran_prepare_ctrl_pkt(mbuf, ¶ms, cc_id, ru_port_id, seq_id); - if(ret < 0) { + ret = xran_prepare_ctrl_pkt(mbuf, params, cc_id, ru_port_id, seq_id); + if(ret < 0){ print_err("Fail to build control plane packet - [%d:%d:%d] dir=%d\n", frame_id, subframe_id, slot_id, dir); - } - else { - xran_ethdi_mbuf_send_cp(mbuf, ETHER_TYPE_ECPRI); - for(i=0; iPrachCPConfig); + uint16_t timeOffset; + uint16_t nNumerology = pxran_lib_ctx->fh_cfg.frame_conf.nNumerology; + if(unlikely(mbuf == NULL)) { + print_err("Alloc fail!\n"); + return (-1); + } #if 0 printf("%d:%d:%d:%d - filter=%d, startSym=%d[%d:%d], numSym=%d, occasions=%d, freqOff=%d\n", frame_id, subframe_id, slot_id, prach_port_id, @@ -272,62 +499,60 @@ int send_cpmsg_prach(void *pHandle, pPrachCPConfig->occassionsInPrachSlot, pPrachCPConfig->freqOffset); #endif - - params.dir = XRAN_DIR_UL; - params.sectionType = XRAN_CP_SECTIONTYPE_3; - params.hdr.filterIdx = pPrachCPConfig->filterIdx; - params.hdr.frameId = frame_id; - params.hdr.subframeId = subframe_id; - params.hdr.slotId = slot_id; - params.hdr.startSymId = pPrachCPConfig->startSymId; - params.hdr.iqWidth = xran_get_conf_iqwidth(pHandle); - params.hdr.compMeth = xran_get_conf_compmethod(pHandle); + timeOffset = pPrachCPConfig->timeOffset; //this is the CP value per 38.211 tab 6.3.3.1-1&2 + timeOffset = timeOffset >> nNumerology; //original number is Tc, convert to Ts based on mu + if (pPrachCPConfig->startSymId > 0) + { + timeOffset += (pPrachCPConfig->startSymId * 2048) >> nNumerology; + if ((slot_id == 0) || (slot_id == (SLOTNUM_PER_SUBFRAME >> 1))) + timeOffset += 16; + } + params->dir = XRAN_DIR_UL; + params->sectionType = XRAN_CP_SECTIONTYPE_3; + params->hdr.filterIdx = pPrachCPConfig->filterIdx; + params->hdr.frameId = frame_id; + params->hdr.subframeId = subframe_id; + params->hdr.slotId = slot_id; + params->hdr.startSymId = pPrachCPConfig->startSymId; + params->hdr.iqWidth = xran_get_conf_iqwidth(pHandle); + params->hdr.compMeth = xran_get_conf_compmethod(pHandle); /* use timeOffset field for the CP length value for prach sequence */ - params.hdr.timeOffset = pPrachCPConfig->timeOffset; - params.hdr.fftSize = xran_get_conf_fftsize(pHandle); - params.hdr.scs = xran_get_conf_prach_scs(pHandle); - params.hdr.cpLength = 0; + params->hdr.timeOffset = timeOffset; + params->hdr.fftSize = xran_get_conf_fftsize(pHandle); + params->hdr.scs = xran_get_conf_prach_scs(pHandle); + params->hdr.cpLength = 0; nsection = 0; - sect_geninfo[nsection].info.type = params.sectionType; - sect_geninfo[nsection].info.id = xran_alloc_sectionid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, slot_id); - sect_geninfo[nsection].info.rb = XRAN_RBIND_EVERY; - sect_geninfo[nsection].info.symInc = XRAN_SYMBOLNUMBER_NOTINC; - sect_geninfo[nsection].info.startPrbc = pPrachCPConfig->startPrbc; - sect_geninfo[nsection].info.numPrbc = pPrachCPConfig->numPrbc, - sect_geninfo[nsection].info.numSymbol = pPrachCPConfig->numSymbol*pPrachCPConfig->occassionsInPrachSlot; - sect_geninfo[nsection].info.reMask = 0xfff; - sect_geninfo[nsection].info.beamId = beam_id; - sect_geninfo[nsection].info.freqOffset= pPrachCPConfig->freqOffset; - - sect_geninfo[nsection].info.ef = 0; // no extension - sect_geninfo[nsection].exDataSize = 0; - sect_geninfo[nsection].exData = NULL; - nsection++; + sect_geninfo[nsection].info.type = params->sectionType; // for database + sect_geninfo[nsection].info.startSymId = params->hdr.startSymId; // for database + sect_geninfo[nsection].info.iqWidth = params->hdr.iqWidth; // for database + sect_geninfo[nsection].info.compMeth = params->hdr.compMeth; // for database + sect_geninfo[nsection].info.id = xran_alloc_sectionid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, slot_id); + sect_geninfo[nsection].info.rb = XRAN_RBIND_EVERY; + sect_geninfo[nsection].info.symInc = XRAN_SYMBOLNUMBER_NOTINC; + sect_geninfo[nsection].info.startPrbc = pPrachCPConfig->startPrbc; + sect_geninfo[nsection].info.numPrbc = pPrachCPConfig->numPrbc, + sect_geninfo[nsection].info.numSymbol = pPrachCPConfig->numSymbol*pPrachCPConfig->occassionsInPrachSlot; + sect_geninfo[nsection].info.reMask = 0xfff; + sect_geninfo[nsection].info.beamId = beam_id; + sect_geninfo[nsection].info.freqOffset = pPrachCPConfig->freqOffset; - params.numSections = nsection; - params.sections = sect_geninfo; + pxran_lib_ctx->prach_last_symbol[cc_id] = sect_geninfo[nsection].info.startSymId + sect_geninfo[nsection].info.numSymbol - 1; - mbuf = xran_ethdi_mbuf_alloc(); - if(unlikely(mbuf == NULL)) { - print_err("Alloc fail!\n"); - return (-1); - } + sect_geninfo[nsection].info.ef = 0; + sect_geninfo[nsection].exDataSize = 0; +// sect_geninfo[nsection].exData = NULL; + nsection++; - ret = xran_prepare_ctrl_pkt(mbuf, ¶ms, cc_id, prach_port_id, seq_id); - if(ret < 0) { - print_err("Fail to build prach control packet - [%d:%d:%d]\n", frame_id, subframe_id, slot_id); - return (ret); - } - else { - xran_ethdi_mbuf_send_cp(mbuf, ETHER_TYPE_ECPRI); - for(i=0; i < nsection; i++) - xran_cp_add_section_info(pHandle, - XRAN_DIR_UL, cc_id, prach_port_id, subframe_id, slot_id, - §_geninfo[i].info); - } + params->numSections = nsection; + params->sections = sect_geninfo; - return (ret); + ret = xran_prepare_ctrl_pkt(mbuf, params, cc_id, prach_port_id, seq_id); + if(ret < 0){ + print_err("Fail to build prach control packet - [%d:%d:%d]\n", frame_id, subframe_id, slot_id); + rte_pktmbuf_free(mbuf); + } + return ret; } @@ -338,60 +563,65 @@ int process_ring(struct rte_ring *r) struct rte_mbuf *mbufs[MBUFS_CNT]; int i; uint32_t remaining; + uint64_t t1; const uint16_t dequeued = rte_ring_dequeue_burst(r, (void **)mbufs, RTE_DIM(mbufs), &remaining); if (!dequeued) return 0; + + t1 = MLogTick(); for (i = 0; i < dequeued; ++i) { if (xran_ethdi_filter_packet(mbufs[i], 0) == MBUF_FREE) rte_pktmbuf_free(mbufs[i]); } + MLogTask(PID_PROCESS_UP_PKT, t1, MLogTick()); return remaining; } -int ring_processing_thread(void *args) +int32_t ring_processing_func(void) { - struct timespec tv = {0}; - int64_t prev_nsec = 0; - uint8_t is_timer_set = 0; struct xran_ethdi_ctx *const ctx = xran_ethdi_get_ctx(); + struct xran_device_ctx *const pxran_lib_ctx = xran_dev_get_ctx(); + + rte_timer_manage(); + + /* UP first */ + if (process_ring(ctx->rx_ring[ETHDI_UP_VF])) + return 0; + /* CP next */ + if (process_ring(ctx->rx_ring[ETHDI_CP_VF])) + return 0; + + if (pxran_lib_ctx->bbdev_dec) + pxran_lib_ctx->bbdev_dec(); + + if (pxran_lib_ctx->bbdev_enc) + pxran_lib_ctx->bbdev_enc(); + + if (XRAN_STOPPED == xran_if_current_state) + return -1; + + return 0; +} + +int ring_processing_thread(void *args) +{ struct sched_param sched_param; int res = 0; printf("%s [CPU %2d] [PID: %6d]\n", __FUNCTION__, rte_lcore_id(), getpid()); sched_param.sched_priority = XRAN_THREAD_DEFAULT_PRIO; - if ((res = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param))) - { + if ((res = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param))){ printf("priority is not changed: coreId = %d, result1 = %d\n",rte_lcore_id(), res); } - for (;;) { - if (!is_timer_set) { - if (clock_gettime(CLOCK_REALTIME, &tv) != 0) - err(1, "gettimeofday() failed"); - if (tv.tv_nsec % 125000 < prev_nsec % 125000) { /* crossed an 125ms boundary */ - rte_timer_manage(); /* timers only run on IO core */ - is_timer_set = 1; - } - prev_nsec = tv.tv_nsec; - } else { - rte_timer_manage(); - } - /* UP first */ - if (process_ring(ctx->rx_ring[ETHDI_UP_VF])) - continue; - /* CP next */ - if (process_ring(ctx->rx_ring[ETHDI_CP_VF])) - continue; - - if (XRAN_STOPPED == xran_if_current_state) + for (;;) + if(ring_processing_func() != 0) break; - } puts("Pkt processing thread finished."); return 0; } -#endif /* _XRAN_COMMON_ */ diff --git a/fhi_lib/lib/src/xran_common.h b/fhi_lib/lib/src/xran_common.h index ad17c1e..07be798 100644 --- a/fhi_lib/lib/src/xran_common.h +++ b/fhi_lib/lib/src/xran_common.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief XRAN layer common functionality for both lls-CU and RU as well as C-plane and * U-plane @@ -28,64 +27,50 @@ #ifndef _XRAN_COMMON_H_ #define _XRAN_COMMON_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include #include +#include + #include #include #include -#include "xran_fh_lls_cu.h" +#include "xran_fh_o_du.h" #include "xran_pkt_up.h" +#include "xran_cp_api.h" + +#define O_DU 0 +#define O_RU 1 -#define APP_LLS_CU 0 -#define APP_RU 1 -#define NUM_OF_PRB_IN_FULL_BAND (66) #define N_SC_PER_PRB 12 +#define MAX_N_FULLBAND_SC 273 #define N_SYM_PER_SLOT 14 -#define N_FULLBAND_SC (NUM_OF_PRB_IN_FULL_BAND*N_SC_PER_PRB) -#define MAX_ANT_CARRIER_SUPPORTED 16 -/* 0.125, just for testing */ -#define SLOTNUM_PER_SUBFRAME 8 +#define SUBFRAME_DURATION_US 1000 +#define SLOTNUM_PER_SUBFRAME (SUBFRAME_DURATION_US/interval_us) #define SUBFRAMES_PER_SYSTEMFRAME 10 #define SLOTS_PER_SYSTEMFRAME (SLOTNUM_PER_SUBFRAME*SUBFRAMES_PER_SYSTEMFRAME) -#define PDSCH_PAYLOAD_SIZE (N_FULLBAND_SC*4) -#define NUM_OF_SLOT_IN_TDD_LOOP (80) -#define IQ_PLAYBACK_BUFFER_BYTES (NUM_OF_SLOT_IN_TDD_LOOP*N_SYM_PER_SLOT*N_FULLBAND_SC*4L) -/* PRACH data samples are 32 bits wide, 16bits for I and 16bits for Q. Each packet contains 839 samples. The payload length is 3356 octets.*/ -#define PRACH_PLAYBACK_BUFFER_BYTES (10*839*4L) +/* PRACH data samples are 32 bits wide, 16bits for I and 16bits for Q. Each packet contains 839 samples for long sequence or 144*14 (max) for short sequence. The payload length is 3356 octets.*/ +#define PRACH_PLAYBACK_BUFFER_BYTES (144*14*4L) -#define XRAN_MAX_NUM_SECTIONS (NUM_OF_PRB_IN_FULL_BAND) // TODO: need to decide proper value +#define XRAN_MAX_NUM_SECTIONS (N_SYM_PER_SLOT*2) /* just an example, no special meaning on this number */ + /* and this is the configuration of M-plane */ -#define XRAN_MAX_MBUF_LEN 9600 /**< jummbo frame */ -#define NSEC_PER_SEC 1000000000 +#define XRAN_MAX_MBUF_LEN 9600 /**< jumbo frame */ +#define NSEC_PER_SEC 1000000000L #define TIMER_RESOLUTION_CYCLES 1596*1 /* 1us */ #define XRAN_RING_SIZE 512 /*4*14*8 pow of 2 */ #define XRAN_NAME_MAX_LEN (64) #define XRAN_RING_NUM (3) -#define MAX_NUM_OF_XRAN_CTX (2) -#define XranIncrementCtx(ctx) ((ctx >= (MAX_NUM_OF_XRAN_CTX-1)) ? 0 : (ctx+1)) -#define XranDecrementCtx(ctx) ((ctx == 0) ? (MAX_NUM_OF_XRAN_CTX-1) : (ctx-1)) - #define XranDiffSymIdx(prevSymIdx, currSymIdx, numTotalSymIdx) ((prevSymIdx > currSymIdx) ? ((currSymIdx + numTotalSymIdx) - prevSymIdx) : (currSymIdx - prevSymIdx)) -#define XRAN_SYM_JOB_SIZE 512 - -struct send_symbol_cb_args -{ - struct rb_map *samp_buf; - uint8_t *symb_id; -}; - -struct pkt_dump -{ - int num_samp; - int num_bytes; - uint8_t symb; - struct ecpri_seq_id seq; -} __rte_packed; +#define XRAN_MLOG_VAR 0 /**< enable debug variables to mlog */ /* PRACH configuration table defines */ #define XRAN_PRACH_CANDIDATE_PREAMBLE (2) @@ -144,27 +129,30 @@ typedef struct uint8_t numSymbol; uint16_t timeOffset; int32_t freqOffset; + uint8_t nrofPrachInSlot; uint8_t occassionsInPrachSlot; uint8_t x; uint8_t y[XRAN_PRACH_CANDIDATE_Y]; uint8_t isPRACHslot[XRAN_PRACH_CANDIDATE_SLOT]; }xRANPrachCPConfigStruct; +#define XRAN_MAX_POOLS_PER_SECTOR_NR 3 /**< TX_OUT, RX_IN, PRACH_IN */ -typedef struct DeviceHandleInfo +typedef struct sectorHandleInfo { /**< Structure that contains the information to describe the * instance i.e service type, virtual function, package Id etc..*/ uint16_t nIndex; + uint16_t nXranPort; /* Unique ID of an handle shared between phy layer and library */ /**< number of antennas supported per link*/ uint32_t nBufferPoolIndex; /**< Buffer poolIndex*/ - struct rte_mempool * p_bufferPool[XRAN_MAX_SECTOR_NR]; - uint32_t bufferPoolElmSz[XRAN_MAX_SECTOR_NR]; - uint32_t bufferPoolNumElm[XRAN_MAX_SECTOR_NR]; + struct rte_mempool * p_bufferPool[XRAN_MAX_POOLS_PER_SECTOR_NR]; + uint32_t bufferPoolElmSz[XRAN_MAX_POOLS_PER_SECTOR_NR]; + uint32_t bufferPoolNumElm[XRAN_MAX_POOLS_PER_SECTOR_NR]; -}XranLibHandleInfoStruct; +}XranSectorHandleInfo, *PXranSectorHandleInfo; typedef void (*XranSymCallbackFn)(struct rte_timer *tim, void* arg); @@ -184,7 +172,7 @@ typedef struct { // -1 means that DL packet to be transmitted is not ready in BS int32_t nSegTransferred; // number of data segments has been transmitted or received struct rte_mbuf *pData[XRAN_N_MAX_BUFFER_SEGMENT]; // point to DPDK allocated memory pool - XRANBufferListStruct sBufferList; + struct xran_buffer_list sBufferList; } BbuIoBufCtrlStruct; struct xran_sym_job { @@ -194,37 +182,45 @@ struct xran_sym_job { #define XranIncrementJob(i) ((i >= (XRAN_SYM_JOB_SIZE-1)) ? 0 : (i+1)) -struct xran_lib_ctx +#define XRAN_MAX_PKT_BURST_PER_SYM 32 +#define XRAN_MAX_PACKET_FRAG 9 + +#define MBUF_TABLE_SIZE (2 * MAX(XRAN_MAX_PKT_BURST_PER_SYM, XRAN_MAX_PACKET_FRAG)) + +struct mbuf_table { + uint16_t len; + struct rte_mbuf *m_table[MBUF_TABLE_SIZE]; +}; + +struct xran_device_ctx { - uint8_t llscu_id; uint8_t sector_id; - XRANEAXCIDCONFIG eAxc_id_cfg; - XRANFHINIT xran_init_cfg; - XRANFHCONFIG xran_fh_cfg; - XranLibHandleInfoStruct* pDevHandle; + uint8_t xran_port_id; + struct xran_eaxcid_config eAxc_id_cfg; + struct xran_fh_init fh_init; + struct xran_fh_config fh_cfg; + uint32_t enablePrach; xRANPrachCPConfigStruct PrachCPConfig; uint32_t enableCP; - char ring_name[XRAN_RING_NUM][XRAN_MAX_SECTOR_NR][RTE_RING_NAMESIZE]; - struct rte_ring *dl_sym_idx_ring[XRAN_MAX_SECTOR_NR]; - struct rte_ring *xran2phy_ring[XRAN_MAX_SECTOR_NR]; - struct rte_ring *xran2prach_ring[XRAN_MAX_SECTOR_NR]; - - struct xran_sym_job sym_job[XRAN_SYM_JOB_SIZE]; - uint32_t sym_job_idx; + int32_t DynamicSectionEna; BbuIoBufCtrlStruct sFrontHaulTxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + BbuIoBufCtrlStruct sFrontHaulTxPrbMapBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; BbuIoBufCtrlStruct sFrontHaulRxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; + BbuIoBufCtrlStruct sFrontHaulRxPrbMapBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; BbuIoBufCtrlStruct sFHPrachRxBbuIoBufCtrl[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR]; /* buffers lists */ - XRANFlatBufferStruct sFrontHaulTxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; - XRANFlatBufferStruct sFrontHaulRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; - XRANFlatBufferStruct sFHPrachRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulTxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulTxPrbMapBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFrontHaulRxPrbMapBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + struct xran_flat_buffer sFHPrachRxBuffers[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_MAX_ANTENNA_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; - XranTransportBlockCallbackFn pCallback[XRAN_MAX_SECTOR_NR]; + xran_transport_callback_fn pCallback[XRAN_MAX_SECTOR_NR]; void *pCallbackTag[XRAN_MAX_SECTOR_NR]; - XranTransportBlockCallbackFn pPrachCallback[XRAN_MAX_SECTOR_NR]; + xran_transport_callback_fn pPrachCallback[XRAN_MAX_SECTOR_NR]; void *pPrachCallbackTag[XRAN_MAX_SECTOR_NR]; XranSymCallbackFn pSymCallback[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; @@ -233,21 +229,38 @@ struct xran_lib_ctx int32_t sym_up; /**< when we start sym 0 of up with respect to OTA time as measured in symbols */ int32_t sym_up_ul; - XRANFHTTIPROCCB ttiCb[XRAN_CB_MAX]; + xran_fh_tti_callback_fn ttiCb[XRAN_CB_MAX]; void *TtiCbParam[XRAN_CB_MAX]; uint32_t SkipTti[XRAN_CB_MAX]; int xran2phy_mem_ready; int rx_packet_symb_tracker[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; + int rx_packet_prach_tracker[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SYMBOL_PER_SLOT]; int rx_packet_callback_tracker[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR]; + int rx_packet_prach_callback_tracker[XRAN_N_FE_BUF_LEN][XRAN_MAX_SECTOR_NR]; + int prach_start_symbol[XRAN_MAX_SECTOR_NR]; + int prach_last_symbol[XRAN_MAX_SECTOR_NR]; + int phy_tti_cb_done; + + struct rte_mempool *direct_pool; + struct rte_mempool *indirect_pool; + struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; + + struct xran_common_counters fh_counters; + + phy_encoder_poll_fn bbdev_enc; /**< call back to poll BBDev encoder */ + phy_decoder_poll_fn bbdev_dec; /**< call back to poll BBDev decoder */ }; +extern long rx_counter; +extern long tx_counter; + extern const xRANPrachConfigTableStruct gxranPrachDataTable_sub6_fdd[XRAN_PRACH_CONFIG_TABLE_SIZE]; extern const xRANPrachConfigTableStruct gxranPrachDataTable_sub6_tdd[XRAN_PRACH_CONFIG_TABLE_SIZE]; extern const xRANPrachConfigTableStruct gxranPrachDataTable_mmw[XRAN_PRACH_CONFIG_TABLE_SIZE]; -extern const xRANPrachPreambleLRAStruct gxranPreambleforLRA[XRAN_PRACH_PREAMBLE_FORMAT_OF_ABC]; +extern const xRANPrachPreambleLRAStruct gxranPreambleforLRA[13]; int process_mbuf(struct rte_mbuf *pkt); int process_ring(struct rte_ring *r); @@ -256,7 +269,9 @@ int packets_dump_thread(void *args); int send_symbol_ex(enum xran_pkt_dir direction, uint16_t section_id, + struct rte_mbuf *mb, struct rb_map *data, + const enum xran_input_byte_order iq_buf_byte_order, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, @@ -267,20 +282,35 @@ int send_symbol_ex(enum xran_pkt_dir direction, uint8_t RU_Port_ID, uint8_t seq_id); -int send_cpmsg_dlul(void *pHandle, enum xran_pkt_dir dir, - uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint8_t startsym, uint8_t numsym, int prb_num, - uint16_t beam_id, uint8_t cc_id, uint8_t ru_port_id, - uint8_t seq_id); +int32_t prepare_symbol_ex(enum xran_pkt_dir direction, + uint16_t section_id, + struct rte_mbuf *mb, + struct rb_map *data, + const enum xran_input_byte_order iq_buf_byte_order, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symbol_no, + int prb_start, + int prb_num, + uint8_t CC_ID, + uint8_t RU_Port_ID, + uint8_t seq_id, + uint32_t do_copy); -int send_cpmsg_prach(void *pHandle, - uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint16_t beam_id, uint8_t cc_id, uint8_t prach_port_id, - uint8_t seq_id); +int send_cpmsg(void *pHandle, struct rte_mbuf *mbuf,struct xran_cp_gen_params *params, + struct xran_section_gen_info *sect_geninfo, uint8_t cc_id, uint8_t ru_port_id, uint8_t seq_id); -uint8_t xran_get_max_sections(void *pHandle); +int generate_cpmsg_dlul(void *pHandle, struct xran_cp_gen_params *params, struct xran_section_gen_info *sect_geninfo, struct rte_mbuf *mbuf, + enum xran_pkt_dir dir, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, + uint8_t startsym, uint8_t numsym, uint16_t prb_start, uint16_t prb_num, + uint16_t beam_id, uint8_t cc_id, uint8_t ru_port_id, uint8_t comp_method, uint8_t seq_id, uint8_t symInc); -XRANEAXCIDCONFIG *xran_get_conf_eAxC(void *pHandle); +int generate_cpmsg_prach(void *pHandle, struct xran_cp_gen_params *params, struct xran_section_gen_info *sect_geninfo, struct rte_mbuf *mbuf, struct xran_device_ctx *pxran_lib_ctx, + uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, + uint16_t beam_id, uint8_t cc_id, uint8_t prach_port_id, uint8_t seq_id); + +struct xran_eaxcid_config *xran_get_conf_eAxC(void *pHandle); uint8_t xran_get_conf_prach_scs(void *pHandle); uint8_t xran_get_conf_fftsize(void *pHandle); uint8_t xran_get_conf_numerology(void *pHandle); @@ -289,12 +319,16 @@ uint8_t xran_get_conf_compmethod(void *pHandle); uint8_t xran_get_num_cc(void *pHandle); uint8_t xran_get_num_eAxc(void *pHandle); -uint8_t xran_get_llscuid(void *pHandle); -uint8_t xran_get_sectorid(void *pHandle); -struct xran_lib_ctx *xran_lib_get_ctx(void); +struct xran_device_ctx *xran_dev_get_ctx(void); uint16_t xran_alloc_sectionid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id); uint8_t xran_get_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id); +int32_t ring_processing_func(void); +int xran_init_prach(struct xran_fh_config* pConf, struct xran_device_ctx * p_xran_dev_ctx); + +#ifdef __cplusplus +} +#endif #endif diff --git a/fhi_lib/lib/src/xran_compression.cpp b/fhi_lib/lib/src/xran_compression.cpp new file mode 100644 index 0000000..8730a20 --- /dev/null +++ b/fhi_lib/lib/src/xran_compression.cpp @@ -0,0 +1,185 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + +#include "xran_compression.hpp" +#include +#include +#include + +void +BlockFloatCompander::BlockFloatCompress_AVX512(const ExpandedData& dataIn, CompressedData* dataOut) +{ + __m512i maxAbs = __m512i(); + + /// Load data and find max(abs(RB)) + const __m512i* rawData = reinterpret_cast(dataIn.dataExpanded); + static constexpr int k_numInputLoopIts = BlockFloatCompander::k_numRB / 4; + +#pragma unroll(k_numInputLoopIts) + for (int n = 0; n < k_numInputLoopIts; ++n) + { + /// Re-order the next 4RB in input data into 3 registers + /// Input SIMD vectors are: + /// [A A A A A A A A A A A A B B B B] + /// [B B B B B B B B C C C C C C C C] + /// [C C C C D D D D D D D D D D D D] + /// Re-ordered SIMD vectors are: + /// [A A A A B B B B C C C C D D D D] + /// [A A A A B B B B C C C C D D D D] + /// [A A A A B B B B C C C C D D D D] + static constexpr uint8_t k_msk1 = 0b11111100; // Copy first lane of src + static constexpr int k_shuff1 = 0x41; + const auto z_w1 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 0], k_msk1, rawData[3 * n + 1], rawData[3 * n + 2], k_shuff1); + + static constexpr uint8_t k_msk2 = 0b11000011; // Copy middle two lanes of src + static constexpr int k_shuff2 = 0xB1; + const auto z_w2 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 1], k_msk2, rawData[3 * n + 0], rawData[3 * n + 2], k_shuff2); + + static constexpr uint8_t k_msk3 = 0b00111111; // Copy last lane of src + static constexpr int k_shuff3 = 0xBE; + const auto z_w3 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 2], k_msk3, rawData[3 * n + 0], rawData[3 * n + 1], k_shuff3); + + /// Perform max abs on these 3 registers + const auto abs16_1 = _mm512_abs_epi16(z_w1); + const auto abs16_2 = _mm512_abs_epi16(z_w2); + const auto abs16_3 = _mm512_abs_epi16(z_w3); + const auto maxAbs_12 = _mm512_max_epi16(abs16_1, abs16_2); + const auto maxAbs_123 = _mm512_max_epi16(maxAbs_12, abs16_3); + + /// Perform horizontal max over each lane + /// Swap 64b in each lane and compute max + static const auto k_perm64b = _mm512_set_epi64(6, 7, 4, 5, 2, 3, 0, 1); + auto maxAbsPerm = _mm512_permutexvar_epi64(k_perm64b, maxAbs_123); + auto maxAbsHorz = _mm512_max_epi16(maxAbs_123, maxAbsPerm); + + /// Swap each pair of 32b in each lane and compute max + static const auto k_perm32b = _mm512_set_epi32(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + maxAbsPerm = _mm512_permutexvar_epi32(k_perm32b, maxAbsHorz); + maxAbsHorz = _mm512_max_epi16(maxAbsHorz, maxAbsPerm); + + /// Swap each IQ pair in each lane (via 32b rotation) and compute max + maxAbsPerm = _mm512_rol_epi32(maxAbsHorz, BlockFloatCompander::k_numBitsIQ); + maxAbsHorz = _mm512_max_epi16(maxAbsHorz, maxAbsPerm); + + /// Insert values into maxAbs + /// Use sliding mask to insert wanted values into maxAbs + /// Pairs of values will be inserted and corrected outside of loop + static const auto k_select4RB = _mm512_set_epi32(28, 24, 20, 16, 28, 24, 20, 16, + 28, 24, 20, 16, 28, 24, 20, 16); + static constexpr uint16_t k_expMsk[k_numInputLoopIts] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; + maxAbs = _mm512_mask_permutex2var_epi32(maxAbs, k_expMsk[n], k_select4RB, maxAbsHorz); + } + + /// Convert to 32b by removing repeated values in maxAbs + static const auto k_upperWordMask = _mm512_set_epi64(0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF, + 0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF, + 0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF, + 0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF); + maxAbs = _mm512_and_epi64(maxAbs, k_upperWordMask); + + /// Compute exponent and store for later use + static constexpr int k_expTotShiftBits = 32 - BlockFloatCompander::k_iqWidth + 1; + const auto totShiftBits = _mm512_set1_epi32(k_expTotShiftBits); + const auto lzCount = _mm512_lzcnt_epi32(maxAbs); + const auto exponent = _mm512_sub_epi32(totShiftBits, lzCount); + int8_t storedExp[BlockFloatCompander::k_numRB] = {}; + static constexpr uint16_t k_expWriteMask = 0xFFFF; + _mm512_mask_cvtepi32_storeu_epi8(storedExp, k_expWriteMask, exponent); + + /// Shift 1RB by corresponding exponent and write exponent and data to output + /// Output data is packed exponent first followed by corresponding compressed RB +#pragma unroll(BlockFloatCompander::k_numRB) + for (int n = 0; n < BlockFloatCompander::k_numRB; ++n) + { + const __m512i* rawDataIn = reinterpret_cast(dataIn.dataExpanded + n * BlockFloatCompander::k_numREReal); + auto compData = _mm512_srai_epi16(*rawDataIn, storedExp[n]); + + dataOut->dataCompressed[n * (BlockFloatCompander::k_numREReal + 1)] = storedExp[n]; + static constexpr uint32_t k_rbMask = 0x00FFFFFF; // Write mask for 1RB (24 values) + _mm512_mask_cvtepi16_storeu_epi8(dataOut->dataCompressed + n * (BlockFloatCompander::k_numREReal + 1) + 1, k_rbMask, compData); + } +} + + +void +BlockFloatCompander::BlockFloatExpand_AVX512(const CompressedData& dataIn, ExpandedData* dataOut) +{ +#pragma unroll(BlockFloatCompander::k_numRB) + for (int n = 0; n < BlockFloatCompander::k_numRB; ++n) + { + /// Expand 1RB of data + const __m256i* rawDataIn = reinterpret_cast(dataIn.dataCompressed + n * (BlockFloatCompander::k_numREReal + 1) + 1); + const auto compData16 = _mm512_cvtepi8_epi16(*rawDataIn); + const auto expData = _mm512_slli_epi16(compData16, *(dataIn.dataCompressed + n * (BlockFloatCompander::k_numREReal + 1))); + + /// Write expanded data to output + static constexpr uint8_t k_rbMask64 = 0b00111111; // 64b write mask for 1RB (24 int16 values) + _mm512_mask_storeu_epi64(dataOut->dataExpanded + n * BlockFloatCompander::k_numREReal, k_rbMask64, expData); + } +} + + +void +BlockFloatCompander::BlockFloatCompress_Basic(const ExpandedData& dataIn, CompressedData* dataOut) +{ + int16_t maxAbs[BlockFloatCompander::k_numRB]; + for (int rb = 0; rb < BlockFloatCompander::k_numRB; ++rb) + { + // Find max abs value for this RB + maxAbs[rb] = 0; + for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re) + { + auto dataIdx = rb * BlockFloatCompander::k_numREReal + re; + int16_t dataAbs = (int16_t)std::abs(dataIn.dataExpanded[dataIdx]); + maxAbs[rb] = std::max(maxAbs[rb], dataAbs); + } + + // Find exponent + static constexpr int k_expTotShiftBits16 = 16 - BlockFloatCompander::k_iqWidth + 1; + auto thisExp = (int8_t)(k_expTotShiftBits16 - __lzcnt16(maxAbs[rb])); + auto expIdx = rb * (BlockFloatCompander::k_numREReal + 1); + dataOut->dataCompressed[expIdx] = thisExp; + + // ARS data by exponent + for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re) + { + auto dataIdxIn = rb * BlockFloatCompander::k_numREReal + re; + auto dataIdxOut = (expIdx + 1) + re; + dataOut->dataCompressed[dataIdxOut] = (int8_t)(dataIn.dataExpanded[dataIdxIn] >> thisExp); + } + } +} + + +void +BlockFloatCompander::BlockFloatExpand_Basic(const CompressedData& dataIn, ExpandedData* dataOut) +{ + // Expand data + for (int rb = 0; rb < BlockFloatCompander::k_numRB; ++rb) + { + for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re) + { + auto dataIdxOut = rb * BlockFloatCompander::k_numREReal + re; + auto expIdx = rb * (BlockFloatCompander::k_numREReal + 1); + auto dataIdxIn = (expIdx + 1) + re; + auto thisData = (int16_t)dataIn.dataCompressed[dataIdxIn]; + auto thisExp = (int16_t)dataIn.dataCompressed[expIdx]; + dataOut->dataExpanded[dataIdxOut] = (int16_t)(thisData << thisExp); + } + } +} diff --git a/fhi_lib/lib/src/xran_cp_api.c b/fhi_lib/lib/src/xran_cp_api.c index d42b22b..03becc9 100644 --- a/fhi_lib/lib/src/xran_cp_api.c +++ b/fhi_lib/lib/src/xran_cp_api.c @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the API functions to build Control Plane Messages * for XRAN Front Haul layer as defined in XRAN-FH.CUS.0-v02.01. @@ -32,153 +31,99 @@ #include "xran_common.h" #include "xran_transport.h" #include "xran_cp_api.h" -#include "xran_hash.h" #include "xran_printf.h" +/** + * This structure to store the section information of C-Plane + * in order to generate and parse corresponding U-Plane */ struct xran_sectioninfo_db { - uint32_t max_num; - uint32_t cur_index; -#if defined(XRAN_CP_USES_HASHTABLE) - struct rte_hash *hash; -#endif - struct xran_section_info *list; + uint32_t cur_index; /**< Current index to store fro this eAXC */ + struct xran_section_info list[XRAN_MAX_NUM_SECTIONS]; /**< The array of section information */ }; +static struct xran_sectioninfo_db sectiondb[XRAN_MAX_SECTIONDB_CTX][XRAN_DIR_MAX][XRAN_COMPONENT_CARRIERS_MAX][XRAN_MAX_ANTENNA_NR*2]; -static struct xran_sectioninfo_db *sectiondb[XRAN_DIR_MAX]; +/** + * @brief Initialize section database. + * Allocate required memory space to store section information. + * Each eAxC allocates dedicated storage and the entry size is the maximum number of sections. + * Total entry size : number of CC * number of antenna * max number of sections * 2(direction) + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_RESOURCE, if memory is not enough to allocate database area + */ int xran_cp_init_sectiondb(void *pHandle) { - int i, j, k; - uint32_t size; - uint16_t cid; - struct xran_sectioninfo_db *ptr; - uint8_t num_eAxc; + int ctx, dir, cc, ant; + for(ctx=0; ctx < XRAN_MAX_SECTIONDB_CTX; ctx++) + for(dir=0; dir < XRAN_DIR_MAX; dir++) + for(cc=0; cc < XRAN_COMPONENT_CARRIERS_MAX; cc++) + for(ant=0; ant < XRAN_MAX_ANTENNA_NR*2; ant++) + sectiondb[ctx][dir][cc][ant].cur_index = 0; -#if !defined(PRACH_USES_SHARED_PORT) - num_eAxc = xran_get_num_eAxc(pHandle) * 2; -#else - num_eAxc = xran_get_num_eAxc(pHandle); -#endif - - for(i=0; i < XRAN_DIR_MAX; i++) { - size = (xran_get_num_cc(pHandle) * num_eAxc * sizeof(struct xran_sectioninfo_db)); - print_log("Allocation Size for Section DB : %d (%dx%dx%ld)", size - , xran_get_num_cc(pHandle) - , num_eAxc - , sizeof(struct xran_sectioninfo_db)); - sectiondb[i] = malloc(size); - - if(sectiondb[i] == NULL) { - print_err("Allocation Failed for Section DB!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); - } - - for(j=0; j < xran_get_num_cc(pHandle); j++) { // CC - for(k=0; k < num_eAxc; k++) { // antenna - ptr = sectiondb[i] + num_eAxc*j + k; - - ptr->max_num = xran_get_max_sections(pHandle); - ptr->cur_index = 0; - - // allicate array to store section information - size = sizeof(struct xran_section_info)*xran_get_max_sections(pHandle); - print_log("Allocation Size for list : %d (%ldx%d)", size, - sizeof(struct xran_section_info), - xran_get_max_sections(pHandle)); - ptr->list = malloc(size); - if(ptr-> list == NULL) { - print_err("Allocation Failed for Section DB!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); - } - -#if defined(XRAN_CP_USES_HASHTABLE) - // Create hash table for section information - cid = rte_be_to_cpu_16(xran_compose_cid(xran_get_llscuid(pHandle), xran_get_sectorid(pHandle), j, k)); - print_log("Creating hash for %04X", cid); - ptr->hash = xran_section_init_hash(i, cid, xran_get_max_sections(pHandle)); -#endif - } - } - } - - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } +/** + * @brief Release and free section database + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @return + * XRAN_STATUS_SUCCESS on success + */ int xran_cp_free_sectiondb(void *pHandle) { - int i, j, k; - uint32_t size; - struct xran_sectioninfo_db *ptr; - uint8_t num_eAxc; - -#if !defined(PRACH_USES_SHARED_PORT) - num_eAxc = xran_get_num_eAxc(pHandle) * 2; -#else - num_eAxc = xran_get_num_eAxc(pHandle); -#endif - - for(i=0; i < XRAN_DIR_MAX; i++) { - for(j=0; j < xran_get_num_cc(pHandle); j++) { // CC - for(k=0; k < num_eAxc; k++) { // antenna - ptr = sectiondb[i] + num_eAxc*j + k; - -#if defined(XRAN_CP_USES_HASHTABLE) - xran_section_free_hash(ptr->hash); -#endif - if(ptr->list != NULL) - free(ptr->list); - else print_err("list is NULL"); - } - } - if(sectiondb[i] != NULL) - free(sectiondb[i]); - else print_err("sectiondb[%d] is NULL", i); - } - - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } -static struct xran_sectioninfo_db *xran_get_section_db(void *pHandle, - uint8_t dir, uint8_t cc_id, uint8_t ruport_id) +static inline struct xran_sectioninfo_db *xran_get_section_db(void *pHandle, + uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id) { struct xran_sectioninfo_db *ptr; - uint8_t num_eAxc; - if(unlikely(dir>=XRAN_DIR_MAX)) { + + if(unlikely(ctx_id >= XRAN_MAX_SECTIONDB_CTX)) { + print_err("Invalid Context id - %d", ctx_id); + return (NULL); + } + + if(unlikely(dir >= XRAN_DIR_MAX)) { print_err("Invalid direction - %d", dir); return (NULL); } - if(unlikely(cc_id >= xran_get_num_cc(pHandle))) { + if(unlikely(cc_id >= XRAN_COMPONENT_CARRIERS_MAX)) { print_err("Invalid CC id - %d", cc_id); return (NULL); } -#if !defined(PRACH_USES_SHARED_PORT) - num_eAxc = xran_get_num_eAxc(pHandle) * 2; -#else - num_eAxc = xran_get_num_eAxc(pHandle); -#endif - - if(unlikely(ruport_id >= num_eAxc)) { + if(unlikely(ruport_id >= XRAN_MAX_ANTENNA_NR*2)) { print_err("Invalid eAxC id - %d", ruport_id); return (NULL); } - ptr = sectiondb[dir] + xran_get_num_eAxc(pHandle)*cc_id + ruport_id; + ptr = §iondb[ctx_id][dir][cc_id][ruport_id]; return(ptr); } -static struct xran_section_info *xran_get_section_info(struct xran_sectioninfo_db *ptr, uint16_t index) +static inline struct xran_section_info *xran_get_section_info(struct xran_sectioninfo_db *ptr, uint16_t index) { if(unlikely(ptr == NULL)) return (NULL); - if(unlikely(ptr->max_num < index)) { + if(unlikely(index > XRAN_MAX_NUM_SECTIONS)) { print_err("Index is out of range - %d", index); return (NULL); } @@ -186,196 +131,650 @@ static struct xran_section_info *xran_get_section_info(struct xran_sectioninfo_d return(&(ptr->list[index])); } +/** + * @brief Add a section information of C-Plane to dabase. + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @param dir + * Direction of C-Plane message for the section to store + * @param cc_id + * CC ID of C-Plane message for the section to store + * @param ruport_id + * RU port ID of C-Plane message for the section to store + * @param ctx_id + * Context index for the section database + * @param info + * The information of this section to store + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARAM, if direction, CC ID or RU port ID is incorrect + * XRAN_STATUS_RESOURCE, if no more space to add on database + */ int xran_cp_add_section_info(void *pHandle, - uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, + uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id, struct xran_section_info *info) { struct xran_sectioninfo_db *ptr; struct xran_section_info *list; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) { - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } - if(unlikely(ptr->cur_index >= ptr->max_num)) { + if(unlikely(ptr->cur_index >= XRAN_MAX_NUM_SECTIONS)) { print_err("No more space to add section information!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); + return (XRAN_STATUS_RESOURCE); } list = xran_get_section_info(ptr, ptr->cur_index); rte_memcpy(list, info, sizeof(struct xran_section_info)); -#if defined(XRAN_CP_USES_HASHTABLE) - xran_section_add_hash(ptr->hash, info->id, ptr->cur_index); -#endif ptr->cur_index++; - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } int xran_cp_add_multisection_info(void *pHandle, - uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, - uint8_t num_sections, struct xran_section_gen_info *gen_info) + uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id, + struct xran_cp_gen_params *gen_info) { int i; + uint8_t dir, num_sections; struct xran_sectioninfo_db *ptr; struct xran_section_info *list; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + + dir = gen_info->dir; + num_sections = gen_info->numSections; + + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) { - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } - if(unlikely(ptr->cur_index >= (ptr->max_num+num_sections))) { + if(unlikely(ptr->cur_index+num_sections >= XRAN_MAX_NUM_SECTIONS)) { print_err("No more space to add section information!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); + return (XRAN_STATUS_RESOURCE); } list = xran_get_section_info(ptr, ptr->cur_index); for(i=0; ihash, gen_info[i].info.id, ptr->cur_index); -#endif + rte_memcpy(&list[i], &gen_info->sections[i].info, sizeof(struct xran_section_info)); ptr->cur_index++; } - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } +/** + * @brief Find a section information of C-Plane from dabase + * by given information + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @param dir + * The direction of the section to find + * @param cc_id + * The CC ID of the section to find + * @param ruport_id + * RU port ID of the section to find + * @param ctx_id + * Context index for the section database + * @param section_id + * The ID of section to find + * @return + * The pointer of section information if matched section is found + * NULL if failed to find matched section + */ struct xran_section_info *xran_cp_find_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, - uint16_t section_id) + uint8_t ctx_id, uint16_t section_id) { - int index; + int index, num_index; struct xran_sectioninfo_db *ptr; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) return (NULL); -#if defined(XRAN_CP_USES_HASHTABLE) - index = xran_section_lookup(ptr->hash, section_id); - if(unlikely(index > ptr->max_num)) { - print_err("Invalid index - %d", index); - return (NULL); - } - - if(index < 0) { - print_dbg("No section ID in the list - %d", section_id); - return (NULL); - } + if(ptr->cur_index > XRAN_MAX_NUM_SECTIONS) + num_index = XRAN_MAX_NUM_SECTIONS; + else + num_index = ptr->cur_index; - return (xran_get_section_info(ptr, index)); -#else - for(index=0; indexcur_index; index++) { + for(index=0; index < num_index; index++) { if(ptr->list[index].id == section_id) { - print_dbg("Found section info %04X", section_id); return (xran_get_section_info(ptr, index)); } } print_dbg("No section ID in the list - %d", section_id); return (NULL); -#endif - } +/** + * @brief Iterate each section information of C-Plane + * from the database of eAxC by given information + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @param dir + * The direction of the section to find + * @param cc_id + * The CC ID of the section to find + * @param ruport_id + * RU port ID of the section to find + * @param ctx_id + * Context index for the section database + * @param next + * The pointer to store the position of next entry + * @return + * The pointer of section information in the list + * NULL if reached at the end of the list + */ struct xran_section_info *xran_cp_iterate_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, - uint8_t subframe_id, uint8_t slot_id, uint32_t *next) + uint8_t ctx_id, uint32_t *next) { int index; struct xran_sectioninfo_db *ptr; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) return (NULL); -#if defined(XRAN_CP_USES_HASHTABLE) - index = xran_section_iterate(ptr->hash, next); - if(unlikely(index > ptr->max_num)) { - print_err("Invalid index - %d", index); - return (NULL); - } - - if(index < 0) { - print_dbg("No section ID in the list - %d", section_id); - return (NULL); - } - - return (xran_get_section_info(ptr, index)); -#else index = *next; if(*next < ptr->cur_index) { (*next)++; return (xran_get_section_info(ptr, index)); } - else + else { print_dbg("No more sections in the list"); - - return (NULL); -#endif + return (NULL); + } } -int xran_cp_getsize_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id) +/** + * @brief Get the size of stored entries + * for the database of eAxC by given information + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @param dir + * The direction of the section to find + * @param cc_id + * The CC ID of the section to find + * @param ruport_id + * RU port ID of the section to find + * @param ctx_id + * Context index for the section database + * @return + * The size of stored entries + * -1 if failed to find matched database + */ +int xran_cp_getsize_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id) { int i, index; struct xran_sectioninfo_db *ptr; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) return (-1); return (ptr->cur_index); } -int xran_cp_reset_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id) +/** + * @brief Reset a database of eAxC by given information + * + * @ingroup xran_cp_pkt + * + * @param pHandle + * handle for xRAN interface, currently not being used + * @param dir + * The direction of the section to find + * @param cc_id + * The CC ID of the section to find + * @param ruport_id + * RU port ID of the section to find + * @param ctx_id + * Context index for the section database + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if failed to find matched database + */ +int xran_cp_reset_section_info(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ruport_id, uint8_t ctx_id) { struct xran_sectioninfo_db *ptr; - ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id); + ptr = xran_get_section_db(pHandle, dir, cc_id, ruport_id, ctx_id); if(unlikely(ptr == NULL)) { - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } ptr->cur_index = 0; -#if defined(XRAN_CP_USES_HASHTABLE) - xran_section_reset_hash(ptr->hash); -#endif - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } + int xran_dump_sectiondb(void) { // TODO: return (0); } - // Cyclic Prefix Length 5.4.4.14 -// CP_length = cpLength * Ts * 2^u, Ts = 1/30.72MHz, if u is N/A, it shall be zero +// CP_length = cpLength * Ts, Ts = 1/30.72MHz +// i.e cpLength = CP_length / Ts ? #define CPLEN_TS (30720000) -inline uint16_t xran_get_cplength(int cpLength, int uval) // uval = -1 for N/A +inline uint16_t xran_get_cplength(int CP_length) { - return ((cpLength * ((uval<0)?0:(2<>1); + return (freqOffset); +} + + +static int xran_prepare_sectionext_1(struct rte_mbuf *mbuf, + struct xran_sectionext1_info *params, int last_flag) +{ + struct xran_cp_radioapp_section_ext1 *ext1; + uint8_t *data; + int parm_size, iq_size; + int total_len; + static const uint8_t zeropad[XRAN_SECTIONEXT_ALIGN] = { 0, 0, 0, 0 }; + + + total_len = 0; + + parm_size = sizeof(struct xran_cp_radioapp_section_ext1); + ext1 = (struct xran_cp_radioapp_section_ext1 *)rte_pktmbuf_append(mbuf, parm_size); + if(ext1 == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + + total_len += parm_size; + + ext1->extType = XRAN_CP_SECTIONEXTCMD_1; + ext1->ef = last_flag; + ext1->bfwCompMeth = params->bfwCompMeth; + ext1->bfwIqWidth = XRAN_CONVERT_BFWIQWIDTH(params->bfwiqWidth); + + switch(params->bfwCompMeth) { + case XRAN_BFWCOMPMETHOD_BLKFLOAT: + parm_size = 1; + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + total_len += parm_size; + *data = (params->bfwCompParam.exponent & 0x0f); + break; + + case XRAN_BFWCOMPMETHOD_BLKSCALE: + parm_size = 1; + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + total_len += parm_size; + *data = params->bfwCompParam.blockScaler; + break; + + case XRAN_BFWCOMPMETHOD_ULAW: + parm_size = 1; + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + total_len += parm_size; + *data = params->bfwCompParam.compBitWidthShift; + break; + + case XRAN_BFWCOMPMETHOD_BEAMSPACE: +#if 0 + parm_size = ceil(params->bfwNumber/8)*8; +#else + parm_size = params->bfwNumber>>3; + if(params->bfwNumber%8) parm_size++; + parm_size *= 8; +#endif + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + rte_memcpy(data, params->bfwCompParam.activeBeamspaceCoeffMask, parm_size); + total_len += parm_size; + break; + + case XRAN_BFWCOMPMETHOD_NONE: + default: + parm_size = 0; + } + + iq_size = params->bfwNumber * params->bfwiqWidth * 2; +#if 0 + parm_size = ceil(iq_size/8); +#else + parm_size = iq_size>>3; + if(iq_size%8) parm_size++; +#endif + + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + rte_memcpy(data, params->bfwIQ, parm_size); + + total_len += parm_size; + parm_size = total_len % XRAN_SECTIONEXT_ALIGN; + if(parm_size) { + parm_size = XRAN_SECTIONEXT_ALIGN - parm_size; + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 1"); + return (XRAN_STATUS_RESOURCE); + } + rte_memcpy(data, zeropad, parm_size); + total_len += parm_size; + } + + ext1->extLen = total_len / XRAN_SECTIONEXT_ALIGN; + + return (total_len); +} + +static int xran_prepare_sectionext_2(struct rte_mbuf *mbuf, + struct xran_sectionext2_info *params, int last_flag) +{ + struct xran_cp_radioapp_section_ext2 *ext2; + uint8_t *data; + int total_len; + int parm_size; + uint32_t val, shift_val; + int val_size; + + + total_len = 0; + + parm_size = sizeof(struct xran_cp_radioapp_section_ext2); + ext2 = (struct xran_cp_radioapp_section_ext2 *)rte_pktmbuf_append(mbuf, parm_size); + if(ext2 == NULL) { + print_err("Fail to allocate the space for section extension 2"); + return (XRAN_STATUS_RESOURCE); + } + total_len += parm_size; + + ext2->extType = XRAN_CP_SECTIONEXTCMD_2; + ext2->ef = last_flag; + ext2->bfZe3ddWidth = params->bfZe3ddWidth; + ext2->bfAz3ddWidth = params->bfAz3ddWidth; + ext2->bfZePtWidth = params->bfZePtWidth; + ext2->bfAzPtWidth = params->bfAzPtWidth; + ext2->bfaCompResv0 = 0; + ext2->bfaCompResv1 = 0; + + val = 0; + shift_val = 0; + if(params->bfAzPtWidth) { + val += params->bfAzPt; + shift_val += 8 - (params->bfAzPtWidth+1); + } + else + shift_val += 8; + + if(params->bfZePtWidth) { + val = val << (params->bfZePtWidth+1); + val += params->bfZePt; + shift_val += 8 - (params->bfZePtWidth+1); + } + else + shift_val += 8; + + if(params->bfAz3ddWidth) { + val = val << (params->bfAz3ddWidth+1); + val += params->bfAz3dd; + shift_val += 8 - (params->bfAz3ddWidth+1); + } + else + shift_val += 8; + + if(params->bfZe3ddWidth) { + val = val << (params->bfZe3ddWidth+1); + val += params->bfZe3dd; + shift_val += 8 - (params->bfZe3ddWidth+1); + } + else + shift_val += 8; + + if(val) { + val = val << shift_val; + val = rte_cpu_to_be_32(val); + } + + val_size = 4 - (shift_val/8); /* ceil(total bit/8) */ + parm_size = val_size + 1; /* additional 1 byte for bfxxSI */ + + data = (uint8_t *)rte_pktmbuf_append(mbuf, parm_size); + if(data == NULL) { + print_err("Fail to allocate the space for section extension 2"); + return (XRAN_STATUS_RESOURCE); + } + total_len += parm_size; + + rte_memcpy(data, &val, val_size); + data += val_size; + *data = ((params->bfAzSI) << 3) + (params->bfZeSI); + + ext2->extLen = total_len / XRAN_SECTIONEXT_ALIGN; + *(uint32_t *)ext2 = rte_cpu_to_be_32(*(uint32_t *)ext2); + + return (total_len); +} + +static int xran_prepare_sectionext_4(struct rte_mbuf *mbuf, + struct xran_sectionext4_info *params, int last_flag) +{ + struct xran_cp_radioapp_section_ext4 *ext4; + int parm_size; + int total_len; + int ret; + + + total_len = 0; + + parm_size = sizeof(struct xran_cp_radioapp_section_ext4); + ext4 = (struct xran_cp_radioapp_section_ext4 *)rte_pktmbuf_append(mbuf, parm_size); + if(ext4 == NULL) { + print_err("Fail to allocate the space for section extension 4"); + return(XRAN_STATUS_RESOURCE); + } + else { + total_len += parm_size; + + ext4->extType = XRAN_CP_SECTIONEXTCMD_4; + ext4->ef = last_flag; + ext4->modCompScaler = params->modCompScaler; + ext4->csf = params->csf?1:0; + ext4->extLen = 1; + + *(uint32_t *)ext4 = rte_cpu_to_be_32(*(uint32_t*)ext4); + } + + return (total_len); +} + +static int xran_prepare_sectionext_5(struct rte_mbuf *mbuf, + struct xran_sectionext5_info *params, int last_flag) +{ + struct xran_cp_radioapp_section_ext5_1 *ext5_1; + struct xran_cp_radioapp_section_ext5_2 *ext5_2; + int parm_size; + int total_len; + uint32_t *data; + + + total_len = 0; + + if(params->num_sets == 1) { + parm_size = sizeof(struct xran_cp_radioapp_section_ext5_1); + ext5_1 = (struct xran_cp_radioapp_section_ext5_1 *)rte_pktmbuf_append(mbuf, parm_size); + if(ext5_1 == NULL) { + print_err("Fail to allocate the space for section extension 5-1"); + return (XRAN_STATUS_RESOURCE); + } + else { + total_len += parm_size; + + ext5_1->extType = XRAN_CP_SECTIONEXTCMD_5; + ext5_1->ef = last_flag; + + ext5_1->mcScaleOffset = params->mc[0].mcScaleOffset; + ext5_1->csf = params->mc[0].csf; + ext5_1->mcScaleReMask = params->mc[0].mcScaleReMask; + ext5_1->reserved = 0; + + ext5_1->extLen = 2; + + *(uint64_t *)ext5_1 = rte_cpu_to_be_64(*(uint64_t *)ext5_1); + } + } + else if(params->num_sets == 2) { + parm_size = sizeof(struct xran_cp_radioapp_section_ext5_2); + ext5_2 = (struct xran_cp_radioapp_section_ext5_2 *)rte_pktmbuf_append(mbuf, parm_size); + if(ext5_2 == NULL) { + print_err("Fail to allocate the space for section extension 5-2"); + return (XRAN_STATUS_RESOURCE); + } + else { + total_len += parm_size; + + ext5_2->extType = XRAN_CP_SECTIONEXTCMD_5; + ext5_2->ef = last_flag; + + ext5_2->mcScaleOffset1 = params->mc[0].mcScaleOffset; + ext5_2->csf1 = params->mc[0].csf; + ext5_2->mcScaleReMask1 = params->mc[0].mcScaleReMask; + ext5_2->mcScaleOffset2 = params->mc[1].mcScaleOffset; + ext5_2->csf2 = params->mc[1].csf; + ext5_2->mcScaleReMask2 = params->mc[1].mcScaleReMask; + + ext5_2->reserved0 = 0; + ext5_2->reserved1 = 0; + + ext5_2->extLen = 3; + + *(uint64_t *)ext5_2 = rte_cpu_to_be_64(*(uint64_t *)ext5_2); + data = (uint32_t *)((uint8_t *)ext5_2 + 8); + *data = rte_cpu_to_be_32(*data); + } + } + else { + print_err("Invalid number of scalar values - %d", params->num_sets); + return (XRAN_STATUS_INVALID_PARAM); + } + + return (total_len); +} + +/** + * @brief add section extension to C-Plane packet + * + * @param mbuf + * A pointer to the packet buffer + * @param params + * A porinter to the information to generate a C-Plane packet + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM + * XRAN_STATUS_RESOURCE if failed to allocate the space to packet buffer + */ +int xran_append_section_extensions(struct rte_mbuf *mbuf, struct xran_section_gen_info *params) +{ + int i, ret; + uint32_t totalen; + int last_flag; + int ext_size; + + + if(unlikely(params->exDataSize >= XRAN_MAX_NUM_EXTENSIONS)) { + print_err("Invalid total number of extensions - %d", params->exDataSize); + return (XRAN_STATUS_INVALID_PARAM); + } + + totalen = 0; + + + ret = XRAN_STATUS_SUCCESS; + + for(i=0; i < params->exDataSize; i++) { + if(params->exData[i].data == NULL) { + print_err("Invalid parameter - extension data %d is NULL", i); + ret = XRAN_STATUS_INVALID_PARAM; + continue; + } + +// params->exData[].len + last_flag = ((params->exDataSize - i)==1)?0:1; + switch(params->exData[i].type) { + case XRAN_CP_SECTIONEXTCMD_1: + ext_size = xran_prepare_sectionext_1(mbuf, params->exData[i].data, last_flag); + break; + case XRAN_CP_SECTIONEXTCMD_2: + ext_size = xran_prepare_sectionext_2(mbuf, params->exData[i].data, last_flag); + break; + case XRAN_CP_SECTIONEXTCMD_4: + ext_size = xran_prepare_sectionext_4(mbuf, params->exData[i].data, last_flag); + break; + case XRAN_CP_SECTIONEXTCMD_5: + ext_size = xran_prepare_sectionext_5(mbuf, params->exData[i].data, last_flag); + break; + + case XRAN_CP_SECTIONEXTCMD_0: + case XRAN_CP_SECTIONEXTCMD_3: + default: + print_err("Extension Type %d is not supported!", params->exData[i].type); + ret = XRAN_STATUS_INVALID_PARAM; + ext_size = 0; + } + + if(ext_size == XRAN_STATUS_RESOURCE) { + break; + } + + totalen += ext_size; + } + + return (totalen); } @@ -387,7 +786,8 @@ inline int32_t xran_get_freqoffset(int freqOffset, int scs) * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if the number of symbol is invalid */ static int xran_prepare_section0( struct xran_cp_radioapp_section0 *section, @@ -396,15 +796,15 @@ static int xran_prepare_section0( #if (XRAN_STRICT_PARM_CHECK) if(unlikely(params->info.numSymbol > XRAN_SYMBOLNUMBER_MAX)) { print_err("Invalid number of Symbols - %d", params->info.numSymbol); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } #endif - section->hdr.sectionId = params->info.id; - section->hdr.rb = params->info.rb; - section->hdr.symInc = params->info.symInc; - section->hdr.startPrbc = params->info.startPrbc; - section->hdr.numPrbc = params->info.numPrbc; + section->hdr.sectionId = params->info.id; + section->hdr.rb = params->info.rb; + section->hdr.symInc = params->info.symInc; + section->hdr.startPrbc = params->info.startPrbc; + section->hdr.numPrbc = XRAN_CONVERT_NUMPRBC(params->info.numPrbc); section->hdr.u.s0.reMask = params->info.reMask; section->hdr.u.s0.numSymbol = params->info.numSymbol; @@ -413,7 +813,7 @@ static int xran_prepare_section0( // for network byte order *((uint64_t *)section) = rte_cpu_to_be_64(*((uint64_t *)section)); - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** * @brief Fill the section header of type 0 in C-Plane packet @@ -423,7 +823,7 @@ static int xran_prepare_section0( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS always */ static int xran_prepare_section0_hdr( struct xran_cp_radioapp_section0_header *s0hdr, @@ -436,7 +836,7 @@ static int xran_prepare_section0_hdr( s0hdr->cpLength = rte_cpu_to_be_16(params->hdr.cpLength); s0hdr->reserved = 0; - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** @@ -448,7 +848,8 @@ static int xran_prepare_section0_hdr( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if the number of symbol is invalid */ static int xran_prepare_section1( struct xran_cp_radioapp_section1 *section, @@ -457,7 +858,7 @@ static int xran_prepare_section1( #if (XRAN_STRICT_PARM_CHECK) if(unlikely(params->info.numSymbol > XRAN_SYMBOLNUMBER_MAX)) { print_err("Invalid number of Symbols - %d", params->info.numSymbol); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } #endif @@ -465,25 +866,18 @@ static int xran_prepare_section1( section->hdr.rb = params->info.rb; section->hdr.symInc = params->info.symInc; section->hdr.startPrbc = params->info.startPrbc; - section->hdr.numPrbc = params->info.numPrbc; + section->hdr.numPrbc = XRAN_CONVERT_NUMPRBC(params->info.numPrbc); section->hdr.u.s1.reMask = params->info.reMask; section->hdr.u.s1.numSymbol = params->info.numSymbol; section->hdr.u.s1.beamId = params->info.beamId; - if(params->info.ef) { - // TODO: need to handle extension - print_err("Extension is not supported!"); - section->hdr.u.s1.ef = 0; -// section->hdr.u.s1.ef = params->info.ef; - } - else - section->hdr.u.s1.ef = 0; + section->hdr.u.s1.ef = params->info.ef; // for network byte order *((uint64_t *)section) = rte_cpu_to_be_64(*((uint64_t *)section)); - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** * @brief Fill the section header of type 1 in C-Plane packet @@ -493,7 +887,7 @@ static int xran_prepare_section1( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS always */ static int xran_prepare_section1_hdr( struct xran_cp_radioapp_section1_header *s1hdr, @@ -503,7 +897,7 @@ static int xran_prepare_section1_hdr( s1hdr->udComp.udCompMeth = params->hdr.compMeth; s1hdr->reserved = 0; - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** @@ -515,7 +909,8 @@ static int xran_prepare_section1_hdr( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if the number of symbol is invalid */ static int xran_prepare_section3( struct xran_cp_radioapp_section3 *section, @@ -524,7 +919,7 @@ static int xran_prepare_section3( #if (XRAN_STRICT_PARM_CHECK) if(unlikely(params->info.numSymbol > XRAN_SYMBOLNUMBER_MAX)) { print_err("Invalid number of Symbols - %d", params->info.numSymbol); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } #endif @@ -532,7 +927,7 @@ static int xran_prepare_section3( section->hdr.rb = params->info.rb; section->hdr.symInc = params->info.symInc; section->hdr.startPrbc = params->info.startPrbc; - section->hdr.numPrbc = params->info.numPrbc; + section->hdr.numPrbc = XRAN_CONVERT_NUMPRBC(params->info.numPrbc); section->hdr.u.s3.reMask = params->info.reMask; section->hdr.u.s3.numSymbol = params->info.numSymbol; @@ -541,19 +936,12 @@ static int xran_prepare_section3( section->freqOffset = rte_cpu_to_be_32(params->info.freqOffset)>>8; section->reserved = 0; - if(params->info.ef) { - // TODO: need to handle extension - print_err("Extension is not supported!"); - section->hdr.u.s3.ef = 0; -// section->hdr.u.s3.ef = params->info.ef; - } - else - section->hdr.u.s3.ef = 0; + section->hdr.u.s3.ef = params->info.ef; // for network byte order (header, 8 bytes) *((uint64_t *)section) = rte_cpu_to_be_64(*((uint64_t *)section)); - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** * @brief Fill the section header of type 3 in C-Plane packet @@ -563,7 +951,7 @@ static int xran_prepare_section3( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS always */ static int xran_prepare_section3_hdr( struct xran_cp_radioapp_section3_header *s3hdr, @@ -577,7 +965,7 @@ static int xran_prepare_section3_hdr( s3hdr->udComp.udIqWidth = params->hdr.iqWidth; s3hdr->udComp.udCompMeth = params->hdr.compMeth; - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** @@ -589,11 +977,13 @@ static int xran_prepare_section3_hdr( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if section type is not 1 or 3, or handler is NULL + * XRAN_STATUS_RESOURCE if failed to allocate the space to packet buffer */ int xran_append_control_section(struct rte_mbuf *mbuf, struct xran_cp_gen_params *params) { - int i, ret; + int i, ret, ext_flag; uint32_t totalen; void *section; int section_size; @@ -624,27 +1014,33 @@ int xran_append_control_section(struct rte_mbuf *mbuf, struct xran_cp_gen_params section_size = 0; xran_prepare_section_func = NULL; print_err("Section Type %d is not supported!", params->sectionType); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } if(unlikely(xran_prepare_section_func == NULL)) { print_err("Section Type %d is not supported!", params->sectionType); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } for(i=0; inumSections; i++) { section = rte_pktmbuf_append(mbuf, section_size); if(section == NULL) { print_err("Fail to allocate the space for section[%d]!", i); - return (-XRAN_ERRCODE_OUTOFMEMORY); - } - - if(unlikely(xran_prepare_section_func((void *)section, - (void *)¶ms->sections[i]) < 0)) { - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_RESOURCE); } + ret = xran_prepare_section_func((void *)section, + (void *)¶ms->sections[i]); + if(ret < 0) + return (ret); totalen += section_size; + + if(params->sections[i].info.ef) { + ret = xran_append_section_extensions(mbuf, ¶ms->sections[i]); + if(ret < 0) + return (ret); + totalen += ret; + } } return (totalen); @@ -658,7 +1054,8 @@ int xran_append_control_section(struct rte_mbuf *mbuf, struct xran_cp_gen_params * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PARM if direction, slot index or symbold index is invalid */ static inline int xran_prepare_radioapp_common_header( struct xran_cp_radioapp_common_header *apphdr, @@ -668,15 +1065,15 @@ static inline int xran_prepare_radioapp_common_header( #if (XRAN_STRICT_PARM_CHECK) if(unlikely(params->dir != XRAN_DIR_DL && params->dir != XRAN_DIR_UL)) { print_err("Invalid direction!"); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } if(unlikely(params->hdr.slotId > XRAN_SLOTID_MAX)) { print_err("Invalid Slot ID!"); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } if(unlikely(params->hdr.startSymId > XRAN_SYMBOLNUMBER_MAX)) { print_err("Invalid Symbol ID!"); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } #endif @@ -690,10 +1087,10 @@ static inline int xran_prepare_radioapp_common_header( apphdr->numOfSections = params->numSections; apphdr->sectionType = params->sectionType; - // radio app header has common parts of 4bytes for all section types + /* radio app header has common parts of 4bytes for all section types */ *((uint32_t *)apphdr) = rte_cpu_to_be_32(*((uint32_t *)apphdr)); - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } /** @@ -704,7 +1101,9 @@ static inline int xran_prepare_radioapp_common_header( * @param params * A porinter to the information to generate a C-Plane packet * @return - * 0 on success; non zero on failure + * The length of added section (>0) on success + * XRAN_STATUS_INVALID_PARM if section type is invalid, or handler is NULL + * XRAN_STATUS_RESOURCE if failed to allocate the space to packet buffer */ int xran_append_radioapp_header(struct rte_mbuf *mbuf, struct xran_cp_gen_params *params) { @@ -717,40 +1116,40 @@ int xran_append_radioapp_header(struct rte_mbuf *mbuf, struct xran_cp_gen_params #if (XRAN_STRICT_PARM_CHECK) if(unlikely(params->sectionType >= XRAN_CP_SECTIONTYPE_MAX)) { print_err("Invalid Section Type - %d", params->sectionType); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } #endif switch(params->sectionType) { - case XRAN_CP_SECTIONTYPE_0: // Unused RB or Symbols in DL or UL, not supportted + case XRAN_CP_SECTIONTYPE_0: /* Unused RB or Symbols in DL or UL, not supportted */ xran_prepare_radioapp_section_hdr_func = (int (*)(void *, void*))xran_prepare_section0_hdr; totalen = sizeof(struct xran_cp_radioapp_section0_header); break; - case XRAN_CP_SECTIONTYPE_1: // Most DL/UL Radio Channels + case XRAN_CP_SECTIONTYPE_1: /* Most DL/UL Radio Channels */ xran_prepare_radioapp_section_hdr_func = (int (*)(void *, void*))xran_prepare_section1_hdr; totalen = sizeof(struct xran_cp_radioapp_section1_header); break; - case XRAN_CP_SECTIONTYPE_3: // PRACH and Mixed-numerology Channels + case XRAN_CP_SECTIONTYPE_3: /* PRACH and Mixed-numerology Channels */ xran_prepare_radioapp_section_hdr_func = (int (*)(void *, void*))xran_prepare_section3_hdr; totalen = sizeof(struct xran_cp_radioapp_section3_header); break; - case XRAN_CP_SECTIONTYPE_5: // UE scheduling information, not supported - case XRAN_CP_SECTIONTYPE_6: // Channel Information, not supported - case XRAN_CP_SECTIONTYPE_7: // LAA, not supported + case XRAN_CP_SECTIONTYPE_5: /* UE scheduling information, not supported */ + case XRAN_CP_SECTIONTYPE_6: /* Channel Information, not supported */ + case XRAN_CP_SECTIONTYPE_7: /* LAA, not supported */ default: print_err("Section Type %d is not supported!", params->sectionType); xran_prepare_radioapp_section_hdr_func = NULL; totalen = 0; - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } apphdr = (struct xran_cp_radioapp_common_header *)rte_pktmbuf_append(mbuf, totalen); if(unlikely(apphdr == NULL)) { print_err("Fail to reserve the space for radio application header!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); + return (XRAN_STATUS_RESOURCE); } ret = xran_prepare_radioapp_common_header(apphdr, params); @@ -759,11 +1158,11 @@ int xran_append_radioapp_header(struct rte_mbuf *mbuf, struct xran_cp_gen_params } if(likely(xran_prepare_radioapp_section_hdr_func)) { - xran_prepare_radioapp_section_hdr_func(apphdr, params); + totalen += xran_prepare_radioapp_section_hdr_func(apphdr, params); } else { print_err("xran_prepare_radioapp_section_hdr_func is NULL!"); - return (-XRAN_ERRCODE_INVALIDPARAM); + return (XRAN_STATUS_INVALID_PARAM); } return (totalen); @@ -786,7 +1185,9 @@ int xran_append_radioapp_header(struct rte_mbuf *mbuf, struct xran_cp_gen_params * @param seq_id * Sequence ID for this C-Plane message * @return - * 0 on success; non zero on failure + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_RESOURCE if failed to allocate the space to packet buffer + * XRAN_STATUS_INVALID_PARM if section type is invalid */ int xran_prepare_ctrl_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *params, @@ -798,24 +1199,7 @@ int xran_prepare_ctrl_pkt(struct rte_mbuf *mbuf, struct xran_ecpri_hdr *ecpri_hdr; - ecpri_hdr = (struct xran_ecpri_hdr *)rte_pktmbuf_append(mbuf, sizeof(struct xran_ecpri_hdr)); - if(unlikely(ecpri_hdr == NULL)) { - print_err("Fail to allocate the space for eCPRI hedaer!"); - return (-XRAN_ERRCODE_OUTOFMEMORY); - } - - ecpri_hdr->ecpri_ver = XRAN_ECPRI_VER; - ecpri_hdr->ecpri_resv = 0; // should be zero - ecpri_hdr->ecpri_concat = 0; - ecpri_hdr->ecpri_mesg_type = ECPRI_RT_CONTROL_DATA; - ecpri_hdr->ecpri_xtc_id = xran_compose_cid(0, 0, CC_ID, Ant_ID); - ecpri_hdr->ecpri_seq_id.seq_id = seq_id; - - /* TODO: Transport layer fragmentation is not supported */ - ecpri_hdr->ecpri_seq_id.sub_seq_id = 0; - ecpri_hdr->ecpri_seq_id.e_bit = 1; - - payloadlen = 0; + payloadlen = xran_build_ecpri_hdr(mbuf, CC_ID, Ant_ID, seq_id, &ecpri_hdr); ret = xran_append_radioapp_header(mbuf, params); if(ret < 0) { @@ -829,15 +1213,34 @@ int xran_prepare_ctrl_pkt(struct rte_mbuf *mbuf, } payloadlen += ret; -// printf("Total Payload length = %d\n", payloadlen); - ecpri_hdr->ecpri_payl_size = rte_cpu_to_be_16(payloadlen); + /* set payload length */ + ecpri_hdr->cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(payloadlen); - return (XRAN_ERRCODE_OK); + return (XRAN_STATUS_SUCCESS); } + /////////////////////////////////////// -// for Debug -int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) +// for RU emulation +/** + * @brief Parse a C-Plane packet (for RU emulation) + * Transport layer fragmentation is not supported. + * + * @ingroup xran_cp_pkt + * + * @param mbuf + * The pointer of the packet buffer to be parsed + * @param params + * The pointer of structure to store the information of parsed packet + * @param eaxc + * The pointer of sturcture to store the decomposed information of ecpriRtcid/ecpriPcid + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PACKET if failed to parse the packet + */ +int xran_parse_cp_pkt(struct rte_mbuf *mbuf, + struct xran_cp_gen_params *result, + struct xran_recv_packet_info *pkt_info) { struct xran_ecpri_hdr *ecpri_hdr; struct xran_cp_radioapp_common_header *apphdr; @@ -845,48 +1248,22 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) int extlen; - ret = 0; - ecpri_hdr = rte_pktmbuf_mtod(mbuf, void *); - if(ecpri_hdr == NULL) { - print_err("Invalid packet - eCPRI hedaer!"); - return (-XRAN_ERRCODE_INVALIDPACKET); - } - - /* Process eCPRI header. */ - if(ecpri_hdr->ecpri_ver != XRAN_ECPRI_VER) { - print_err("Invalid eCPRI version - %d", ecpri_hdr->ecpri_ver); - ret = -XRAN_ERRCODE_INVALIDPACKET; - } - - if(ecpri_hdr->ecpri_resv != 0) { - print_err("Invalid reserved field - %d", ecpri_hdr->ecpri_resv); - ret = -XRAN_ERRCODE_INVALIDPACKET; - } - - if(ecpri_hdr->ecpri_mesg_type != ECPRI_RT_CONTROL_DATA) { - print_err("Not C-Plane Message - %d", ecpri_hdr->ecpri_mesg_type); - ret = -XRAN_ERRCODE_INVALIDPACKET; - } -#if 0 - printf("[CPlane] [%04X:%03d-%3d-%d] len=%5d\n", - rte_be_to_cpu_16(ecpri_hdr->ecpri_xtc_id), - ecpri_hdr->ecpri_seq_id.seq_id, ecpri_hdr->ecpri_seq_id.sub_seq_id, - ecpri_hdr->ecpri_seq_id.e_bit, - rte_be_to_cpu_16(ecpri_hdr->ecpri_payl_size)); -#endif + ret = xran_parse_ecpri_hdr(mbuf, &ecpri_hdr, pkt_info); + if(ret < 0 && ecpri_hdr == NULL) + return (XRAN_STATUS_INVALID_PACKET); /* Process radio header. */ apphdr = (void *)rte_pktmbuf_adj(mbuf, sizeof(struct xran_ecpri_hdr)); if(apphdr == NULL) { print_err("Invalid packet - radio app hedaer!"); - return (-XRAN_ERRCODE_INVALIDPACKET); + return (XRAN_STATUS_INVALID_PACKET); } *((uint32_t *)apphdr) = rte_cpu_to_be_32(*((uint32_t *)apphdr)); if(apphdr->payloadVer != XRAN_PAYLOAD_VER) { print_err("Invalid Payload version - %d", apphdr->payloadVer); - ret = -XRAN_ERRCODE_INVALIDPACKET; + ret = XRAN_STATUS_INVALID_PACKET; } result->dir = apphdr->dataDirection; @@ -898,6 +1275,19 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) result->sectionType = apphdr->sectionType; result->numSections = apphdr->numOfSections; +#if 0 + printf("[CP%5d] eAxC[%d:%d:%02d:%02d] %s seq[%03d-%03d-%d] sec[%d-%d] frame[%3d-%2d-%2d] sym%02d\n", + pkt_info->payload_len, + pkt_info->eaxc.cuPortId, pkt_info->eaxc.bandSectorId, + pkt_info->eaxc.ccId, pkt_info->eaxc.ruPortId, + result->dir?"DL":"UL", + pkt_info->seq_id, pkt_info->subseq_id, pkt_info->ebit, + result->sectionType, result->numSections, + result->hdr.frameId, result->hdr.subframeId, result->hdr.slotId, + result->hdr.startSymId + ); +#endif + switch(apphdr->sectionType) { case XRAN_CP_SECTIONTYPE_0: // Unused RB or Symbols in DL or UL, not supportted { @@ -911,11 +1301,11 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) result->hdr.timeOffset = hdr->frameStructure.uScs; result->hdr.cpLength = rte_be_to_cpu_16(hdr->cpLength); //hdr->reserved; /* should be zero */ - + section = (void *)rte_pktmbuf_adj(mbuf, sizeof(struct xran_cp_radioapp_section0_header)); if(section == NULL) { print_err("Invalid packet 0 - radio app hedaer!"); - return (-XRAN_ERRCODE_INVALIDPACKET); + return (XRAN_STATUS_INVALID_PACKET); } for(i=0; inumSections; i++) { *((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section)); @@ -935,7 +1325,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) print_err("Invalid packet 0 - number of section [%d:%d]!", result->numSections, i); result->numSections = i; - ret = (-XRAN_ERRCODE_INVALIDPACKET); + ret = XRAN_STATUS_INVALID_PACKET; break; } } @@ -955,7 +1345,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) section = (void *)rte_pktmbuf_adj(mbuf, sizeof(struct xran_cp_radioapp_section1_header)); if(section == NULL) { print_err("Invalid packet 1 - radio app hedaer!"); - return (-XRAN_ERRCODE_INVALIDPACKET); + return (XRAN_STATUS_INVALID_PACKET); } for(i=0; inumSections; i++) { @@ -984,7 +1374,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) print_err("Invalid packet 1 - number of section [%d:%d]!", result->numSections, i); result->numSections = i; - ret = (-XRAN_ERRCODE_INVALIDPACKET); + ret = XRAN_STATUS_INVALID_PACKET; break; } } @@ -1008,7 +1398,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) section = (void *)rte_pktmbuf_adj(mbuf, sizeof(struct xran_cp_radioapp_section3_header)); if(section == NULL) { print_err("Invalid packet 3 - radio app hedaer!"); - return (-XRAN_ERRCODE_INVALIDPACKET); + return (XRAN_STATUS_INVALID_PACKET); } for(i=0; inumSections; i++) { @@ -1028,7 +1418,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) if(section->reserved) { print_err("Invalid packet 3 - section[%d:%d]", i, section->reserved); - ret = -XRAN_ERRCODE_INVALIDPACKET; + ret = XRAN_STATUS_INVALID_PACKET; } if(section->hdr.u.s3.ef) { @@ -1043,7 +1433,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) print_err("Invalid packet 3 - number of section [%d:%d]!", result->numSections, i); result->numSections = i; - ret = (-XRAN_ERRCODE_INVALIDPACKET); + ret = XRAN_STATUS_INVALID_PACKET; break; } } @@ -1054,7 +1444,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) case XRAN_CP_SECTIONTYPE_6: // Channel Information, not supported case XRAN_CP_SECTIONTYPE_7: // LAA, not supported default: - ret = -XRAN_ERRCODE_INVALIDPARAM; + ret = XRAN_STATUS_INVALID_PARAM; print_err("Non-supported Section Type - %d", apphdr->sectionType); } @@ -1068,7 +1458,7 @@ int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result) result->hdr.iqWidth, result->hdr.compMeth); for(i=0; inumSections; i++) { - printf(" >> %3d:%04X| rb=%d symInc=%d numSym=%d startPrbc=%02X numPrbc=%d reMask=%03X beamId=%04X freqOffset=%d ef=%d\n", + printf(" >> %3d:%04X| rb=%d symInc=%d numSym=%d startPrbc=%02d numPrbc=%d reMask=%03X beamId=%04X freqOffset=%d ef=%d\n", i, result->sections[i].info.id, result->sections[i].info.rb, result->sections[i].info.symInc, result->sections[i].info.numSymbol, diff --git a/fhi_lib/lib/src/xran_frame_struct.c b/fhi_lib/lib/src/xran_frame_struct.c new file mode 100644 index 0000000..9dd4781 --- /dev/null +++ b/fhi_lib/lib/src/xran_frame_struct.c @@ -0,0 +1,509 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +/** + * @brief XRAN layer common functionality for both lls-CU and RU as well as C-plane and + * U-plane + * @file xran_common.c + * @ingroup group_source_xran + * @author Intel Corporation + **/ + +#include +#include +#include +#include +#include + +#include "xran_frame_struct.h" +#include "xran_printf.h" + +enum nXranChBwOptions +{ + XRAN_BW_5_0_MHZ = 5, XRAN_BW_10_0_MHZ = 10, XRAN_BW_15_0_MHZ = 15, XRAN_BW_20_0_MHZ = 20, XRAN_BW_25_0_MHZ = 25, + XRAN_BW_30_0_MHZ = 30, XRAN_BW_40_0_MHZ = 40, XRAN_BW_50_0_MHZ = 50, XRAN_BW_60_0_MHZ = 60, XRAN_BW_70_0_MHZ = 70, + XRAN_BW_80_0_MHZ = 80, XRAN_BW_90_0_MHZ = 90, XRAN_BW_100_0_MHZ = 100, XRAN_BW_200_0_MHZ = 200, XRAN_BW_400_0_MHZ = 400 +}; + +// F1 Tables 38.101-1 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB +static uint16_t nNumRbsPerSymF1[3][13] = +{ + // 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz + {25, 52, 79, 106, 133, 160, 216, 270, 0, 0, 0, 0, 0}, // Numerology 0 (15KHz) + {11, 24, 38, 51, 65, 78, 106, 133, 162, 0, 217, 245, 273}, // Numerology 1 (30KHz) + {0, 11, 18, 24, 31, 38, 51, 65, 79, 0, 107, 121, 135} // Numerology 2 (60KHz) +}; + +// F2 Tables 38.101-2 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB +static uint16_t nNumRbsPerSymF2[2][4] = +{ + // 50Mhz 100MHz 200MHz 400MHz + {66, 132, 264, 0}, // Numerology 2 (60KHz) + {32, 66, 132, 264} // Numerology 3 (120KHz) +}; + +// 38.211 - Table 4.2.1 +static uint16_t nSubCarrierSpacing[5] = +{ + 15, // mu = 0 + 30, // mu = 1 + 60, // mu = 2 + 120, // mu = 3 + 240 // mu = 4 +}; + +// TTI interval in us (slot duration) +static uint16_t nTtiInterval[4] = +{ + 1000, // mu = 0 + 500, // mu = 1 + 250, // mu = 2 + 125, // mu = 3 +}; + +// F1 Tables 38.101-1 Table F.5.3. Window length for normal CP +static uint16_t nCpSizeF1[3][13][2] = +{ + // 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz + {{40, 36}, {80, 72}, {120, 108}, {160, 144}, {160, 144}, {240, 216}, {320, 288}, {320, 288}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, // Numerology 0 (15KHz) + {{22, 18}, {44, 36}, {66, 54}, {88, 72}, {88, 72}, {132, 108}, {176, 144}, {176, 144}, {264, 216}, {264, 216}, {352, 288}, {352, 288}, {352, 288}}, // Numerology 1 (30KHz) + { {0, 0}, {26, 18}, {39, 27}, {52, 36}, {52, 36}, {78, 54}, {104, 72}, {104, 72}, {156, 108}, {156, 108}, {208, 144}, {208, 144}, {208, 144}}, // Numerology 2 (60KHz) +}; + +// F2 Tables 38.101-2 Table F.5.3. Window length for normal CP +static int16_t nCpSizeF2[2][4][2] = +{ + // 50Mhz 100MHz 200MHz 400MHz + { {0, 0}, {104, 72}, {208, 144}, {416, 288}}, // Numerology 2 (60KHz) + {{68, 36}, {136, 72}, {272, 144}, {544, 288}}, // Numerology 3 (120KHz) +}; + +static uint32_t xran_fs_max_slot_num = 8000; +static uint16_t xran_fs_num_slot_tdd_loop[XRAN_MAX_SECTOR_NR] = { XRAN_NUM_OF_SLOT_IN_TDD_LOOP }; +static uint16_t xran_fs_num_dl_sym_sp[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {0}; +static uint16_t xran_fs_num_ul_sym_sp[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {0}; +static uint8_t xran_fs_slot_type[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP] = {{XRAN_SLOT_TYPE_INVALID}}; +static uint8_t xran_fs_slot_symb_type[XRAN_MAX_SECTOR_NR][XRAN_NUM_OF_SLOT_IN_TDD_LOOP][XRAN_NUM_OF_SYMBOL_PER_SLOT] = {{{XRAN_SLOT_TYPE_INVALID}}}; +static float xran_fs_ul_rate[XRAN_MAX_SECTOR_NR] = {0.0}; +static float xran_fs_dl_rate[XRAN_MAX_SECTOR_NR] = {0.0}; + +uint32_t xran_fs_get_tti_interval(uint8_t nMu) +{ + if (nMu < 4) + { + return nTtiInterval[nMu]; + } + else + { + printf("ERROR: %s Mu[%d] is not valid, setting to 0\n",__FUNCTION__, nMu); + return nTtiInterval[0]; + } +} + +uint32_t xran_fs_get_scs(uint8_t nMu) +{ + if (nMu <= 3) + { + return nSubCarrierSpacing[nMu]; + } + else + { + printf("ERROR: %s Mu[%d] is not valid\n",__FUNCTION__, nMu); + } + + return 0; +} + +//------------------------------------------------------------------------------------------- +/** @ingroup group_nr5g_source_phy_common + * + * @param[in] nNumerology - Numerology determine sub carrier spacing, Value: 0->4 0: 15khz, 1: 30khz, 2: 60khz 3: 120khz, 4: 240khz + * @param[in] nBandwidth - Carrier bandwidth for in MHz. Value: 5->400 + * @param[in] nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 + * + * @return Number of RBs in cell + * + * @description + * Returns number of RBs based on 38.101-1 and 38.101-2 for the cell + * +**/ +//------------------------------------------------------------------------------------------- +uint16_t xran_fs_get_num_rbs(uint32_t nNumerology, uint32_t nBandwidth, uint32_t nAbsFrePointA) +{ + uint32_t error = 1; + uint16_t numRBs = 0; + + if (nAbsFrePointA <= 6000000) + { + // F1 Tables 38.101-1 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB + if (nNumerology < 3) + { + switch(nBandwidth) + { + case XRAN_BW_5_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][0]; + error = 0; + break; + case XRAN_BW_10_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][1]; + error = 0; + break; + case XRAN_BW_15_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][2]; + error = 0; + break; + case XRAN_BW_20_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][3]; + error = 0; + break; + case XRAN_BW_25_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][4]; + error = 0; + break; + case XRAN_BW_30_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][5]; + error = 0; + break; + case XRAN_BW_40_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][6]; + error = 0; + break; + case XRAN_BW_50_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][7]; + error = 0; + break; + case XRAN_BW_60_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][8]; + error = 0; + break; + case XRAN_BW_70_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][9]; + error = 0; + break; + case XRAN_BW_80_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][10]; + error = 0; + break; + case XRAN_BW_90_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][11]; + error = 0; + break; + case XRAN_BW_100_0_MHZ: + numRBs = nNumRbsPerSymF1[nNumerology][12]; + error = 0; + break; + default: + error = 1; + break; + } + } + } + else + { + if ((nNumerology >= 2) && (nNumerology <= 3)) + { + // F2 Tables 38.101-2 Table 5.3.2-1. Maximum transmission bandwidth configuration NRB + switch(nBandwidth) + { + case XRAN_BW_50_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][0]; + error = 0; + break; + case XRAN_BW_100_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][1]; + error = 0; + break; + case XRAN_BW_200_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][2]; + error = 0; + break; + case XRAN_BW_400_0_MHZ: + numRBs = nNumRbsPerSymF2[nNumerology-2][3]; + error = 0; + break; + default: + error = 1; + break; + } + } + } + + + if (error) + { + printf("ERROR: %s: nNumerology[%d] nBandwidth[%d] nAbsFrePointA[%d]\n",__FUNCTION__, nNumerology, nBandwidth, nAbsFrePointA); + } + else + { + printf("%s: nNumerology[%d] nBandwidth[%d] nAbsFrePointA[%d] numRBs[%d]\n",__FUNCTION__, nNumerology, nBandwidth, nAbsFrePointA, numRBs); + } + + return numRBs; +} + +//------------------------------------------------------------------------------------------- +/** @ingroup phy_cal_nrarfcn + * + * @param[in] center frequency + * + * @return NR-ARFCN + * + * @description + * This calculates NR-ARFCN value according to center frequency + * +**/ +//------------------------------------------------------------------------------------------- +uint32_t xran_fs_cal_nrarfcn(uint32_t nCenterFreq) +{ + uint32_t nDeltaFglobal,nFoffs,nNoffs; + uint32_t nNRARFCN = 0; + + if(nCenterFreq > 0 && nCenterFreq < 3000*1000) + { + nDeltaFglobal = 5; + nFoffs = 0; + nNoffs = 0; + } + else if(nCenterFreq >= 3000*1000 && nCenterFreq < 24250*1000) + { + nDeltaFglobal = 15; + nFoffs = 3000*1000; + nNoffs = 600000; + } + else if(nCenterFreq >= 24250*1000 && nCenterFreq <= 100000*1000) + { + nDeltaFglobal = 60; + nFoffs = 24250080; + nNoffs = 2016667; + } + else + { + printf("@@@@ incorrect center frerquency %d\n",nCenterFreq); + return (0); + } + + nNRARFCN = ((nCenterFreq - nFoffs)/nDeltaFglobal) + nNoffs; + + printf("%s: nCenterFreq[%d] nDeltaFglobal[%d] nFoffs[%d] nNoffs[%d] nNRARFCN[%d]\n", __FUNCTION__, nCenterFreq, nDeltaFglobal, nFoffs, nNoffs, nNRARFCN); + return (nNRARFCN); +} + +uint32_t xran_fs_slot_limit_init(int32_t tti_interval_us) +{ + xran_fs_max_slot_num = (1000/tti_interval_us)*1000; + return xran_fs_max_slot_num; +} + +uint32_t xran_fs_get_max_slot(void) +{ + return xran_fs_max_slot_num; +} + +int32_t xran_fs_slot_limit(int32_t nSfIdx) +{ + while (nSfIdx < 0) { + nSfIdx += xran_fs_max_slot_num; + } + + while (nSfIdx >= xran_fs_max_slot_num) { + nSfIdx -= xran_fs_max_slot_num; + } + + return nSfIdx; +} + +void xran_fs_clear_slot_type(uint32_t nPhyInstanceId) +{ + xran_fs_ul_rate[nPhyInstanceId] = 0.0; + xran_fs_dl_rate[nPhyInstanceId] = 0.0; + xran_fs_num_slot_tdd_loop[nPhyInstanceId] = 1; +} + +int32_t xran_fs_set_slot_type(uint32_t nPhyInstanceId, uint32_t nFrameDuplexType, uint32_t nTddPeriod, struct xran_slot_config* psSlotConfig) +{ + uint32_t nSlotNum, nSymNum, nVal, i, j; + uint32_t numDlSym, numUlSym, numGuardSym; + uint32_t numDlSlots = 0, numUlSlots = 0, numSpDlSlots = 0, numSpUlSlots = 0, numSpSlots = 0; + char sSlotPattern[XRAN_SLOT_TYPE_LAST][10] = {"IN\0", "DL\0", "UL\0", "SP\0", "FD\0"}; + + // nPhyInstanceId Carrier ID + // nFrameDuplexType 0 = FDD 1 = TDD + // nTddPeriod Tdd Periodicity + // psSlotConfig[80] Slot Config Structure for nTddPeriod Slots + + xran_fs_ul_rate[nPhyInstanceId] = 0.0; + xran_fs_dl_rate[nPhyInstanceId] = 0.0; + xran_fs_num_slot_tdd_loop[nPhyInstanceId] = nTddPeriod; + + for (i = 0; i < XRAN_NUM_OF_SLOT_IN_TDD_LOOP; i++) + { + xran_fs_slot_type[nPhyInstanceId][i] = XRAN_SLOT_TYPE_INVALID; + xran_fs_num_dl_sym_sp[nPhyInstanceId][i] = 0; + xran_fs_num_ul_sym_sp[nPhyInstanceId][i] = 0; + } + + if (nFrameDuplexType == XRAN_FDD) + { + for (i = 0; i < XRAN_NUM_OF_SLOT_IN_TDD_LOOP; i++) + { + xran_fs_slot_type[nPhyInstanceId][i] = XRAN_SLOT_TYPE_FDD; + for(j = 0; j < XRAN_NUM_OF_SYMBOL_PER_SLOT; j++) + xran_fs_slot_symb_type[nPhyInstanceId][i][j] = XRAN_SYMBOL_TYPE_FDD; + } + xran_fs_num_slot_tdd_loop[nPhyInstanceId] = 1; + xran_fs_dl_rate[nPhyInstanceId] = 1.0; + xran_fs_ul_rate[nPhyInstanceId] = 1.0; + } + else + { + for (nSlotNum = 0; nSlotNum < nTddPeriod; nSlotNum++) + { + numDlSym = 0; + numUlSym = 0; + numGuardSym = 0; + for (nSymNum = 0; nSymNum < XRAN_NUM_OF_SYMBOL_PER_SLOT; nSymNum++) + { + switch(psSlotConfig[nSlotNum].nSymbolType[nSymNum]) + { + case XRAN_SYMBOL_TYPE_DL: + numDlSym++; + xran_fs_slot_symb_type[nPhyInstanceId][nSlotNum][nSymNum] = XRAN_SYMBOL_TYPE_DL; + break; + case XRAN_SYMBOL_TYPE_GUARD: + xran_fs_slot_symb_type[nPhyInstanceId][nSlotNum][nSymNum] = XRAN_SYMBOL_TYPE_GUARD; + numGuardSym++; + break; + default: + xran_fs_slot_symb_type[nPhyInstanceId][nSlotNum][nSymNum] = XRAN_SYMBOL_TYPE_UL; + numUlSym++; + break; + } + } + + print_dbg("nSlotNum[%d] : numDlSym[%d] numGuardSym[%d] numUlSym[%d] ", nSlotNum, numDlSym, numGuardSym, numUlSym); + + if ((numUlSym == 0) && (numGuardSym == 0)) + { + xran_fs_slot_type[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_DL; + numDlSlots++; + print_dbg("XRAN_SLOT_TYPE_DL\n"); + } + else if ((numDlSym == 0) && (numGuardSym == 0)) + { + xran_fs_slot_type[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_UL; + numUlSlots++; + print_dbg("XRAN_SLOT_TYPE_UL\n"); + } + else + { + xran_fs_slot_type[nPhyInstanceId][nSlotNum] = XRAN_SLOT_TYPE_SP; + numSpSlots++; + print_dbg("XRAN_SLOT_TYPE_SP\n"); + + if (numDlSym) + { + numSpDlSlots++; + xran_fs_num_dl_sym_sp[nPhyInstanceId][nSlotNum] = numDlSym; + } + if (numUlSym) + { + numSpUlSlots++; + xran_fs_num_ul_sym_sp[nPhyInstanceId][nSlotNum] = numUlSym; + } + } + print_dbg(" numDlSlots[%d] numUlSlots[%d] numSpSlots[%d] numSpDlSlots[%d] numSpUlSlots[%d]\n", numDlSlots, numUlSlots, numSpSlots, numSpDlSlots, numSpUlSlots); + } + + xran_fs_dl_rate[nPhyInstanceId] = (float)(numDlSlots + numSpDlSlots) / (float)nTddPeriod; + xran_fs_ul_rate[nPhyInstanceId] = (float)(numUlSlots + numSpUlSlots) / (float)nTddPeriod; + } + + print_dbg("%s: nPhyInstanceId[%d] nFrameDuplexType[%d], nTddPeriod[%d]\n", + __FUNCTION__, nPhyInstanceId, nFrameDuplexType, nTddPeriod); + + print_dbg("DLRate[%f] ULRate[%f]\n", xran_fs_dl_rate[nPhyInstanceId], xran_fs_ul_rate[nPhyInstanceId]); + + nVal = (xran_fs_num_slot_tdd_loop[nPhyInstanceId] < 10) ? xran_fs_num_slot_tdd_loop[nPhyInstanceId] : 10; + + print_dbg("SlotPattern:\n"); + print_dbg("Slot: "); + for (nSlotNum = 0; nSlotNum < nVal; nSlotNum++) + { + print_dbg("%d ", nSlotNum); + } + print_dbg("\n"); + + print_dbg(" %3d ", 0); + for (nSlotNum = 0, i = 0; nSlotNum < xran_fs_num_slot_tdd_loop[nPhyInstanceId]; nSlotNum++) + { + print_dbg("%s ", sSlotPattern[xran_fs_slot_type[nPhyInstanceId][nSlotNum]]); + i++; + if ((i == 10) && ((nSlotNum+1) < xran_fs_num_slot_tdd_loop[nPhyInstanceId])) + { + print_dbg("\n"); + print_dbg(" %3d ", nSlotNum); + i = 0; + } + } + print_dbg("\n\n"); + + return 0; +} + +int32_t xran_fs_get_slot_type(int32_t nCellIdx, int32_t nSlotdx, int32_t nType) +{ + int32_t nSfIdxMod, nSfType, ret = 0; + + nSfIdxMod = xran_fs_slot_limit(nSlotdx) % ((xran_fs_num_slot_tdd_loop[nCellIdx] > 0) ? xran_fs_num_slot_tdd_loop[nCellIdx]: 1); + nSfType = xran_fs_slot_type[nCellIdx][nSfIdxMod]; + + if (nSfType == nType) + { + ret = 1; + } + else if (nSfType == XRAN_SLOT_TYPE_SP) + { + if ((nType == XRAN_SLOT_TYPE_DL) && xran_fs_num_dl_sym_sp[nCellIdx][nSfIdxMod]) + { + ret = 1; + } + + if ((nType == XRAN_SLOT_TYPE_UL) && xran_fs_num_ul_sym_sp[nCellIdx][nSfIdxMod]) + { + ret = 1; + } + } + else if (nSfType == XRAN_SLOT_TYPE_FDD) + { + ret = 1; + } + + return ret; +} + +int32_t xran_fs_get_symbol_type(int32_t nCellIdx, int32_t nSlotdx, int32_t nSymbIdx) +{ + int32_t nSfIdxMod, nSfType, ret = 0; + + nSfIdxMod = xran_fs_slot_limit(nSlotdx) % ((xran_fs_num_slot_tdd_loop[nCellIdx] > 0) ? xran_fs_num_slot_tdd_loop[nCellIdx]: 1); + + return xran_fs_slot_symb_type[nCellIdx][nSfIdxMod][nSymbIdx]; +} + + diff --git a/fhi_lib/lib/src/xran_frame_struct.h b/fhi_lib/lib/src/xran_frame_struct.h new file mode 100644 index 0000000..c8794b6 --- /dev/null +++ b/fhi_lib/lib/src/xran_frame_struct.h @@ -0,0 +1,83 @@ +/****************************************************************************** +* +* Copyright (c) 2019 Intel. +* +* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +* +* 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. +* +*******************************************************************************/ + + +/** + * @brief Header file for function to work with 5G NR frame structure and related + * routines + * @file xran_frame_struct.h + * @ingroup group_source_xran + * @author Intel Corporation + **/ + +#ifndef _XRAN_FRAME_STRUCT_ +#define _XRAN_FRAME_STRUCT_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "xran_fh_o_du.h" + +uint32_t xran_fs_get_tti_interval(uint8_t nMu); +uint32_t xran_fs_get_scs(uint8_t nMu); + +//------------------------------------------------------------------------------------------- +/** @ingroup group_nr5g_source_phy_common + * + * @param[in] nNumerology - Numerology determine sub carrier spacing, Value: 0->4 0: 15khz, 1: 30khz, 2: 60khz 3: 120khz, 4: 240khz + * @param[in] nBandwidth - Carrier bandwidth for in MHz. Value: 5->400 + * @param[in] nAbsFrePointA - Abs Freq Point A of the Carrier Center Frequency for in KHz Value: 450000->52600000 + * + * @return Number of RBs in cell + * + * @description + * Returns number of RBs based on 38.101-1 and 38.101-2 for the cell + * +**/ +//------------------------------------------------------------------------------------------- +uint16_t xran_fs_get_num_rbs(uint32_t nNumerology, uint32_t nBandwidth, uint32_t nAbsFrePointA); + +//------------------------------------------------------------------------------------------- +/** @ingroup phy_cal_nrarfcn + * + * @param[in] center frequency + * + * @return NR-ARFCN + * + * @description + * This calculates NR-ARFCN value according to center frequency + * +**/ +//------------------------------------------------------------------------------------------- +uint32_t xran_fs_cal_nrarfcn(uint32_t nCenterFreq); +int32_t xran_fs_slot_limit(int32_t nSlotIdx); +void xran_fs_clear_slot_type(uint32_t nCcId); +int32_t xran_fs_set_slot_type(uint32_t nCcId, uint32_t nFrameDuplexType, uint32_t nTddPeriod, struct xran_slot_config* psSlotConfig); +int32_t xran_fs_get_slot_type(int32_t nCcId, int32_t nSlotIdx, int32_t nType); +uint32_t xran_fs_slot_limit_init(int32_t tti_interval_us); +uint32_t xran_fs_get_max_slot(void); +int32_t xran_fs_get_symbol_type(int32_t nCellIdx, int32_t nSlotdx, int32_t nSymbIdx); + +#ifdef __cplusplus +} +#endif + +#endif /* _XRAN_FRAME_STRUCT_ */ + diff --git a/fhi_lib/lib/src/xran_hash.h b/fhi_lib/lib/src/xran_hash.h deleted file mode 100644 index b06c694..0000000 --- a/fhi_lib/lib/src/xran_hash.h +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** -* -* Copyright (c) 2019 Intel. -* -* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 -* -* 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. -* -*******************************************************************************/ - - -/** - * @brief Definitions and support functions to process XRAN packet - * @file xran_hash.h - * @ingroup group_source_xran - * @author Intel Corporation - **/ - -#ifndef _XRAN_HASH_H -#define _XRAN_HASH_H - -#include -#include -#include - -#define DEFAULT_HASH_FUNC rte_hash_crc - - -struct rte_hash *xran_section_init_hash(uint16_t dir, uint16_t cid, int max_num_sections); -void xran_section_free_hash(struct rte_hash *hash); - -void xran_section_reset_hash(struct rte_hash *hash); -int xran_section_add_hash(const struct rte_hash *hash, uint16_t section_id, int index); -int xran_section_lookup(const struct rte_hash *hash, uint16_t section_id); -int xran_section_iterate(const struct rte_hash *hash, uint32_t *next); - -#endif diff --git a/fhi_lib/lib/src/xran_lib_mlog_tasks_id.h b/fhi_lib/lib/src/xran_lib_mlog_tasks_id.h index 2c99c19..46f264b 100644 --- a/fhi_lib/lib/src/xran_lib_mlog_tasks_id.h +++ b/fhi_lib/lib/src/xran_lib_mlog_tasks_id.h @@ -26,6 +26,10 @@ #ifndef _XRAN_TASK_ID_H_ #define _XRAN_TASK_ID_H_ +#ifdef __cplusplus +extern "C" { +#endif + #define RESOURCE_CORE_0 0 #define RESOURCE_CORE_1 1 #define RESOURCE_CORE_2 2 @@ -96,6 +100,9 @@ #define PID_PROCESS_CP_PKT 2700 +#ifdef __cplusplus +} +#endif #endif /* _XRAN_TASK_ID_H_ */ diff --git a/fhi_lib/lib/src/xran_main.c b/fhi_lib/lib/src/xran_main.c index 94751f6..256457d 100644 --- a/fhi_lib/lib/src/xran_main.c +++ b/fhi_lib/lib/src/xran_main.c @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief XRAN main functionality module * @file xran_main.c @@ -34,6 +33,7 @@ #include #include #include +#include #include #include @@ -41,9 +41,10 @@ #include #include #include +#include #include -#include "xran_fh_lls_cu.h" +#include "xran_fh_o_du.h" #include "ethdi.h" #include "xran_pkt.h" @@ -53,13 +54,11 @@ #include "xran_lib_mlog_tasks_id.h" #include "xran_timer.h" #include "xran_common.h" +#include "xran_frame_struct.h" #include "xran_printf.h" +#include "xran_app_frag.h" -#ifndef MLOG_ENABLED -#include "mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif +#include "xran_mlog_lnx.h" #define DIV_ROUND_OFFSET(X,Y) ( X/Y + ((X%Y)?1:0) ) @@ -69,42 +68,51 @@ (((int32_t)otaSym - (int32_t)offSym) - numSymTotal) : \ ((int32_t)otaSym - (int32_t)offSym)) -#define MAX_NUM_OF_XRAN_CTX (2) +#define MAX_NUM_OF_XRAN_CTX (2) #define XranIncrementCtx(ctx) ((ctx >= (MAX_NUM_OF_XRAN_CTX-1)) ? 0 : (ctx+1)) #define XranDecrementCtx(ctx) ((ctx == 0) ? (MAX_NUM_OF_XRAN_CTX-1) : (ctx-1)) +#define MAX_NUM_OF_DPDK_TIMERS (10) +#define DpdkTiemerIncrementCtx(ctx) ((ctx >= (MAX_NUM_OF_DPDK_TIMERS-1)) ? 0 : (ctx+1)) +#define DpdkTiemerDecrementCtx(ctx) ((ctx == 0) ? (MAX_NUM_OF_DPDK_TIMERS-1) : (ctx-1)) + + +//#define XRAN_CREATE_RBMAP /**< generate slot map base on symbols */ + + struct xran_timer_ctx { uint32_t tti_to_process; }; -static XranLibHandleInfoStruct DevHandle; -static struct xran_lib_ctx g_xran_lib_ctx = { 0 }; +static xran_cc_handle_t pLibInstanceHandles[XRAN_PORTS_NUM][XRAN_MAX_SECTOR_NR] = {NULL}; +static struct xran_device_ctx g_xran_dev_ctx[XRAN_PORTS_NUM] = { 0 }; struct xran_timer_ctx timer_ctx[MAX_NUM_OF_XRAN_CTX]; static struct rte_timer tti_to_phy_timer[10]; -static struct rte_timer tti_timer; static struct rte_timer sym_timer; -static struct rte_timer tx_cp_dl_timer; -static struct rte_timer tx_cp_ul_timer; -static struct rte_timer tx_up_timer; +static struct rte_timer dpdk_timer[MAX_NUM_OF_DPDK_TIMERS]; -static long interval_us = 125; +long interval_us = 1000; -uint32_t xran_lib_ota_tti = 0; /* [0:7999] */ -uint32_t xran_lib_ota_sym = 0; /* [0:7] */ -uint32_t xran_lib_ota_sym_idx = 0; /* [0 : 14*8*1000-1] */ +uint32_t xran_lib_ota_tti = 0; /* [0:(1000000/TTI-1)] */ +uint32_t xran_lib_ota_sym = 0; /* [0:1000/TTI-1] */ +uint32_t xran_lib_ota_sym_idx = 0; /* [0 : 14*(1000000/TTI)-1] + where TTI is TTI interval in microseconds */ -uint64_t xran_lib_gps_second = 0; +static uint8_t xran_cp_seq_id_num[XRAN_MAX_CELLS_PER_PORT][XRAN_DIR_MAX][XRAN_MAX_ANTENNA_NR * 2]; //XRAN_MAX_ANTENNA_NR * 2 for PUSCH and PRACH +static uint8_t xran_updl_seq_id_num[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR]; +static uint8_t xran_upul_seq_id_num[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR * 2]; -static uint8_t xran_cp_seq_id_num[XRAN_MAX_CELLS_PER_PORT][XRAN_DIR_MAX][XRAN_MAX_ANTENNA_NR]; -static uint8_t xran_section_id_curslot[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR]; -static uint16_t xran_section_id[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR]; +static uint8_t xran_section_id_curslot[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR * 2]; +static uint16_t xran_section_id[XRAN_MAX_CELLS_PER_PORT][XRAN_MAX_ANTENNA_NR * 2]; void xran_timer_arm(struct rte_timer *tim, void* arg); + int xran_process_tx_sym(void *arg); int xran_process_rx_sym(void *arg, + struct rte_mbuf *mbuf, void *iq_data_start, uint16_t size, uint8_t CC_ID, @@ -112,27 +120,91 @@ int xran_process_rx_sym(void *arg, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint8_t symb_id); + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id, + uint32_t *mb_free); + +int xran_process_prach_sym(void *arg, + struct rte_mbuf *mbuf, + void *iq_data_start, + uint16_t size, + uint8_t CC_ID, + uint8_t Ant_ID, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id); void tti_ota_cb(struct rte_timer *tim, void *arg); void tti_to_phy_cb(struct rte_timer *tim, void *arg); void xran_timer_arm_ex(struct rte_timer *tim, void* CbFct, void *CbArg, unsigned tim_lcore); -struct xran_lib_ctx *xran_lib_get_ctx(void) +struct xran_device_ctx *xran_dev_get_ctx(void) { - return &g_xran_lib_ctx; + return &g_xran_dev_ctx[0]; } -static inline XRANFHCONFIG *xran_lib_get_ctx_fhcfg(void) +static inline struct xran_fh_config *xran_lib_get_ctx_fhcfg(void) { - return (&(xran_lib_get_ctx()->xran_fh_cfg)); + return (&(xran_dev_get_ctx()->fh_cfg)); } -inline uint16_t xran_get_beamid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id) +uint16_t xran_get_beamid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id) { return (0); // NO BEAMFORMING } +enum xran_if_state xran_get_if_state(void) +{ + return xran_if_current_state; +} + +int xran_isPRACHSlot(uint32_t subframe_id, uint32_t slot_id) +{ + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + xRANPrachCPConfigStruct *pPrachCPConfig = &(p_xran_dev_ctx->PrachCPConfig); + int isPRACHslot = 0; + + if (p_xran_dev_ctx->fh_cfg.frame_conf.nNumerology < 2){ + //for FR1, in 38.211 tab 6.3.3.2-2&3 it is subframe index + if (pPrachCPConfig->isPRACHslot[subframe_id] == 1){ + if (pPrachCPConfig->nrofPrachInSlot != 1) + isPRACHslot = 1; + else{ + if (p_xran_dev_ctx->fh_cfg.frame_conf.nNumerology == 0) + isPRACHslot = 1; + else if (slot_id == 1) + isPRACHslot = 1; + } + } + } + else if (p_xran_dev_ctx->fh_cfg.frame_conf.nNumerology == 3){ + //for FR2, 38.211 tab 6.3.3.4 it is slot index of 60kHz slot + uint32_t slotidx; + slotidx = subframe_id * SLOTNUM_PER_SUBFRAME + slot_id; + if (pPrachCPConfig->nrofPrachInSlot == 2){ + if (pPrachCPConfig->isPRACHslot[slotidx>>1] == 1) + isPRACHslot = 1; + } + else{ + if ((pPrachCPConfig->isPRACHslot[slotidx>>1] == 1) && (slotidx % 2 == 1)) + isPRACHslot = 1; + } + } + else + print_err("Numerology %d not supported", p_xran_dev_ctx->fh_cfg.frame_conf.nNumerology); + return isPRACHslot; +} + int xran_init_sectionid(void *pHandle) { int cell, dir, ant; @@ -147,19 +219,72 @@ int xran_init_sectionid(void *pHandle) return (0); } -int xran_init_seqid(void *pHandle) +int xran_init_prach(struct xran_fh_config* pConf, struct xran_device_ctx * p_xran_dev_ctx) { - int cell, dir, ant; + int32_t i; + uint8_t slotNr; + struct xran_prach_config* pPRACHConfig = &(pConf->prach_conf); + const xRANPrachConfigTableStruct *pxRANPrachConfigTable; + uint8_t nNumerology = pConf->frame_conf.nNumerology; + uint8_t nPrachConfIdx = pPRACHConfig->nPrachConfIdx; + xRANPrachCPConfigStruct *pPrachCPConfig = &(p_xran_dev_ctx->PrachCPConfig); - for(cell=0; cell < XRAN_MAX_CELLS_PER_PORT; cell++) { - for(dir=0; dir < XRAN_DIR_MAX; dir++) { - for(ant=0; ant < XRAN_MAX_ANTENNA_NR; ant++) { - xran_cp_seq_id_num[cell][dir][ant] = 0; - } - } + if (nNumerology > 2) + pxRANPrachConfigTable = &gxranPrachDataTable_mmw[nPrachConfIdx]; + else if (pConf->frame_conf.nFrameDuplexType == 1) + pxRANPrachConfigTable = &gxranPrachDataTable_sub6_tdd[nPrachConfIdx]; + else + pxRANPrachConfigTable = &gxranPrachDataTable_sub6_fdd[nPrachConfIdx]; + + uint8_t preambleFmrt = pxRANPrachConfigTable->preambleFmrt[0]; + const xRANPrachPreambleLRAStruct *pxranPreambleforLRA = &gxranPreambleforLRA[preambleFmrt]; + memset(pPrachCPConfig, 0, sizeof(xRANPrachCPConfigStruct)); + if(pConf->log_level) + printf("xRAN open PRACH config: Numerology %u ConfIdx %u, preambleFmrt %u startsymb %u, numSymbol %u, occassionsInPrachSlot %u\n", nNumerology, nPrachConfIdx, preambleFmrt, pxRANPrachConfigTable->startingSym, pxRANPrachConfigTable->duration, pxRANPrachConfigTable->occassionsInPrachSlot); + + pPrachCPConfig->filterIdx = XRAN_FILTERINDEX_PRACH_ABC; // 3, PRACH preamble format A1~3, B1~4, C0, C2 + pPrachCPConfig->startSymId = pxRANPrachConfigTable->startingSym; + pPrachCPConfig->startPrbc = pPRACHConfig->nPrachFreqStart; + pPrachCPConfig->numPrbc = (preambleFmrt >= FORMAT_A1)? 12 : 70; + pPrachCPConfig->timeOffset = pxranPreambleforLRA->nRaCp; + pPrachCPConfig->freqOffset = xran_get_freqoffset(pPRACHConfig->nPrachFreqOffset, pPRACHConfig->nPrachSubcSpacing); + pPrachCPConfig->x = pxRANPrachConfigTable->x; + pPrachCPConfig->nrofPrachInSlot = pxRANPrachConfigTable->nrofPrachInSlot; + pPrachCPConfig->y[0] = pxRANPrachConfigTable->y[0]; + pPrachCPConfig->y[1] = pxRANPrachConfigTable->y[1]; + if (preambleFmrt >= FORMAT_A1) + { + pPrachCPConfig->numSymbol = pxRANPrachConfigTable->duration; + pPrachCPConfig->occassionsInPrachSlot = pxRANPrachConfigTable->occassionsInPrachSlot; + } + else + { + pPrachCPConfig->numSymbol = 1; + pPrachCPConfig->occassionsInPrachSlot = 1; + } + + if(pConf->log_level) + printf("PRACH: x %u y[0] %u, y[1] %u prach slot: %u ..", pPrachCPConfig->x, pPrachCPConfig->y[0], pPrachCPConfig->y[1], pxRANPrachConfigTable->slotNr[0]); + pPrachCPConfig->isPRACHslot[pxRANPrachConfigTable->slotNr[0]] = 1; + for (i=1; i < XRAN_PRACH_CANDIDATE_SLOT; i++) + { + slotNr = pxRANPrachConfigTable->slotNr[i]; + if (slotNr > 0){ + pPrachCPConfig->isPRACHslot[slotNr] = 1; + if(pConf->log_level) + printf(" %u ..", slotNr); } + } + printf("\n"); + for (i = 0; i < XRAN_MAX_SECTOR_NR; i++){ + p_xran_dev_ctx->prach_start_symbol[i] = pPrachCPConfig->startSymId; + p_xran_dev_ctx->prach_last_symbol[i] = pPrachCPConfig->startSymId + pPrachCPConfig->numSymbol * pPrachCPConfig->occassionsInPrachSlot - 1; + } + if(pConf->log_level){ + printf("PRACH start symbol %u lastsymbol %u\n", p_xran_dev_ctx->prach_start_symbol[0], p_xran_dev_ctx->prach_last_symbol[0]); + } - return (0); + return (XRAN_STATUS_SUCCESS); } inline uint16_t xran_alloc_sectionid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id) @@ -168,22 +293,40 @@ inline uint16_t xran_alloc_sectionid(void *pHandle, uint8_t dir, uint8_t cc_id, print_err("Invalid CC ID - %d", cc_id); return (0); } - if(ant_id >= XRAN_MAX_ANTENNA_NR) { + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { //for PRACH, ant_id starts from num_ant print_err("Invalid antenna ID - %d", ant_id); return (0); - } + } /* if new slot has been started, * then initializes section id again for new start */ if(xran_section_id_curslot[cc_id][ant_id] != slot_id) { xran_section_id[cc_id][ant_id] = 0; xran_section_id_curslot[cc_id][ant_id] = slot_id; - } - + } + return(xran_section_id[cc_id][ant_id]++); } -inline uint8_t xran_get_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id) +int xran_init_seqid(void *pHandle) +{ + int cell, dir, ant; + + for(cell=0; cell < XRAN_MAX_CELLS_PER_PORT; cell++) { + for(dir=0; dir < XRAN_DIR_MAX; dir++) { + for(ant=0; ant < XRAN_MAX_ANTENNA_NR * 2; ant++) + xran_cp_seq_id_num[cell][dir][ant] = 0; + } + for(ant=0; ant < XRAN_MAX_ANTENNA_NR; ant++) + xran_updl_seq_id_num[cell][ant] = 0; + for(ant=0; ant < XRAN_MAX_ANTENNA_NR * 2; ant++) + xran_upul_seq_id_num[cell][ant] = 0; + } + + return (0); +} + +static inline uint8_t xran_get_cp_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id) { if(dir >= XRAN_DIR_MAX) { print_err("Invalid direction - %d", dir); @@ -193,64 +336,185 @@ inline uint8_t xran_get_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t print_err("Invalid CC ID - %d", cc_id); return (0); } - if(ant_id >= XRAN_MAX_ANTENNA_NR) { + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { print_err("Invalid antenna ID - %d", ant_id); return (0); } return(xran_cp_seq_id_num[cc_id][dir][ant_id]++); } +static inline uint8_t xran_get_updl_seqid(void *pHandle, uint8_t cc_id, uint8_t ant_id) +{ + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (0); + } + if(ant_id >= XRAN_MAX_ANTENNA_NR) { + print_err("Invalid antenna ID - %d", ant_id); + return (0); + } -inline int xran_update_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id, uint8_t seq_id) + /* Only U-Plane DL needs to get sequence ID in O-DU */ + return(xran_updl_seq_id_num[cc_id][ant_id]++); +} +static inline uint8_t *xran_get_updl_seqid_addr(void *pHandle, uint8_t cc_id, uint8_t ant_id) { - return (0); + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (0); + } + if(ant_id >= XRAN_MAX_ANTENNA_NR) { + print_err("Invalid antenna ID - %d", ant_id); + return (0); + } + + /* Only U-Plane DL needs to get sequence ID in O-DU */ + return(&xran_updl_seq_id_num[cc_id][ant_id]); +} +static inline int8_t xran_check_upul_seqid(void *pHandle, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id, uint8_t seq_id) +{ + + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (-1); + } + + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { + print_err("Invalid antenna ID - %d", ant_id); + return (-1); + } + + /* O-DU needs to check the sequence ID of U-Plane UL from O-RU */ + xran_upul_seq_id_num[cc_id][ant_id]++; + if(xran_upul_seq_id_num[cc_id][ant_id] == seq_id) { /* expected sequence */ + return (XRAN_STATUS_SUCCESS); + } else { + print_err("expected seqid %u received %u, slot %u, ant %u cc %u", xran_upul_seq_id_num[cc_id][ant_id], seq_id, slot_id, ant_id, cc_id); + xran_upul_seq_id_num[cc_id][ant_id] = seq_id; // for next + return (-1); + } } ////////////////////////////////////////// // For RU emulation -static struct xran_section_gen_info cpSections[255]; +static inline uint8_t xran_get_upul_seqid(void *pHandle, uint8_t cc_id, uint8_t ant_id) +{ + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (0); + } + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { + print_err("Invalid antenna ID - %d", ant_id); + return (0); + } + + return(xran_upul_seq_id_num[cc_id][ant_id]++); +} +static inline uint8_t *xran_get_upul_seqid_addr(void *pHandle, uint8_t cc_id, uint8_t ant_id) +{ + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (0); + } + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { + print_err("Invalid antenna ID - %d", ant_id); + return (0); + } + + return(&xran_upul_seq_id_num[cc_id][ant_id]); +} +static inline int8_t xran_check_cp_seqid(void *pHandle, uint8_t dir, uint8_t cc_id, uint8_t ant_id, uint8_t seq_id) +{ + if(dir >= XRAN_DIR_MAX) { + print_err("Invalid direction - %d", dir); + return (-1); + } + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (-1); + } + if(ant_id >= XRAN_MAX_ANTENNA_NR * 2) { + print_err("Invalid antenna ID - %d", ant_id); + return (-1); + } + + xran_cp_seq_id_num[cc_id][dir][ant_id]++; + if(xran_cp_seq_id_num[cc_id][dir][ant_id] == seq_id) { /* expected sequence */ + return (0); + } + else { + xran_cp_seq_id_num[cc_id][dir][ant_id] = seq_id; + return (-1); + } +} +static inline int8_t xran_check_updl_seqid(void *pHandle, uint8_t cc_id, uint8_t ant_id, uint8_t slot_id, uint8_t seq_id) +{ + if(cc_id >= XRAN_MAX_CELLS_PER_PORT) { + print_err("Invalid CC ID - %d", cc_id); + return (-1); + } + + if(ant_id >= XRAN_MAX_ANTENNA_NR) { + print_err("Invalid antenna ID - %d", ant_id); + return (-1); + } + + /* O-RU needs to check the sequence ID of U-Plane DL from O-DU */ + xran_updl_seq_id_num[cc_id][ant_id]++; + if(xran_updl_seq_id_num[cc_id][ant_id] == seq_id) { + /* expected sequence */ + return (0); + } else { + xran_updl_seq_id_num[cc_id][ant_id] = seq_id; + return (-1); + } +} + + +static struct xran_section_gen_info cpSections[XRAN_MAX_NUM_SECTIONS]; static struct xran_cp_gen_params cpInfo; int process_cplane(struct rte_mbuf *pkt) { - int xran_parse_cp_pkt(struct rte_mbuf *mbuf, struct xran_cp_gen_params *result); + struct xran_recv_packet_info recv; cpInfo.sections = cpSections; - xran_parse_cp_pkt(pkt, &cpInfo); + xran_parse_cp_pkt(pkt, &cpInfo, &recv); - return (0); + return (MBUF_FREE); } ////////////////////////////////////////// void sym_ota_cb(struct rte_timer *tim, void *arg) { - uint8_t offset = 0; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); struct xran_timer_ctx *pTCtx = (struct xran_timer_ctx *)arg; long t1 = MLogTick(); + static int32_t ctx = 0; if(XranGetSymNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT) == 0){ tti_ota_cb(NULL, arg); } - if(XranGetSymNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT) == 1){ - if(p_xran_lib_ctx->phy_tti_cb_done == 0){ - uint64_t t3 = MLogTick(); + if(XranGetSymNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT) == 3){ + if(p_xran_dev_ctx->phy_tti_cb_done == 0){ /* rearm timer to deliver TTI event to PHY */ - p_xran_lib_ctx->phy_tti_cb_done = 0; - xran_timer_arm_ex(&tti_to_phy_timer[xran_lib_ota_tti % 10], tti_to_phy_cb, (void*)pTCtx, p_xran_lib_ctx->xran_init_cfg.io_cfg.pkt_proc_core); - MLogTask(PID_TIME_ARM_TIMER, t3, MLogTick()); + p_xran_dev_ctx->phy_tti_cb_done = 0; + xran_timer_arm_ex(&tti_to_phy_timer[xran_lib_ota_tti % 10], tti_to_phy_cb, (void*)pTCtx, p_xran_dev_ctx->fh_init.io_cfg.timing_core); } } xran_process_tx_sym(timer_ctx); /* check if there is call back to do something else on this symbol */ - if(p_xran_lib_ctx->pSymCallback[0][xran_lib_ota_sym]) - p_xran_lib_ctx->pSymCallback[0][xran_lib_ota_sym](&tx_cp_dl_timer, p_xran_lib_ctx->pSymCallbackTag[0][xran_lib_ota_sym]); + if(p_xran_dev_ctx->pSymCallback[0][xran_lib_ota_sym]){ + p_xran_dev_ctx->pSymCallback[0][xran_lib_ota_sym](&dpdk_timer[ctx], p_xran_dev_ctx->pSymCallbackTag[0][xran_lib_ota_sym]); + ctx = DpdkTiemerIncrementCtx(ctx); + } xran_lib_ota_sym++; if(xran_lib_ota_sym >= N_SYM_PER_SLOT){ xran_lib_ota_sym=0; } + MLogTask(PID_SYM_OTA_CB, t1, MLogTick()); } @@ -267,13 +531,13 @@ void tti_ota_cb(struct rte_timer *tim, void *arg) uint64_t t3 = 0; uint32_t reg_tti = 0; struct xran_timer_ctx *pTCtx = (struct xran_timer_ctx *)arg; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); MLogTask(PID_TTI_TIMER, t1, MLogTick()); /* To match TTbox */ if(xran_lib_ota_tti == 0) - reg_tti = 8000-1; + reg_tti = xran_fs_get_max_slot() - 1; else reg_tti = xran_lib_ota_tti -1; MLogIncrementCounter(); @@ -298,38 +562,33 @@ void tti_ota_cb(struct rte_timer *tim, void *arg) mlogVar[mlogVarCnt++] = 0; MLogAddVariables(mlogVarCnt, mlogVar, MLogTick()); - if(p_xran_lib_ctx->xran_init_cfg.io_cfg.id == ID_LLS_CU) + if(p_xran_dev_ctx->fh_init.io_cfg.id == ID_LLS_CU) next_tti = xran_lib_ota_tti + 1; else next_tti = xran_lib_ota_tti; - if(next_tti>= SLOTNUM_PER_SUBFRAME*1000){ + if(next_tti>= xran_fs_get_max_slot()){ print_dbg("[%d]SFN %d sf %d slot %d\n",next_tti, frame_id, subframe_id, slot_id); next_tti=0; } - /* [0 - 7] */ + slot_id = XranGetSlotNum(next_tti, SLOTNUM_PER_SUBFRAME); - /* sf [0 - 9] */ subframe_id = XranGetSubFrameNum(next_tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); - /* frame [0 - 99] for now */ frame_id = XranGetFrameNum(next_tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); print_dbg("[%d]SFN %d sf %d slot %d\n",next_tti, frame_id, subframe_id, slot_id); - if(p_xran_lib_ctx->xran_init_cfg.io_cfg.id == ID_LLS_CU){ + if(p_xran_dev_ctx->fh_init.io_cfg.id == ID_LLS_CU){ pTCtx[(xran_lib_ota_tti & 1)].tti_to_process = next_tti; } else { pTCtx[(xran_lib_ota_tti & 1)].tti_to_process = pTCtx[(xran_lib_ota_tti & 1)^1].tti_to_process; } - t3 = MLogTick(); - p_xran_lib_ctx->phy_tti_cb_done = 0; - xran_timer_arm_ex(&tti_to_phy_timer[xran_lib_ota_tti % 10], tti_to_phy_cb, (void*)pTCtx, p_xran_lib_ctx->xran_init_cfg.io_cfg.pkt_proc_core); - MLogTask(PID_TIME_ARM_TIMER, t3, MLogTick()); + p_xran_dev_ctx->phy_tti_cb_done = 0; + xran_timer_arm_ex(&tti_to_phy_timer[xran_lib_ota_tti % 10], tti_to_phy_cb, (void*)pTCtx, p_xran_dev_ctx->fh_init.io_cfg.timing_core); xran_lib_ota_tti++; - /* within 1 sec we have 8000 TTIs as 1000ms/0.125ms where TTI is 125us*/ - if(xran_lib_ota_tti >= SLOTNUM_PER_SUBFRAME*1000){ + if(xran_lib_ota_tti >= xran_fs_get_max_slot()){ print_dbg("[%d]SFN %d sf %d slot %d\n",xran_lib_ota_tti, frame_id, subframe_id, slot_id); xran_lib_ota_tti=0; } @@ -338,87 +597,309 @@ void tti_ota_cb(struct rte_timer *tim, void *arg) void xran_timer_arm(struct rte_timer *tim, void* arg) { - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + uint64_t t3 = MLogTick(); + if (xran_if_current_state == XRAN_RUNNING){ rte_timer_cb_t fct = (rte_timer_cb_t)arg; - rte_timer_reset_sync(tim, 0, SINGLE, p_xran_lib_ctx->xran_init_cfg.io_cfg.pkt_proc_core, fct, timer_ctx); + rte_timer_init(tim); + rte_timer_reset_sync(tim, 0, SINGLE, p_xran_dev_ctx->fh_init.io_cfg.timing_core, fct, &timer_ctx[0]); } + MLogTask(PID_TIME_ARM_TIMER, t3, MLogTick()); } void xran_timer_arm_ex(struct rte_timer *tim, void* CbFct, void *CbArg, unsigned tim_lcore) { - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + uint64_t t3 = MLogTick(); + if (xran_if_current_state == XRAN_RUNNING){ rte_timer_cb_t fct = (rte_timer_cb_t)CbFct; rte_timer_init(tim); rte_timer_reset_sync(tim, 0, SINGLE, tim_lcore, fct, CbArg); } + MLogTask(PID_TIME_ARM_TIMER, t3, MLogTick()); } +int xran_cp_create_and_send_section(void *pHandle, uint8_t ru_port_id, int dir, int tti, int cc_id, + struct xran_prb_map *prbMapElem) +{ + struct xran_cp_gen_params params; + struct xran_section_gen_info sect_geninfo[XRAN_MAX_NUM_SECTIONS]; + struct rte_mbuf *mbuf; + int ret = 0; + uint32_t i; + uint32_t nsection = prbMapElem->nPrbElm; + struct xran_prb_elm *pPrbMapElem = &prbMapElem->prbMap[0]; + struct xran_prb_elm *pPrbMapElemPrev; + uint32_t slot_id = XranGetSlotNum(tti, SLOTNUM_PER_SUBFRAME); + uint32_t subframe_id = XranGetSubFrameNum(tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); + uint32_t frame_id = XranGetFrameNum(tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); + uint8_t seq_id = xran_get_cp_seqid(pHandle, XRAN_DIR_DL, cc_id, ru_port_id); + + params.dir = dir; + params.sectionType = XRAN_CP_SECTIONTYPE_1; // Most DL/UL Radio Channels + params.hdr.filterIdx = XRAN_FILTERINDEX_STANDARD; + params.hdr.frameId = frame_id; + params.hdr.subframeId = subframe_id; + params.hdr.slotId = slot_id; + params.hdr.startSymId = pPrbMapElem->nStartSymb; + params.hdr.iqWidth = xran_get_conf_iqwidth(pHandle); + params.hdr.compMeth = pPrbMapElem->compMethod; + + for (i=0; iprbMap[i]; + sect_geninfo[i].info.type = params.sectionType; // for database + sect_geninfo[i].info.startSymId = params.hdr.startSymId; // for database + sect_geninfo[i].info.iqWidth = params.hdr.iqWidth; // for database + sect_geninfo[i].info.compMeth = params.hdr.compMeth; // for database + sect_geninfo[i].info.id = xran_alloc_sectionid(pHandle, dir, cc_id, ru_port_id, slot_id); + sect_geninfo[i].info.rb = XRAN_RBIND_EVERY; + sect_geninfo[i].info.startPrbc = pPrbMapElem->nRBStart; + sect_geninfo[i].info.numPrbc = pPrbMapElem->nRBSize; + sect_geninfo[i].info.numSymbol = pPrbMapElem->numSymb; + sect_geninfo[i].info.reMask = 0xfff; + sect_geninfo[i].info.beamId = pPrbMapElem->nBeamIndex; + if (i==0) + sect_geninfo[i].info.symInc = XRAN_SYMBOLNUMBER_NOTINC; + else + { + pPrbMapElemPrev = &prbMapElem->prbMap[i-1]; + if (pPrbMapElemPrev->nStartSymb == pPrbMapElem->nStartSymb) + { + sect_geninfo[i].info.symInc = XRAN_SYMBOLNUMBER_NOTINC; + if (pPrbMapElemPrev->numSymb != pPrbMapElem->numSymb) + print_err("section info error: previous numSymb %d not equal to current numSymb %d\n", pPrbMapElemPrev->numSymb, pPrbMapElem->numSymb); + } + else + { + sect_geninfo[i].info.symInc = XRAN_SYMBOLNUMBER_INC; + if (pPrbMapElem->nStartSymb != (pPrbMapElemPrev->nStartSymb + pPrbMapElemPrev->numSymb)) + print_err("section info error: current startSym %d not equal to previous endSymb %d\n", pPrbMapElem->nStartSymb, pPrbMapElemPrev->nStartSymb + pPrbMapElemPrev->numSymb); + } + } + + /* extension is not supported */ + sect_geninfo[nsection].info.ef = 0; + sect_geninfo[nsection].exDataSize = 0; + //sect_geninfo[nsection].exData = NULL; + } + params.numSections = nsection; + params.sections = sect_geninfo; + + mbuf = xran_ethdi_mbuf_alloc(); + if(unlikely(mbuf == NULL)) { + print_err("Alloc fail!\n"); + return (-1); + } + + ret = xran_prepare_ctrl_pkt(mbuf, ¶ms, cc_id, ru_port_id, seq_id); + if(ret < 0) { + print_err("Fail to build control plane packet - [%d:%d:%d] dir=%d\n", + frame_id, subframe_id, slot_id, dir); + } else { + /* add in the ethernet header */ + struct ether_hdr *const h = (void *)rte_pktmbuf_prepend(mbuf, sizeof(*h)); + xran_ethdi_mbuf_send_cp(mbuf, ETHER_TYPE_ECPRI); + tx_counter++; + for(i=0; icc_id; + tti = prb_map->tti_id; + dir = prb_map->dir; + + + list_index = -1; + for(sym_id = 0; sym_id < N_SYM_PER_SLOT; sym_id++) { + /* skip symbol, if not matched with given direction */ + int type_sym = xran_fs_get_symbol_type(cc_id, tti, sym_id); + if(type_sym != XRAN_SYMBOL_TYPE_FDD && type_sym != dir) + continue; + + /* retrieve the information of RB allocation */ + prb_map = (struct xran_prb_map *)prbMapElem[sym_id].pData; + if(unlikely(prb_map == NULL)) { + print_err("RB allocation table is NULL! (tti:%d, cc:%d, sym_id:%d)", tti, cc_id, sym_id); + continue; + } + + /* creating 2D mapping */ + for(i=0; i < prb_map->nPrbElm; i++) { + if(list_index < 0) { /* create first entry */ + list_index = 0; + rbMapList[list_index].grp_id = 0; + rbMapList[list_index].sym_start = sym_id; // prb_map->sym_id + rbMapList[list_index].sym_num = 1; + rbMapList[list_index].rb_start = prb_map->prbMap[i].nRBStart; + rbMapList[list_index].rb_num = prb_map->prbMap[i].nRBSize; + rbMapList[list_index].beam_id = prb_map->prbMap[i].nBeamIndex; + rbMapList[list_index].comp_meth = prb_map->prbMap[i].compMethod; + } + else { + /* find consecutive allocation from list */ + for(j=0; jprbMap[i].nRBStart != rbMapList[j].rb_start + || prb_map->prbMap[i].nRBSize != rbMapList[j].rb_num + || prb_map->prbMap[i].nBeamIndex != rbMapList[j].beam_id + || prb_map->prbMap[i].compMethod != rbMapList[j].comp_meth + || sym_id != (rbMapList[j].sym_start+rbMapList[j].sym_num)) { + /* move to next */ + continue; + } + else { + /* consecutive allocation has been found */ + rbMapList[j].sym_num++; + break; + } + } + + if(j == list_index+1) { /* different allocation, create new entry */ + list_index++; + rbMapList[list_index].grp_id = 0; + rbMapList[list_index].sym_start = sym_id; // prb_map->sym_id + rbMapList[list_index].sym_num = 1; + rbMapList[list_index].rb_start = prb_map->prbMap[i].nRBStart; + rbMapList[list_index].rb_num = prb_map->prbMap[i].nRBSize; + rbMapList[list_index].beam_id = prb_map->prbMap[i].nBeamIndex; + rbMapList[list_index].comp_meth = prb_map->prbMap[i].compMethod; + } + } + } /* for(i=0; i < prb_map->nPrbElm; i++) */ + } /* for(sym_id = 0; sym_id < N_SYM_PER_SLOT; sym_id++) */ + + list_index++; + +#if 0 + for(i=0; ienableCP) { + if(p_xran_dev_ctx->enableCP) { tti = pTCtx[(xran_lib_ota_tti & 1) ^ 1].tti_to_process; + buf_id = tti % XRAN_N_FE_BUF_LEN; slot_id = XranGetSlotNum(tti, SLOTNUM_PER_SUBFRAME); subframe_id = XranGetSubFrameNum(tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); frame_id = XranGetFrameNum(tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); + ctx_id = XranGetSlotNum(tti, SLOTS_PER_SYSTEMFRAME) % XRAN_MAX_SECTIONDB_CTX; print_dbg("[%d]SFN %d sf %d slot %d\n", tti, frame_id, subframe_id, slot_id); for(ant_id = 0; ant_id < num_eAxc; ++ant_id) { for(cc_id = 0; cc_id < num_CCPorts; cc_id++ ) { - // start new section information list - xran_cp_reset_section_info(pHandle, XRAN_DIR_DL, cc_id, ant_id); - beam_id = xran_get_beamid(pHandle, XRAN_DIR_DL, cc_id, ant_id, slot_id); + /* start new section information list */ + xran_cp_reset_section_info(pHandle, XRAN_DIR_DL, cc_id, ant_id, ctx_id); + + if(xran_fs_get_slot_type(cc_id, tti, XRAN_SLOT_TYPE_DL) == 1) { // 1 when FDD, DL slot or DL symbol is present in SP slot + if (p_xran_dev_ctx->DynamicSectionEna){ + num_list = xran_cp_create_and_send_section(pHandle, ant_id, XRAN_SYMBOL_TYPE_DL, tti, cc_id, + (struct xran_prb_map *)p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList.pBuffers->pData); + } + else{ + struct xran_cp_gen_params params; + struct xran_section_gen_info sect_geninfo[8]; + struct rte_mbuf *mbuf = xran_ethdi_mbuf_alloc(); + + /* use symb 0 only with constant RBs for full slot */ + struct xran_prb_map *prb_map = (struct xran_prb_map *)p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList.pBuffers->pData; + num_list = 1; + rb_map_list[0].sym_start = 0; + rb_map_list[0].sym_num = 14; + rb_map_list[0].rb_start = prb_map->prbMap[0].nRBStart; + rb_map_list[0].rb_num = prb_map->prbMap[0].nRBSize; + rb_map_list[0].beam_id = prb_map->prbMap[0].nBeamIndex; + rb_map_list[0].comp_meth = prb_map->prbMap[0].compMethod; + + for(i=0; ienableCP) */ - send_cpmsg_dlul(pHandle, XRAN_DIR_DL, - frame_id, subframe_id, slot_id, - 0, N_SYM_PER_SLOT, NUM_OF_PRB_IN_FULL_BAND, - beam_id, cc_id, ant_id, - xran_get_seqid(pHandle, XRAN_DIR_DL, cc_id, ant_id, slot_id)); - } - } - } MLogTask(PID_CP_DL_CB, t1, MLogTick()); } void rx_ul_deadline_half_cb(struct rte_timer *tim, void *arg) { long t1 = MLogTick(); - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); - XranStatusInt32 status; + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + xran_status_t status; /* half of RX for current TTI as measured against current OTA time */ int32_t rx_tti = (int32_t)XranGetTtiNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT); + int32_t cc_id; - if(p_xran_lib_ctx->xran2phy_mem_ready == 0) + if(p_xran_dev_ctx->xran2phy_mem_ready == 0) return; - if(p_xran_lib_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][0] == 0){ - status = (rx_tti << 16) | 0; /* base on PHY side implementation first 7 sym of slot */ - if(p_xran_lib_ctx->pCallback[0]) - p_xran_lib_ctx->pCallback[0](p_xran_lib_ctx->pCallbackTag[0], status); - } else { - p_xran_lib_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][0] = 0; + for(cc_id = 0; cc_id < xran_get_num_cc(p_xran_dev_ctx); cc_id++) { + if(p_xran_dev_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][cc_id] == 0){ + status = (rx_tti << 16) | 0; /* base on PHY side implementation first 7 sym of slot */ + if(p_xran_dev_ctx->pCallback[cc_id]) + p_xran_dev_ctx->pCallback[cc_id](p_xran_dev_ctx->pCallbackTag[cc_id], status); + } else { + p_xran_dev_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][cc_id] = 0; + } } MLogTask(PID_UP_UL_HALF_DEAD_LINE_CB, t1, MLogTick()); } @@ -426,24 +907,28 @@ void rx_ul_deadline_half_cb(struct rte_timer *tim, void *arg) void rx_ul_deadline_full_cb(struct rte_timer *tim, void *arg) { long t1 = MLogTick(); - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); - XranStatusInt32 status; + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + xran_status_t status = 0; int32_t rx_tti = (int32_t)XranGetTtiNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT); + int32_t cc_id = 0; - if(rx_tti >= 8000-1) - rx_tti = 0; + if(rx_tti == 0) + rx_tti = (xran_fs_get_max_slot()-1); else rx_tti -= 1; /* end of RX for prev TTI as measured against current OTA time */ - if(p_xran_lib_ctx->xran2phy_mem_ready == 0) + if(p_xran_dev_ctx->xran2phy_mem_ready == 0) return; - if(p_xran_lib_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][0] == 0){ + /* U-Plane */ + for(cc_id = 0; cc_id < xran_get_num_cc(p_xran_dev_ctx); cc_id++) { status = (rx_tti << 16) | 7; /* last 7 sym means full slot of Symb */ - if(p_xran_lib_ctx->pCallback[0]) - p_xran_lib_ctx->pCallback[0](p_xran_lib_ctx->pCallbackTag[0], status); - } else { - p_xran_lib_ctx->rx_packet_callback_tracker[rx_tti % XRAN_N_FE_BUF_LEN][0] = 0; + if(p_xran_dev_ctx->pCallback[cc_id]) + p_xran_dev_ctx->pCallback[cc_id](p_xran_dev_ctx->pCallbackTag[cc_id], status); + + if(p_xran_dev_ctx->pPrachCallback[cc_id]) + p_xran_dev_ctx->pPrachCallback[cc_id](p_xran_dev_ctx->pPrachCallbackTag[cc_id], status); + } MLogTask(PID_UP_UL_FULL_DEAD_LINE_CB, t1, MLogTick()); @@ -453,69 +938,105 @@ void rx_ul_deadline_full_cb(struct rte_timer *tim, void *arg) void tx_cp_ul_cb(struct rte_timer *tim, void *arg) { long t1 = MLogTick(); - int sym, tti; - uint32_t frame_id = 0; - uint32_t subframe_id = 0; - uint32_t slot_id = 0; - + int tti, buf_id; + int i, ret; + uint32_t slot_id, subframe_id, frame_id; int32_t cc_id; int ant_id, prach_port_id; uint16_t beam_id; uint8_t num_eAxc, num_CCPorts; + uint8_t ctx_id; void *pHandle; + int num_list; + struct xran_cp_rbmap_list rb_map_list[XRAN_MAX_PRBS*N_SYM_PER_SLOT]; /* array size can be reduced */ - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); - xRANPrachCPConfigStruct *pPrachCPConfig = &(p_xran_lib_ctx->PrachCPConfig); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + xRANPrachCPConfigStruct *pPrachCPConfig = &(p_xran_dev_ctx->PrachCPConfig); struct xran_timer_ctx *pTCtx = (struct xran_timer_ctx *)arg; pHandle = NULL; // TODO: temp implemantation num_eAxc = xran_get_num_eAxc(pHandle); num_CCPorts = xran_get_num_cc(pHandle); + tti = pTCtx[(xran_lib_ota_tti & 1) ^ 1].tti_to_process; + buf_id = tti % XRAN_N_FE_BUF_LEN; + slot_id = XranGetSlotNum(tti, SLOTNUM_PER_SUBFRAME); + subframe_id = XranGetSubFrameNum(tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); + frame_id = XranGetFrameNum(tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); + ctx_id = XranGetSlotNum(tti, SLOTS_PER_SYSTEMFRAME) % XRAN_MAX_SECTIONDB_CTX; - if (p_xran_lib_ctx->enableCP){ - tti = pTCtx[(xran_lib_ota_tti & 1) ^ 1].tti_to_process; - slot_id = XranGetSlotNum(tti, SLOTNUM_PER_SUBFRAME); - subframe_id = XranGetSubFrameNum(tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); - frame_id = XranGetFrameNum(tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); - print_dbg("[%d]SFN %d sf %d slot %d\n", tti, frame_id, subframe_id, slot_id); + if(p_xran_dev_ctx->enableCP) { + print_dbg("[%d]SFN %d sf %d slot %d\n", tti, frame_id, subframe_id, slot_id); for(ant_id = 0; ant_id < num_eAxc; ++ant_id) { - for(cc_id = 0; cc_id < num_CCPorts; cc_id++ ) { - // start new section information list - xran_cp_reset_section_info(pHandle, XRAN_DIR_UL, cc_id, ant_id); + for(cc_id = 0; cc_id < num_CCPorts; cc_id++) { + if(xran_fs_get_slot_type(cc_id, tti, XRAN_SLOT_TYPE_UL) == 1 || + xran_fs_get_slot_type(cc_id, tti, XRAN_SLOT_TYPE_SP) == 1 ){ + /* start new section information list */ + xran_cp_reset_section_info(pHandle, XRAN_DIR_UL, cc_id, ant_id, ctx_id); + if (p_xran_dev_ctx->DynamicSectionEna){ + /* create a map of RB allocation to generate proper C-Plane */ + num_list = xran_cp_create_and_send_section(pHandle, ant_id, XRAN_SYMBOL_TYPE_UL, tti, cc_id, + (struct xran_prb_map *)p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList.pBuffers->pData); + } + else{ + struct xran_cp_gen_params params; + struct xran_section_gen_info sect_geninfo[8]; + struct rte_mbuf *mbuf = xran_ethdi_mbuf_alloc(); + /* use symb 0 only with constant RBs for full slot */ + struct xran_prb_map *prb_map = (struct xran_prb_map *)p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList.pBuffers->pData; + num_list = 1; + rb_map_list[0].sym_start = 0; + rb_map_list[0].sym_num = 14; + rb_map_list[0].rb_start = prb_map->prbMap[0].nRBStart; + rb_map_list[0].rb_num = prb_map->prbMap[0].nRBSize; + rb_map_list[0].beam_id = prb_map->prbMap[0].nBeamIndex; + rb_map_list[0].comp_meth = prb_map->prbMap[0].compMethod; + + for(i=0; ienableCP) */ - if ((frame_id % pPrachCPConfig->x == pPrachCPConfig->y[0]) && (pPrachCPConfig->isPRACHslot[slot_id]==1)) //is prach slot - { - for(ant_id = 0; ant_id < num_eAxc; ant_id++) { + if(p_xran_dev_ctx->enablePrach) { + uint32_t isPRACHslot = xran_isPRACHSlot(subframe_id, slot_id); + if((frame_id % pPrachCPConfig->x == pPrachCPConfig->y[0]) && (isPRACHslot==1)) { //is prach slot + for(ant_id = 0; ant_id < num_eAxc; ++ant_id) { for(cc_id = 0; cc_id < num_CCPorts; cc_id++) { -#if !defined(PRACH_USES_SHARED_PORT) + struct xran_cp_gen_params params; + struct xran_section_gen_info sect_geninfo[8]; + struct rte_mbuf *mbuf = xran_ethdi_mbuf_alloc(); prach_port_id = ant_id + num_eAxc; - // start new section information list - xran_cp_reset_section_info(pHandle, XRAN_DIR_UL, cc_id, prach_port_id); -#else - prach_port_id = ant_id; -#endif - beam_id = xran_get_beamid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, slot_id); - send_cpmsg_prach(pHandle, - frame_id, subframe_id, slot_id, - beam_id, cc_id, prach_port_id, - xran_get_seqid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, slot_id)); + /* start new section information list */ + xran_cp_reset_section_info(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, ctx_id); + + beam_id = xran_get_beamid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id, slot_id); /* TODO: */ + ret = generate_cpmsg_prach(pHandle, ¶ms, sect_geninfo, mbuf, p_xran_dev_ctx, + frame_id, subframe_id, slot_id, + beam_id, cc_id, prach_port_id, + xran_get_cp_seqid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id)); + if (ret == XRAN_STATUS_SUCCESS) + send_cpmsg(pHandle, mbuf, ¶ms, sect_geninfo, + cc_id, prach_port_id, xran_get_cp_seqid(pHandle, XRAN_DIR_UL, cc_id, prach_port_id)); } } } - } + MLogTask(PID_CP_UL_CB, t1, MLogTick()); } @@ -529,22 +1050,22 @@ void ul_up_full_slot_cb(struct rte_timer *tim, void *arg) void tti_to_phy_cb(struct rte_timer *tim, void *arg) { long t1 = MLogTick(); - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); static int first_call = 0; - p_xran_lib_ctx->phy_tti_cb_done = 1; /* DPDK called CB */ + p_xran_dev_ctx->phy_tti_cb_done = 1; /* DPDK called CB */ if (first_call){ - if(p_xran_lib_ctx->ttiCb[XRAN_CB_TTI]){ - if(p_xran_lib_ctx->SkipTti[XRAN_CB_TTI] <= 0){ - p_xran_lib_ctx->ttiCb[XRAN_CB_TTI](p_xran_lib_ctx->TtiCbParam[XRAN_CB_TTI]); + if(p_xran_dev_ctx->ttiCb[XRAN_CB_TTI]){ + if(p_xran_dev_ctx->SkipTti[XRAN_CB_TTI] <= 0){ + p_xran_dev_ctx->ttiCb[XRAN_CB_TTI](p_xran_dev_ctx->TtiCbParam[XRAN_CB_TTI]); }else{ - p_xran_lib_ctx->SkipTti[XRAN_CB_TTI]--; + p_xran_dev_ctx->SkipTti[XRAN_CB_TTI]--; } } } else { - if(p_xran_lib_ctx->ttiCb[XRAN_CB_TTI]){ + if(p_xran_dev_ctx->ttiCb[XRAN_CB_TTI]){ int32_t tti = (int32_t)XranGetTtiNum(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT); - if(tti == 8000-1) + if(tti == xran_fs_get_max_slot()-1) first_call = 1; } } @@ -570,7 +1091,7 @@ int xran_timing_source_thread(void *args) uint32_t sym_up_ul; int32_t sym_up; struct sched_param sched_param; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); /* ToS = Top of Second start +- 1.5us */ struct timespec ts; @@ -583,7 +1104,7 @@ int xran_timing_source_thread(void *args) sched_param.sched_priority = 98; CPU_ZERO(&cpuset); - CPU_SET(p_xran_lib_ctx->xran_init_cfg.io_cfg.timing_core, &cpuset); + CPU_SET(p_xran_dev_ctx->fh_init.io_cfg.timing_core, &cpuset); if (result1 = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)) { printf("pthread_setaffinity_np failed: coreId = 2, result1 = %d\n",result1); @@ -593,7 +1114,7 @@ int xran_timing_source_thread(void *args) printf("priority is not changed: coreId = 2, result1 = %d\n",result1); } - if (p_xran_lib_ctx->xran_init_cfg.io_cfg.id == APP_LLS_CU) { + if (p_xran_dev_ctx->fh_init.io_cfg.id == O_DU) { do { timespec_get(&ts, TIME_UTC); }while (ts.tv_nsec >1500); @@ -603,18 +1124,18 @@ int xran_timing_source_thread(void *args) printf("lls-CU: thread_run start time: %s.%09ld UTC [%ld]\n", buff, ts.tv_nsec, interval_us); } - delay_cp_dl = p_xran_lib_ctx->xran_init_cfg.ttiPeriod - p_xran_lib_ctx->xran_init_cfg.T1a_max_cp_dl; - delay_cp_ul = p_xran_lib_ctx->xran_init_cfg.ttiPeriod - p_xran_lib_ctx->xran_init_cfg.T1a_max_cp_ul; - delay_up = p_xran_lib_ctx->xran_init_cfg.T1a_max_up; - delay_up_ul = p_xran_lib_ctx->xran_init_cfg.Ta4_max; + delay_cp_dl = interval_us - p_xran_dev_ctx->fh_init.T1a_max_cp_dl; + delay_cp_ul = interval_us - p_xran_dev_ctx->fh_init.T1a_max_cp_ul; + delay_up = p_xran_dev_ctx->fh_init.T1a_max_up; + delay_up_ul = p_xran_dev_ctx->fh_init.Ta4_max; delay_cp2up = delay_up-delay_cp_dl; sym_cp_dl = delay_cp_dl*1000/(interval_us*1000/N_SYM_PER_SLOT)+1; sym_cp_ul = delay_cp_ul*1000/(interval_us*1000/N_SYM_PER_SLOT)+1; sym_up_ul = delay_up_ul*1000/(interval_us*1000/N_SYM_PER_SLOT); - p_xran_lib_ctx->sym_up = sym_up = -(delay_up*1000/(interval_us*1000/N_SYM_PER_SLOT)+1); - p_xran_lib_ctx->sym_up_ul = sym_up_ul = (delay_up_ul*1000/(interval_us*1000/N_SYM_PER_SLOT)+1); + p_xran_dev_ctx->sym_up = sym_up = -(delay_up*1000/(interval_us*1000/N_SYM_PER_SLOT)+1); + p_xran_dev_ctx->sym_up_ul = sym_up_ul = (delay_up_ul*1000/(interval_us*1000/N_SYM_PER_SLOT)+1); printf("Start C-plane DL %d us after TTI [trigger on sym %d]\n", delay_cp_dl, sym_cp_dl); printf("Start C-plane UL %d us after TTI [trigger on sym %d]\n", delay_cp_ul, sym_cp_ul); @@ -624,24 +1145,24 @@ int xran_timing_source_thread(void *args) printf("C-plane to U-plane delay %d us after TTI\n", delay_cp2up); printf("Start Sym timer %ld ns\n", TX_TIMER_INTERVAL/N_SYM_PER_SLOT); - p_xran_lib_ctx->pSymCallback[0][sym_cp_dl] = xran_timer_arm; - p_xran_lib_ctx->pSymCallbackTag[0][sym_cp_dl] = tx_cp_dl_cb; + p_xran_dev_ctx->pSymCallback[0][sym_cp_dl] = xran_timer_arm; + p_xran_dev_ctx->pSymCallbackTag[0][sym_cp_dl] = tx_cp_dl_cb; - p_xran_lib_ctx->pSymCallback[0][sym_cp_ul] = xran_timer_arm; - p_xran_lib_ctx->pSymCallbackTag[0][sym_cp_ul] = tx_cp_ul_cb; + p_xran_dev_ctx->pSymCallback[0][sym_cp_ul] = xran_timer_arm; + p_xran_dev_ctx->pSymCallbackTag[0][sym_cp_ul] = tx_cp_ul_cb; /* Full slot UL OTA + delay_up_ul */ - p_xran_lib_ctx->pSymCallback[0][sym_up_ul] = xran_timer_arm; - p_xran_lib_ctx->pSymCallbackTag[0][sym_up_ul] = rx_ul_deadline_full_cb; + p_xran_dev_ctx->pSymCallback[0][sym_up_ul] = xran_timer_arm; + p_xran_dev_ctx->pSymCallbackTag[0][sym_up_ul] = rx_ul_deadline_full_cb; /* Half slot UL OTA + delay_up_ul*/ - p_xran_lib_ctx->pSymCallback[0][sym_up_ul + N_SYM_PER_SLOT/2] = xran_timer_arm; - p_xran_lib_ctx->pSymCallbackTag[0][sym_up_ul + N_SYM_PER_SLOT/2] = rx_ul_deadline_half_cb; + p_xran_dev_ctx->pSymCallback[0][sym_up_ul + N_SYM_PER_SLOT/2] = xran_timer_arm; + p_xran_dev_ctx->pSymCallbackTag[0][sym_up_ul + N_SYM_PER_SLOT/2] = rx_ul_deadline_half_cb; - } else { // APP_RU + } else { // APP_O_RU /* calcualte when to send UL U-plane */ - delay_up = p_xran_lib_ctx->xran_init_cfg.Ta3_min; - p_xran_lib_ctx->sym_up = sym_up = delay_up*1000/(interval_us*1000/N_SYM_PER_SLOT)+1; + delay_up = p_xran_dev_ctx->fh_init.Ta3_min; + p_xran_dev_ctx->sym_up = sym_up = delay_up*1000/(interval_us*1000/N_SYM_PER_SLOT)+1; printf("Start UL U-plane %d us after OTA [offset in sym %d]\n", delay_up, sym_up); do { timespec_get(&ts, TIME_UTC); @@ -653,6 +1174,7 @@ int xran_timing_source_thread(void *args) } } + printf("interval_us %ld\n", interval_us); do { timespec_get(&ts, TIME_UTC); }while (ts.tv_nsec == 0); @@ -661,9 +1183,11 @@ int xran_timing_source_thread(void *args) delta = poll_next_tick(interval_us*1000L/N_SYM_PER_SLOT); if (XRAN_STOPPED == xran_if_current_state) break; - sym_ota_cb(&sym_timer, timer_ctx); + + if (likely(XRAN_RUNNING == xran_if_current_state)) + sym_ota_cb(&sym_timer, timer_ctx); } - printf("Closing timing source thread...\n"); + printf("Closing timing source thread...tx counter %lu, rx counter %lu\n", tx_counter, rx_counter); return 0; } @@ -673,45 +1197,45 @@ int handle_ecpri_ethertype(struct rte_mbuf *pkt, uint64_t rx_time) { const struct xran_ecpri_hdr *ecpri_hdr; unsigned long t1; + int32_t ret = MBUF_FREE; if (rte_pktmbuf_data_len(pkt) < sizeof(struct xran_ecpri_hdr)) { - wlog("Packet too short - %d bytes", rte_pktmbuf_data_len(pkt)); + print_err("Packet too short - %d bytes", rte_pktmbuf_data_len(pkt)); return 0; } /* check eCPRI header. */ ecpri_hdr = rte_pktmbuf_mtod(pkt, struct xran_ecpri_hdr *); - if(ecpri_hdr == NULL) + if(ecpri_hdr == NULL){ + print_err("ecpri_hdr error\n"); return MBUF_FREE; + } - switch(ecpri_hdr->ecpri_mesg_type) { + switch(ecpri_hdr->cmnhdr.ecpri_mesg_type) { case ECPRI_IQ_DATA: - t1 = MLogTick(); - process_mbuf(pkt); - MLogTask(PID_PROCESS_UP_PKT, t1, MLogTick()); + // t1 = MLogTick(); + ret = process_mbuf(pkt); + // MLogTask(PID_PROCESS_UP_PKT, t1, MLogTick()); break; // For RU emulation case ECPRI_RT_CONTROL_DATA: t1 = MLogTick(); - if(xran_lib_get_ctx()->xran_init_cfg.io_cfg.id == APP_RU) { - process_cplane(pkt); + if(xran_dev_get_ctx()->fh_init.io_cfg.id == O_RU) { + ret = process_cplane(pkt); } else { - print_err("LLS-CU recevied CP message!"); + print_err("O-DU recevied C-Plane message!"); } MLogTask(PID_PROCESS_CP_PKT, t1, MLogTick()); break; default: - wlog("Invalid eCPRI message type - %d", ecpri_hdr->ecpri_mesg_type); + print_err("Invalid eCPRI message type - %d", ecpri_hdr->cmnhdr.ecpri_mesg_type); } -#if 0 -//def DEBUG - return MBUF_KEEP; -#else - return MBUF_FREE; -#endif + + return ret; } -int xran_process_rx_sym(void *arg, +int xran_process_prach_sym(void *arg, + struct rte_mbuf *mbuf, void *iq_data_start, uint16_t size, uint8_t CC_ID, @@ -719,37 +1243,48 @@ int xran_process_rx_sym(void *arg, uint8_t frame_id, uint8_t subframe_id, uint8_t slot_id, - uint8_t symb_id) + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id) { char *pos = NULL; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); - uint32_t tti=0; - XranStatusInt32 status; + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + uint8_t symb_id_offset; + uint32_t tti = 0; + xran_status_t status; void *pHandle = NULL; + struct rte_mbuf *mb; - if(p_xran_lib_ctx->xran2phy_mem_ready == 0) + uint16_t iq_sample_size_bits = 16; + + if(p_xran_dev_ctx->xran2phy_mem_ready == 0) return 0; tti = frame_id * SLOTS_PER_SYSTEMFRAME + subframe_id * SLOTNUM_PER_SUBFRAME + slot_id; status = tti << 16 | symb_id; - if(tti < 8000 && CC_ID < XRAN_MAX_SECTOR_NR && CC_ID == 0 && Ant_ID < XRAN_MAX_ANTENNA_NR && symb_id < XRAN_NUM_OF_SYMBOL_PER_SLOT){ - pos = (char*) p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id].pData; + if(tti < xran_fs_get_max_slot() && CC_ID < XRAN_MAX_SECTOR_NR && Ant_ID < XRAN_MAX_ANTENNA_NR && symb_id < XRAN_NUM_OF_SYMBOL_PER_SLOT){ + symb_id_offset = symb_id - p_xran_dev_ctx->prach_start_symbol[CC_ID]; //make the storing of prach packets to start from 0 for easy of processing within PHY + pos = (char*) p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id_offset].pData; if(pos && iq_data_start && size){ -#ifdef XRAN_BYTE_ORDER_SWAP - int idx = 0; - uint16_t *restrict psrc = (uint16_t *)iq_data_start; - uint16_t *restrict pdst = (uint16_t *)pos; - /* network byte (be) order of IQ to CPU byte order (le) */ - for (idx = 0; idx < size/sizeof(int16_t); idx++){ - pdst[idx] = (psrc[idx]>>8) | (psrc[idx]<<8); //rte_be_to_cpu_16(psrc[idx]); + if (p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder == XRAN_CPU_LE_BYTE_ORDER) { + int idx = 0; + uint16_t *psrc = (uint16_t *)iq_data_start; + uint16_t *pdst = (uint16_t *)pos; + /* network byte (be) order of IQ to CPU byte order (le) */ + for (idx = 0; idx < size/sizeof(int16_t); idx++){ + pdst[idx] = (psrc[idx]>>8) | (psrc[idx]<<8); //rte_be_to_cpu_16(psrc[idx]); + } + }else { + mb = p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id_offset].pCtrl; + rte_pktmbuf_free(mb); + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id_offset].pData = iq_data_start; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id_offset].pCtrl = mbuf; } -#else -#error xran spec is network byte order - /* for debug */ - rte_memcpy(pdst, psrc, size); -#endif #ifdef DEBUG_XRAN_BUFFERS if (pos[0] != tti % XRAN_N_FE_BUF_LEN || pos[1] != CC_ID || @@ -764,25 +1299,176 @@ int xran_process_rx_sym(void *arg, } else { print_err("TTI %d(f_%d sf_%d slot_%d) CC %d Ant_ID %d symb_id %d\n",tti, frame_id, subframe_id, slot_id, CC_ID, Ant_ID, symb_id); } - if (symb_id == 7 || symb_id == 13){ - p_xran_lib_ctx->rx_packet_symb_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id]++; - if(p_xran_lib_ctx->rx_packet_symb_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id] >= xran_get_num_eAxc(pHandle)){ - if(p_xran_lib_ctx->pCallback[0]) - p_xran_lib_ctx->pCallback[0](p_xran_lib_ctx->pCallbackTag[0], status); - p_xran_lib_ctx->rx_packet_callback_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID] = 1; - p_xran_lib_ctx->rx_packet_symb_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id] = 0; +/* if (symb_id == p_xran_dev_ctx->prach_last_symbol[CC_ID] ){ + p_xran_dev_ctx->rx_packet_prach_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id]++; + if(p_xran_dev_ctx->rx_packet_prach_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id] >= xran_get_num_eAxc(pHandle)){ + if(p_xran_dev_ctx->pPrachCallback[0]) + p_xran_dev_ctx->pPrachCallback[0](p_xran_dev_ctx->pPrachCallbackTag[0], status); + p_xran_dev_ctx->rx_packet_prach_tracker[tti % XRAN_N_FE_BUF_LEN][CC_ID][symb_id] = 0; } } +*/ return size; } +int32_t xran_pkt_validate(void *arg, + struct rte_mbuf *mbuf, + void *iq_data_start, + uint16_t size, + uint8_t CC_ID, + uint8_t Ant_ID, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symb_id, + struct ecpri_seq_id *seq_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id) +{ + struct xran_device_ctx * pctx = xran_dev_get_ctx(); + struct xran_common_counters *pCnt = &pctx->fh_counters; + + if(pctx->fh_init.io_cfg.id == O_DU) { + if(xran_check_upul_seqid(NULL, CC_ID, Ant_ID, slot_id, seq_id->seq_id) != XRAN_STATUS_SUCCESS) { + pCnt->Rx_pkt_dupl++; + return (XRAN_STATUS_FAIL); + } + }else if(pctx->fh_init.io_cfg.id == O_RU) { + if(xran_check_updl_seqid(NULL, CC_ID, Ant_ID, slot_id, seq_id->seq_id) != XRAN_STATUS_SUCCESS) { + pCnt->Rx_pkt_dupl++; + return (XRAN_STATUS_FAIL); + } + }else { + print_err("incorrect dev type %d\n", pctx->fh_init.io_cfg.id); + } + + rx_counter++; + + pCnt->Rx_on_time++; + pCnt->Total_msgs_rcvd++; + + return XRAN_STATUS_SUCCESS; +} + +int xran_process_rx_sym(void *arg, + struct rte_mbuf *mbuf, + void *iq_data_start, + uint16_t size, + uint8_t CC_ID, + uint8_t Ant_ID, + uint8_t frame_id, + uint8_t subframe_id, + uint8_t slot_id, + uint8_t symb_id, + uint16_t num_prbu, + uint16_t start_prbu, + uint16_t sym_inc, + uint16_t rb, + uint16_t sect_id, + uint32_t *mb_free) +{ + char *pos = NULL; + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + uint32_t tti = 0; + xran_status_t status; + void *pHandle = NULL; + struct rte_mbuf *mb = NULL; + + uint16_t iq_sample_size_bits = 16; + + tti = frame_id * SLOTS_PER_SYSTEMFRAME + subframe_id * SLOTNUM_PER_SUBFRAME + slot_id; + + status = tti << 16 | symb_id; + + if(tti < xran_fs_get_max_slot() && CC_ID < XRAN_MAX_SECTOR_NR && Ant_ID < XRAN_MAX_ANTENNA_NR && symb_id < XRAN_NUM_OF_SYMBOL_PER_SLOT){ + pos = (char*) p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id].pData; + pos += start_prbu * N_SC_PER_PRB*(iq_sample_size_bits/8)*2; + if(pos && iq_data_start && size){ + if (p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder == XRAN_CPU_LE_BYTE_ORDER) { + int idx = 0; + uint16_t *psrc = (uint16_t *)iq_data_start; + uint16_t *pdst = (uint16_t *)pos; + rte_panic("XRAN_CPU_LE_BYTE_ORDER is not supported 0x16%lx\n", (long)mb); + /* network byte (be) order of IQ to CPU byte order (le) */ + for (idx = 0; idx < size/sizeof(int16_t); idx++){ + pdst[idx] = (psrc[idx]>>8) | (psrc[idx]<<8); //rte_be_to_cpu_16(psrc[idx]); + } + } else if (likely(p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder == XRAN_NE_BE_BYTE_ORDER)){ + if (likely (p_xran_dev_ctx->fh_init.mtu >= + p_xran_dev_ctx->fh_cfg.nULRBs * N_SC_PER_PRB*(iq_sample_size_bits/8)*2)) { + /* no fragmentation */ + mb = p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id].pCtrl; + if(mb){ + rte_pktmbuf_free(mb); + }else{ + print_err("mb==NULL\n"); + } + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id].pData = iq_data_start; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][CC_ID][Ant_ID].sBufferList.pBuffers[symb_id].pCtrl = mbuf; + *mb_free = MBUF_KEEP; + } else { + /* packet can be fragmented copy RBs */ + rte_memcpy(pos, iq_data_start, size); + *mb_free = MBUF_FREE; + } + } +#ifdef DEBUG_XRAN_BUFFERS + if (pos[0] != tti % XRAN_N_FE_BUF_LEN || + pos[1] != CC_ID || + pos[2] != Ant_ID || + pos[3] != symb_id){ + printf("%d %d %d %d\n", pos[0], pos[1], pos[2], pos[3]); + } +#endif + } else { + print_err("pos %p iq_data_start %p size %d\n",pos, iq_data_start, size); + } + } else { + print_err("TTI %d(f_%d sf_%d slot_%d) CC %d Ant_ID %d symb_id %d\n",tti, frame_id, subframe_id, slot_id, CC_ID, Ant_ID, symb_id); + } + + return size; +} + +/* Send burst of packets on an output interface */ +static inline int +xran_send_burst(struct xran_device_ctx *dev, uint16_t n, uint16_t port) +{ + struct rte_mbuf **m_table; + struct rte_mbuf *m; + int32_t i = 0; + int j; + int32_t ret = 0; + + m_table = (struct rte_mbuf **)dev->tx_mbufs[port].m_table; + + for(i = 0; i < n; i++){ + rte_mbuf_sanity_check(m_table[i], 0); + /*rte_pktmbuf_dump(stdout, m_table[i], 256);*/ + tx_counter++; + ret += xran_ethdi_mbuf_send(m_table[i], ETHER_TYPE_ECPRI); + } + + + if (unlikely(ret < n)) { + print_err("ret < n\n"); + } + + return 0; +} + int xran_process_tx_sym(void *arg) { uint32_t tti=0; +#if XRAN_MLOG_VAR uint32_t mlogVar[10]; uint32_t mlogVarCnt = 0; +#endif unsigned long t1 = MLogTick(); void *pHandle = NULL; @@ -798,56 +1484,61 @@ int xran_process_tx_sym(void *arg) uint32_t sym_idx = 0; char *pos = NULL; + void *mb = NULL; int prb_num = 0; + uint16_t iq_sample_size_bits = 16; // TODO: make dynamic per struct xran_section_info *sectinfo; uint32_t next; + uint32_t num_sections; + uint8_t ctx_id; enum xran_pkt_dir direction; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); struct xran_timer_ctx *pTCtx = (struct xran_timer_ctx *)arg; - - if(p_xran_lib_ctx->xran2phy_mem_ready == 0) + if(p_xran_dev_ctx->xran2phy_mem_ready == 0) return 0; - if(p_xran_lib_ctx->xran_init_cfg.io_cfg.id == APP_LLS_CU) { + if(p_xran_dev_ctx->fh_init.io_cfg.id == O_DU) { direction = XRAN_DIR_DL; /* lls-CU */ - prb_num = NUM_OF_PRB_IN_FULL_BAND; - } - else { + prb_num = p_xran_dev_ctx->fh_cfg.nDLRBs; + } else { direction = XRAN_DIR_UL; /* RU */ - prb_num = NUM_OF_PRB_IN_FULL_BAND; /*TODO: simulation on D-1541 @ 2.10GHz has issue with performace. reduce copy size */ - } + prb_num = p_xran_dev_ctx->fh_cfg.nULRBs; + } /* RU: send symb after OTA time with delay (UL) */ /* lls-CU:send symb in advance of OTA time (DL) */ - sym_idx = XranOffsetSym(p_xran_lib_ctx->sym_up, xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT*SLOTNUM_PER_SUBFRAME*1000); + sym_idx = XranOffsetSym(p_xran_dev_ctx->sym_up, xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT*SLOTNUM_PER_SUBFRAME*1000); tti = XranGetTtiNum(sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT); slot_id = XranGetSlotNum(tti, SLOTNUM_PER_SUBFRAME); subframe_id = XranGetSubFrameNum(tti,SLOTNUM_PER_SUBFRAME, SUBFRAMES_PER_SYSTEMFRAME); frame_id = XranGetFrameNum(tti,SUBFRAMES_PER_SYSTEMFRAME, SLOTNUM_PER_SUBFRAME); sym_id = XranGetSymNum(sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT); + ctx_id = XranGetSlotNum(tti, SLOTS_PER_SYSTEMFRAME) % XRAN_MAX_SECTIONDB_CTX; print_dbg("[%d]SFN %d sf %d slot %d\n", tti, frame_id, subframe_id, slot_id); +#if XRAN_MLOG_VAR mlogVar[mlogVarCnt++] = 0xAAAAAAAA; mlogVar[mlogVarCnt++] = xran_lib_ota_sym_idx; mlogVar[mlogVarCnt++] = sym_idx; - mlogVar[mlogVarCnt++] = abs(p_xran_lib_ctx->sym_up); + mlogVar[mlogVarCnt++] = abs(p_xran_dev_ctx->sym_up); mlogVar[mlogVarCnt++] = tti; mlogVar[mlogVarCnt++] = frame_id; mlogVar[mlogVarCnt++] = subframe_id; mlogVar[mlogVarCnt++] = slot_id; mlogVar[mlogVarCnt++] = sym_id; MLogAddVariables(mlogVarCnt, mlogVar, MLogTick()); +#endif if(frame_id > 99) { print_err("OTA %d: TX:[sym_idx %d: TTI %d] fr %d sf %d slot %d sym %d\n",xran_lib_ota_sym_idx, sym_idx, tti, frame_id, subframe_id, slot_id, sym_id); - xran_if_current_state =XRAN_STOPPED; - } + xran_if_current_state = XRAN_STOPPED; + } num_eAxc = xran_get_num_eAxc(pHandle); num_CCPorts = xran_get_num_cc(pHandle); @@ -855,55 +1546,208 @@ int xran_process_tx_sym(void *arg) /* U-Plane */ for(ant_id = 0; ant_id < num_eAxc; ant_id++) { for(cc_id = 0; cc_id < num_CCPorts; cc_id++) { - if(p_xran_lib_ctx->xran_init_cfg.io_cfg.id == APP_LLS_CU && p_xran_lib_ctx->enableCP) { + if(p_xran_dev_ctx->fh_init.io_cfg.id == O_DU + && p_xran_dev_ctx->enableCP) { + /*==== lls-CU and C-Plane has been enabled ===*/ next = 0; - while(next < xran_cp_getsize_section_info(pHandle, direction, cc_id, ant_id)) { - sectinfo = xran_cp_iterate_section_info(pHandle, direction, - cc_id, ant_id, subframe_id, slot_id, &next); + num_sections = xran_cp_getsize_section_info(pHandle, direction, cc_id, ant_id, ctx_id); + /* iterate C-Plane configuration to generate corresponding U-Plane */ + while(next < num_sections) { + sectinfo = xran_cp_iterate_section_info(pHandle, direction, cc_id, ant_id, ctx_id, &next); + if(sectinfo == NULL) break; - /* pointer to IQs input */ - /* TODO: need to implement the case of partial RB assignment */ - pos = (char*) p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; - print_dbg(">>> [%d] type%d, id %d, startPrbc=%d, numPrbc=%d, numSymbol=%d\n", next, - sectinfo->type, sectinfo->id, sectinfo->startPrbc, - sectinfo->numPrbc, sectinfo->numSymbol); - - if(sectinfo->type != XRAN_CP_SECTIONTYPE_1) { + if(sectinfo->type != XRAN_CP_SECTIONTYPE_1) { /* only supports type 1 */ print_err("Invalid section type in section DB - %d", sectinfo->type); continue; + } + + /* skip, if not scheduled */ + if(sym_id < sectinfo->startSymId || sym_id >= sectinfo->startSymId + sectinfo->numSymbol) + continue; + + /* if(sectinfo->compMeth) + iq_sample_size_bits = sectinfo->iqWidth;*/ + + if(iq_sample_size_bits != 16) {/* TODO: support for compression */ + print_err("Incorrect iqWidth %d", iq_sample_size_bits); + iq_sample_size_bits = 16; + } + + print_dbg(">>> sym %2d [%d] type%d, id %d, startPrbc=%d, numPrbc=%d, numSymbol=%d\n", sym_id, next, + sectinfo->type, sectinfo->id, sectinfo->startPrbc, + sectinfo->numPrbc, sectinfo->numSymbol); + + p_xran_dev_ctx->tx_mbufs[0].len = 0; + uint16_t len = p_xran_dev_ctx->tx_mbufs[0].len; + int16_t len2 = 0; + uint16_t i = 0; + + //Added for Klocworks + if (len >= MBUF_TABLE_SIZE) + len = MBUF_TABLE_SIZE - 1; + + pos = (char*) p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; + mb = p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pCtrl; + + /* first all PRBs */ + prepare_symbol_ex(direction, sectinfo->id, + mb, + (struct rb_map *)pos, + p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder, + frame_id, subframe_id, slot_id, sym_id, + sectinfo->startPrbc, sectinfo->numPrbc, + cc_id, ant_id, + xran_get_updl_seqid(pHandle, cc_id, ant_id), + 0); + + /* if we don't need to do any fragmentation */ + if (likely (p_xran_dev_ctx->fh_init.mtu >= + sectinfo->numPrbc * N_SC_PER_PRB*(iq_sample_size_bits/8)*2)) { + /* no fragmentation */ + p_xran_dev_ctx->tx_mbufs[0].m_table[len] = mb; + len2 = 1; + } else { + /* fragmentation */ + len2 = xran_app_fragment_packet(mb, + &p_xran_dev_ctx->tx_mbufs[0].m_table[len], + (uint16_t)(MBUF_TABLE_SIZE - len), + p_xran_dev_ctx->fh_init.mtu, + p_xran_dev_ctx->direct_pool, + p_xran_dev_ctx->indirect_pool, + sectinfo, + xran_get_updl_seqid_addr(pHandle, cc_id, ant_id)); + + /* Free input packet */ + rte_pktmbuf_free(mb); + + /* If we fail to fragment the packet */ + if (unlikely (len2 < 0)){ + print_err("len2= %d\n", len2); + return 0; } + } - send_symbol_ex(direction, sectinfo->id, - (struct rb_map *)pos, - frame_id, subframe_id, slot_id, sym_id, - sectinfo->startPrbc, sectinfo->numPrbc, - cc_id, ant_id, - xran_get_seqid(pHandle, direction, cc_id, ant_id, slot_id)); + if(len2 > 1){ + for (i = len; i < len + len2; i ++) { + struct rte_mbuf *m; + m = p_xran_dev_ctx->tx_mbufs[0].m_table[i]; + struct ether_hdr *eth_hdr = (struct ether_hdr *) + rte_pktmbuf_prepend(m, (uint16_t)sizeof(struct ether_hdr)); + if (eth_hdr == NULL) { + rte_panic("No headroom in mbuf.\n"); + } + } } - } - else { /* if(p_xran_lib_ctx->xran_init_cfg.io_cfg.id == APP_LLS_CU && p_xran_lib_ctx->enableCP) */ - /* pointer to IQs input */ - pos = (char*) p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; + len += len2; + + if (unlikely(len > XRAN_MAX_PKT_BURST_PER_SYM)) { + rte_panic("XRAN_MAX_PKT_BURST_PER_SYM\n"); + } + + /* Transmit packets */ + xran_send_burst(p_xran_dev_ctx, (uint16_t)len, 0); + p_xran_dev_ctx->tx_mbufs[0].len = 0; + } /* while(section) */ + + } + else { + /*==== RU or C-Plane is disabled ===*/ + xRANPrachCPConfigStruct *pPrachCPConfig = &(p_xran_dev_ctx->PrachCPConfig); + + if(xran_fs_get_slot_type(cc_id, tti, ((p_xran_dev_ctx->fh_init.io_cfg.id == O_DU)? XRAN_SLOT_TYPE_DL : XRAN_SLOT_TYPE_UL)) == 1 + || xran_fs_get_slot_type(cc_id, tti, XRAN_SLOT_TYPE_SP) == 1 + || xran_fs_get_slot_type(cc_id, tti, XRAN_SLOT_TYPE_FDD) == 1){ + + if(xran_fs_get_symbol_type(cc_id, tti, sym_id) == ((p_xran_dev_ctx->fh_init.io_cfg.id == O_DU)? XRAN_SYMBOL_TYPE_DL : XRAN_SYMBOL_TYPE_UL) + || xran_fs_get_symbol_type(cc_id, tti, sym_id) == XRAN_SYMBOL_TYPE_FDD){ + + if(iq_sample_size_bits != 16) + print_err("Incorrect iqWidth %d\n", iq_sample_size_bits ); + + pos = (char*) p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pData; + mb = (void*) p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[sym_id].pCtrl; + + if( prb_num > 136 || prb_num == 0) { + uint16_t sec_id = xran_alloc_sectionid(pHandle, direction, cc_id, ant_id, slot_id); + /* first 136 PRBs */ + send_symbol_ex(direction, + sec_id, + NULL, + (struct rb_map *)pos, + p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder, + frame_id, subframe_id, slot_id, sym_id, + 0, 136, + cc_id, ant_id, + (p_xran_dev_ctx->fh_init.io_cfg.id == O_DU) ? + xran_get_updl_seqid(pHandle, cc_id, ant_id) : + xran_get_upul_seqid(pHandle, cc_id, ant_id)); + + pos += 136 * N_SC_PER_PRB * (iq_sample_size_bits/8)*2; + /* last 137 PRBs */ + send_symbol_ex(direction, sec_id, + NULL, + (struct rb_map *)pos, + p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder, + frame_id, subframe_id, slot_id, sym_id, + 136, 137, + cc_id, ant_id, + (p_xran_dev_ctx->fh_init.io_cfg.id == O_DU) ? + xran_get_updl_seqid(pHandle, cc_id, ant_id) : + xran_get_upul_seqid(pHandle, cc_id, ant_id)); + } else { #ifdef DEBUG_XRAN_BUFFERS - if (pos[0] != tti % XRAN_N_FE_BUF_LEN || - pos[1] != cc_id || - pos[2] != ant_id || - pos[3] != sym_id) - printf("%d %d %d %d\n", pos[0], pos[1], pos[2], pos[3]); + if (pos[0] != tti % XRAN_N_FE_BUF_LEN || + pos[1] != cc_id || + pos[2] != ant_id || + pos[3] != sym_id) + printf("%d %d %d %d\n", pos[0], pos[1], pos[2], pos[3]); #endif - send_symbol_ex(direction, - xran_alloc_sectionid(pHandle, direction, cc_id, ant_id, slot_id), - (struct rb_map *)pos, - frame_id, subframe_id, slot_id, sym_id, - 0, prb_num, - cc_id, ant_id, - xran_get_seqid(pHandle, direction, cc_id, ant_id, slot_id)); + send_symbol_ex(direction, + xran_alloc_sectionid(pHandle, direction, cc_id, ant_id, slot_id), + (struct rte_mbuf *)mb, + (struct rb_map *)pos, + p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder, + frame_id, subframe_id, slot_id, sym_id, + 0, prb_num, + cc_id, ant_id, + (p_xran_dev_ctx->fh_init.io_cfg.id == O_DU) ? + xran_get_updl_seqid(pHandle, cc_id, ant_id) : + xran_get_upul_seqid(pHandle, cc_id, ant_id)); + } + + if(p_xran_dev_ctx->enablePrach + && (p_xran_dev_ctx->fh_init.io_cfg.id == O_RU)) { /* Only RU needs to send PRACH I/Q */ + uint32_t isPRACHslot = xran_isPRACHSlot(subframe_id, slot_id); + if((frame_id % pPrachCPConfig->x == pPrachCPConfig->y[0]) + && (isPRACHslot == 1) + && (sym_id >= p_xran_dev_ctx->prach_start_symbol[cc_id]) + && (sym_id <= p_xran_dev_ctx->prach_last_symbol[cc_id])) { //is prach slot + for(ant_id = 0; ant_id < num_eAxc; ant_id++) { + int prach_port_id = ant_id + num_eAxc; + pos = (char*) p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[0].pData; + pos += (sym_id - p_xran_dev_ctx->prach_start_symbol[cc_id]) * pPrachCPConfig->numPrbc * N_SC_PER_PRB * 4; + mb = NULL;//(void*) p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[tti % XRAN_N_FE_BUF_LEN][cc_id][ant_id].sBufferList.pBuffers[0].pCtrl; + send_symbol_ex(direction, + xran_alloc_sectionid(pHandle, direction, cc_id, prach_port_id, slot_id), + (struct rte_mbuf *)mb, + (struct rb_map *)pos, + p_xran_dev_ctx->fh_cfg.ru_conf.byteOrder, + frame_id, subframe_id, slot_id, sym_id, + pPrachCPConfig->startPrbc, pPrachCPConfig->numPrbc, + cc_id, prach_port_id, + xran_get_upul_seqid(pHandle, cc_id, prach_port_id)); + } + } /* if((frame_id % pPrachCPConfig->x == pPrachCPConfig->y[0]) .... */ + } /* if(p_xran_dev_ctx->enablePrach ..... */ + + } /* RU mode or C-Plane is not used */ } } - } + } /* for(cc_id = 0; cc_id < num_CCPorts; cc_id++) */ + } /* for(ant_id = 0; ant_id < num_eAxc; ant_id++) */ MLogTask(PID_PROCESS_TX_SYM, t1, MLogTick()); return 0; @@ -946,106 +1790,111 @@ int xran_packet_and_dpdk_timer_thread(void *args) } -int32_t xran_init(int argc, char *argv[], PXRANFHINIT p_xran_fh_init, char *appName, void ** pHandle) +int32_t +xran_init(int argc, char *argv[], + struct xran_fh_init *p_xran_fh_init, char *appName, void ** pXranLayerHandle) { - int i; - int j; + int32_t i; + int32_t j; struct xran_io_loop_cfg *p_io_cfg = (struct xran_io_loop_cfg *)&p_xran_fh_init->io_cfg; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); - int lcore_id = 0; + int32_t lcore_id = 0; char filename[64]; - memset(p_xran_lib_ctx, 0, sizeof(struct xran_lib_ctx)); + memset(p_xran_dev_ctx, 0, sizeof(struct xran_device_ctx)); + /* copy init */ - p_xran_lib_ctx->xran_init_cfg = *p_xran_fh_init; + p_xran_dev_ctx->fh_init = *p_xran_fh_init; - xran_if_current_state = XRAN_RUNNING; - interval_us = p_xran_fh_init->ttiPeriod; + printf(" %s: MTU %d\n", __FUNCTION__, p_xran_dev_ctx->fh_init.mtu); - p_xran_lib_ctx->llscu_id = p_xran_fh_init->llscuId; - memcpy(&(p_xran_lib_ctx->eAxc_id_cfg), &(p_xran_fh_init->eAxCId_conf), sizeof(XRANEAXCIDCONFIG)); + xran_if_current_state = XRAN_INIT; - p_xran_lib_ctx->enableCP = p_xran_fh_init->enableCP; + memcpy(&(p_xran_dev_ctx->eAxc_id_cfg), &(p_xran_fh_init->eAxCId_conf), sizeof(struct xran_eaxcid_config)); + + p_xran_dev_ctx->enableCP = p_xran_fh_init->enableCP; + p_xran_dev_ctx->enablePrach = p_xran_fh_init->prachEnable; + p_xran_dev_ctx->DynamicSectionEna = p_xran_fh_init->DynamicSectionEna; xran_register_ethertype_handler(ETHER_TYPE_ECPRI, handle_ecpri_ethertype); if (p_io_cfg->id == 0) - xran_ethdi_init_dpdk_io(basename(appName), + xran_ethdi_init_dpdk_io(p_xran_fh_init->filePrefix, p_io_cfg, &lcore_id, - (struct ether_addr *)p_xran_fh_init->p_lls_cu_addr, - (struct ether_addr *)p_xran_fh_init->p_ru_addr, + (struct ether_addr *)p_xran_fh_init->p_o_du_addr, + (struct ether_addr *)p_xran_fh_init->p_o_ru_addr, p_xran_fh_init->cp_vlan_tag, p_xran_fh_init->up_vlan_tag); else - xran_ethdi_init_dpdk_io(basename(appName), + xran_ethdi_init_dpdk_io(p_xran_fh_init->filePrefix, p_io_cfg, &lcore_id, - (struct ether_addr *)p_xran_fh_init->p_ru_addr, - (struct ether_addr *)p_xran_fh_init->p_lls_cu_addr, + (struct ether_addr *)p_xran_fh_init->p_o_ru_addr, + (struct ether_addr *)p_xran_fh_init->p_o_du_addr, p_xran_fh_init->cp_vlan_tag, p_xran_fh_init->up_vlan_tag); for(i = 0; i < 10; i++ ) rte_timer_init(&tti_to_phy_timer[i]); - rte_timer_init(&tti_timer); rte_timer_init(&sym_timer); - rte_timer_init(&tx_cp_dl_timer); - rte_timer_init(&tx_cp_ul_timer); - rte_timer_init(&tx_up_timer); + for (i = 0; i< MAX_NUM_OF_DPDK_TIMERS; i++) + rte_timer_init(&dpdk_timer[i]); + + p_xran_dev_ctx->direct_pool = socket_direct_pool; + p_xran_dev_ctx->indirect_pool = socket_indirect_pool; - for(i = 0; i < XRAN_MAX_SECTOR_NR; i++ ){ - unsigned n = snprintf(&p_xran_lib_ctx->ring_name[0][i][0], RTE_RING_NAMESIZE, "dl_sym_ring_%u", i); - p_xran_lib_ctx->dl_sym_idx_ring[i] = rte_ring_create(&p_xran_lib_ctx->ring_name[0][i][0], XRAN_RING_SIZE, - rte_lcore_to_socket_id(lcore_id), RING_F_SP_ENQ | RING_F_SC_DEQ); + printf("Set debug stop %d, debug stop count %d\n", p_xran_fh_init->debugStop, p_xran_fh_init->debugStopCount); + timing_set_debug_stop(p_xran_fh_init->debugStop, p_xran_fh_init->debugStopCount); + + for (uint32_t nCellIdx = 0; nCellIdx < XRAN_MAX_SECTOR_NR; nCellIdx++){ + xran_fs_clear_slot_type(nCellIdx); } + *pXranLayerHandle = p_xran_dev_ctx; - lcore_id = rte_get_next_lcore(lcore_id, 0, 0); - PANIC_ON(lcore_id == RTE_MAX_LCORE, "out of lcores for io_loop()"); + return 0; +} - /* Start packet processing thread */ - if (rte_eal_remote_launch(ring_processing_thread, NULL, lcore_id)) - rte_panic("ring_processing_thread() failed to start\n"); +int32_t xran_sector_get_instances (void * pDevHandle, uint16_t nNumInstances, + xran_cc_handle_t * pSectorInstanceHandles) +{ + xran_status_t nStatus = XRAN_STATUS_FAIL; + struct xran_device_ctx *pDev = (struct xran_device_ctx *)pDevHandle; + XranSectorHandleInfo *pCcHandle = NULL; + int32_t i = 0; - if(p_io_cfg->pkt_aux_core > 0){ - lcore_id = rte_get_next_lcore(lcore_id, 0, 0); - PANIC_ON(lcore_id == RTE_MAX_LCORE, "out of lcores for io_loop()"); + /* Check for the Valid Parameters */ + CHECK_NOT_NULL (pSectorInstanceHandles, XRAN_STATUS_INVALID_PARAM); - /* Start packet processing thread */ - if (rte_eal_remote_launch(xran_packet_and_dpdk_timer_thread, NULL, lcore_id)) - rte_panic("ring_processing_thread() failed to start\n"); + if (!nNumInstances) { + print_dbg("Instance is not assigned for this function !!! \n"); + return XRAN_STATUS_INVALID_PARAM; } - lcore_id = rte_get_next_lcore(lcore_id, 0, 0); - PANIC_ON(lcore_id == RTE_MAX_LCORE, "out of lcores for io_loop()"); - - /* Start packet processing thread */ - if (rte_eal_remote_launch(xran_timing_source_thread, xran_lib_get_ctx(), lcore_id)) - rte_panic("thread_run() failed to start\n"); + for (i = 0; i < nNumInstances; i++) { - printf("Set debug stop %d\n", p_xran_fh_init->debugStop); - timing_set_debug_stop(p_xran_fh_init->debugStop); + /* Allocate Memory for CC handles */ + pCcHandle = (XranSectorHandleInfo *) _mm_malloc( /*"xran_cc_handles",*/ sizeof (XranSectorHandleInfo), 64); - memset(&DevHandle, 0, sizeof(XranLibHandleInfoStruct)); + if(pCcHandle == NULL) + return XRAN_STATUS_RESOURCE; - *pHandle = &DevHandle; + memset (pCcHandle, 0, (sizeof (XranSectorHandleInfo))); - return 0; -} + pCcHandle->nIndex = i; + pCcHandle->nXranPort = pDev->xran_port_id; -int32_t xran_sector_get_instances (void * pHandle, uint16_t nNumInstances, - XranCcInstanceHandleVoidP * pSectorInstanceHandles) -{ - int i; + printf("%s [%d]: CC %d handle %p\n", __FUNCTION__, pDev->xran_port_id, i, pCcHandle); + pLibInstanceHandles[pDev->xran_port_id][i] = pSectorInstanceHandles[i] = pCcHandle; - /* only one handle as only one CC is currently supported */ - for(i = 0; i < nNumInstances; i++ ) - pSectorInstanceHandles[i] = pHandle; + printf("Handle: %p Instance: %p\n", + &pSectorInstanceHandles[i], pSectorInstanceHandles[i]); + } - return 0; + return XRAN_STATUS_SUCCESS; } int32_t xran_mm_init (void * pHandle, uint64_t nMemorySize, @@ -1057,66 +1906,109 @@ int32_t xran_mm_init (void * pHandle, uint64_t nMemorySize, int32_t xran_bm_init (void * pHandle, uint32_t * pPoolIndex, uint32_t nNumberOfBuffers, uint32_t nBufferSize) { - XranLibHandleInfoStruct* pXran = (XranLibHandleInfoStruct*) pHandle; + XranSectorHandleInfo* pXranCc = (XranSectorHandleInfo*) pHandle; + uint32_t nAllocBufferSize; char pool_name[RTE_MEMPOOL_NAMESIZE]; - snprintf(pool_name, RTE_MEMPOOL_NAMESIZE, "bm_mempool_%ld", pPoolIndex); + snprintf(pool_name, RTE_MEMPOOL_NAMESIZE, "ru_%d_cc_%d_idx_%d", + pXranCc->nXranPort, pXranCc->nIndex, pXranCc->nBufferPoolIndex); + + nAllocBufferSize = nBufferSize + sizeof(struct ether_hdr) + + sizeof (struct xran_ecpri_hdr) + + sizeof (struct radio_app_common_hdr) + + sizeof(struct data_section_hdr) + 256; + + + printf("%s: [ handle %p %d %d ] [nPoolIndex %d] nNumberOfBuffers %d nBufferSize %d\n", pool_name, + pXranCc, pXranCc->nXranPort, pXranCc->nIndex, pXranCc->nBufferPoolIndex, nNumberOfBuffers, nBufferSize); - pXran->p_bufferPool[pXran->nBufferPoolIndex] = rte_pktmbuf_pool_create(pool_name, nNumberOfBuffers, - MBUF_CACHE, 0, XRAN_MAX_MBUF_LEN, rte_socket_id()); + pXranCc->p_bufferPool[pXranCc->nBufferPoolIndex] = rte_pktmbuf_pool_create(pool_name, nNumberOfBuffers, + MBUF_CACHE, 0, nAllocBufferSize, rte_socket_id()); + + if(pXranCc->p_bufferPool[pXranCc->nBufferPoolIndex] == NULL){ + rte_panic("rte_pktmbuf_pool_create failed [ handle %p %d %d ] [nPoolIndex %d] nNumberOfBuffers %d nBufferSize %d errno %s\n", + pXranCc, pXranCc->nXranPort, pXranCc->nIndex, pXranCc->nBufferPoolIndex, nNumberOfBuffers, nBufferSize, rte_strerror(rte_errno)); + return -1; + } - pXran->bufferPoolElmSz[pXran->nBufferPoolIndex] = nBufferSize; - pXran->bufferPoolNumElm[pXran->nBufferPoolIndex] = nNumberOfBuffers; + pXranCc->bufferPoolElmSz[pXranCc->nBufferPoolIndex] = nBufferSize; + pXranCc->bufferPoolNumElm[pXranCc->nBufferPoolIndex] = nNumberOfBuffers; - print_dbg("[nPoolIndex %d] mb pool %p \n", pXran->nBufferPoolIndex, pXran->p_bufferPool[pXran->nBufferPoolIndex]); + printf("CC:[ handle %p ru %d cc_idx %d ] [nPoolIndex %d] mb pool %p \n", + pXranCc, pXranCc->nXranPort, pXranCc->nIndex, + pXranCc->nBufferPoolIndex, pXranCc->p_bufferPool[pXranCc->nBufferPoolIndex]); - *pPoolIndex = pXran->nBufferPoolIndex++; + *pPoolIndex = pXranCc->nBufferPoolIndex++; return 0; } -int32_t xran_bm_allocate_buffer(void * pHandle, uint32_t nPoolIndex, void **ppVirtAddr) +int32_t xran_bm_allocate_buffer(void * pHandle, uint32_t nPoolIndex, void **ppData, void **ppCtrl) { - XranLibHandleInfoStruct* pXran = (XranLibHandleInfoStruct*) pHandle; - *ppVirtAddr = NULL; + XranSectorHandleInfo* pXranCc = (XranSectorHandleInfo*) pHandle; + *ppData = NULL; + *ppCtrl = NULL; - struct rte_mbuf * mb = rte_pktmbuf_alloc(pXran->p_bufferPool[nPoolIndex]); + struct rte_mbuf * mb = rte_pktmbuf_alloc(pXranCc->p_bufferPool[nPoolIndex]); if(mb){ - *ppVirtAddr = rte_pktmbuf_append(mb, pXran->bufferPoolElmSz[nPoolIndex]); - + char * start = rte_pktmbuf_append(mb, pXranCc->bufferPoolElmSz[nPoolIndex]); + char * ethhdr = rte_pktmbuf_prepend(mb, sizeof(struct ether_hdr)); + + if(start && ethhdr){ + char * iq_offset = rte_pktmbuf_mtod(mb, char * ); + /* skip headers */ + iq_offset = iq_offset + sizeof(struct ether_hdr) + + sizeof (struct xran_ecpri_hdr) + + sizeof (struct radio_app_common_hdr) + + sizeof(struct data_section_hdr); + + if (0) /* if compression */ + iq_offset += sizeof (struct data_section_compression_hdr); + + *ppData = (void *)iq_offset; + *ppCtrl = (void *)mb; + } + else { + print_err("[nPoolIndex %d] start ethhdr failed \n", nPoolIndex ); + return -1; + } }else { print_err("[nPoolIndex %d] mb alloc failed \n", nPoolIndex ); return -1; } - if (*ppVirtAddr == NULL){ - print_err("[nPoolIndex %d] rte_pktmbuf_append for %d failed \n", nPoolIndex, pXran->bufferPoolElmSz[nPoolIndex]); + if (*ppData == NULL){ + print_err("[nPoolIndex %d] rte_pktmbuf_append for %d failed \n", nPoolIndex, pXranCc->bufferPoolElmSz[nPoolIndex]); return -1; } return 0; } -int32_t xran_bm_free_buffer(void * pHandle, void *pVirtAddr) +int32_t xran_bm_free_buffer(void * pHandle, void *pData, void *pCtrl) { - XranLibHandleInfoStruct* pXran = (XranLibHandleInfoStruct*) pHandle; - rte_pktmbuf_free(pVirtAddr); + XranSectorHandleInfo* pXranCc = (XranSectorHandleInfo*) pHandle; + + if(pCtrl) + rte_pktmbuf_free(pCtrl); return 0; } int32_t xran_5g_fronthault_config (void * pHandle, - XRANBufferListStruct *pSrcBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XRANBufferListStruct *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XranTransportBlockCallbackFn pCallback, + struct xran_buffer_list *pSrcBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pSrcCpBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + struct xran_buffer_list *pDstCpBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + xran_transport_callback_fn pCallback, void *pCallbackTag) { - XranLibHandleInfoStruct *pInfo = (XranLibHandleInfoStruct *) pHandle; - XranStatusInt32 nStatus = XRAN_STATUS_SUCCESS; + XranSectorHandleInfo* pXranCc = (XranSectorHandleInfo*) pHandle; + xran_status_t nStatus = XRAN_STATUS_SUCCESS; int j, i = 0, z, k; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); print_dbg("%s\n", __FUNCTION__); @@ -1125,31 +2017,59 @@ int32_t xran_5g_fronthault_config (void * pHandle, printf("Handle is NULL!\n"); return XRAN_STATUS_FAIL; } + if (pCallback == NULL) { printf ("no callback\n"); return XRAN_STATUS_FAIL; } + i = pXranCc->nIndex; + for(j=0; jsFrontHaulTxBbuIoBufCtrl[j][i][z].bValid = 0; - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_lib_ctx->sFrontHaulTxBuffers[j][i][z][0]; - - p_xran_lib_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList = *pSrcBuffer[z][j]; - - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].bValid = 0; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_lib_ctx->sFrontHaulRxBuffers[j][i][z][0]; - p_xran_lib_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList = *pDstBuffer[z][j]; + /* U-plane TX */ + + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].bValid = 0; + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_dev_ctx->sFrontHaulTxBuffers[j][i][z][0]; + + p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList = *pSrcBuffer[z][j]; + + /* C-plane TX */ + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].bValid = 0; + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_dev_ctx->sFrontHaulTxPrbMapBuffers[j][i][z][0]; + + p_xran_dev_ctx->sFrontHaulTxPrbMapBbuIoBufCtrl[j][i][z].sBufferList = *pSrcCpBuffer[z][j]; + + /* U-plane RX */ + + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].bValid = 0; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_dev_ctx->sFrontHaulRxBuffers[j][i][z][0]; + + p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList = *pDstBuffer[z][j]; + + /* C-plane RX */ + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].bValid = 0; + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_NUM_OF_SYMBOL_PER_SLOT; + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_dev_ctx->sFrontHaulRxPrbMapBuffers[j][i][z][0]; + + p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[j][i][z].sBufferList = *pDstCpBuffer[z][j]; } } @@ -1158,7 +2078,7 @@ int32_t xran_5g_fronthault_config (void * pHandle, for(z = 0; z < XRAN_MAX_ANTENNA_NR; z++){ printf("TTI:TX 0x%02x Sec %d Ant%d\n",j,i,z); for(k = 0; k sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData; + uint8_t *ptr = p_xran_dev_ctx->sFrontHaulTxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData; printf(" sym: %2d %p 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", k, ptr, ptr[0],ptr[1], ptr[2], ptr[3], ptr[4]); } } @@ -1167,29 +2087,29 @@ int32_t xran_5g_fronthault_config (void * pHandle, for(z = 0; z < XRAN_MAX_ANTENNA_NR; z++){ printf("TTI:RX 0x%02x Sec %d Ant%d\n",j,i,z); for(k = 0; k sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData; + uint8_t *ptr = p_xran_dev_ctx->sFrontHaulRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers[k].pData; printf(" sym: %2d %p 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", k, ptr, ptr[0],ptr[1], ptr[2], ptr[3], ptr[4]); } } #endif - p_xran_lib_ctx->pCallback[i] = pCallback; - p_xran_lib_ctx->pCallbackTag[i] = pCallbackTag; + p_xran_dev_ctx->pCallback[i] = pCallback; + p_xran_dev_ctx->pCallbackTag[i] = pCallbackTag; - p_xran_lib_ctx->xran2phy_mem_ready = 1; + p_xran_dev_ctx->xran2phy_mem_ready = 1; return nStatus; } int32_t xran_5g_prach_req (void * pHandle, - XRANBufferListStruct *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], - XranTransportBlockCallbackFn pCallback, + struct xran_buffer_list *pDstBuffer[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], + xran_transport_callback_fn pCallback, void *pCallbackTag) { - XranLibHandleInfoStruct *pInfo = (XranLibHandleInfoStruct *) pHandle; - XranStatusInt32 nStatus = XRAN_STATUS_SUCCESS; + XranSectorHandleInfo* pXranCc = (XranSectorHandleInfo*) pHandle; + xran_status_t nStatus = XRAN_STATUS_SUCCESS; int j, i = 0, z; - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); if(NULL == pHandle) { @@ -1202,74 +2122,96 @@ int32_t xran_5g_prach_req (void * pHandle, return XRAN_STATUS_FAIL; } + i = pXranCc->nIndex; + for(j=0; jsFHPrachRxBbuIoBufCtrl[j][i][z].bValid = 0; - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_MAX_ANTENNA_NR; // ant number. - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_lib_ctx->sFHPrachRxBuffers[j][i][z][0]; - p_xran_lib_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList = *pDstBuffer[z][j]; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].bValid = 0; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegGenerated = -1; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegToBeGen = -1; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].nSegTransferred = 0; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.nNumBuffers = XRAN_MAX_ANTENNA_NR; // ant number. + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList.pBuffers = &p_xran_dev_ctx->sFHPrachRxBuffers[j][i][z][0]; + p_xran_dev_ctx->sFHPrachRxBbuIoBufCtrl[j][i][z].sBufferList = *pDstBuffer[z][j]; } } - p_xran_lib_ctx->pPrachCallback[i] = pCallback; - p_xran_lib_ctx->pPrachCallbackTag[i] = pCallbackTag; + p_xran_dev_ctx->pPrachCallback[i] = pCallback; + p_xran_dev_ctx->pPrachCallbackTag[i] = pCallbackTag; return 0; } -int32_t xran_5g_pre_compenstor_cfg(void* pHandle, - uint32_t nTxPhaseCps, - uint32_t nRxPhaseCps, - uint8_t nSectorId) +int32_t xran_open(void *pHandle, struct xran_fh_config* pConf) { - /* functionality is not yet implemented */ - return 0; -} + int32_t i; + uint8_t nNumerology = 0; + int32_t lcore_id = 0; + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); + struct xran_fh_config *pFhCfg; + pFhCfg = &(p_xran_dev_ctx->fh_cfg); -int32_t xran_open(void *pHandle, PXRANFHCONFIG pConf) -{ - int i; - uint8_t slotNr; - XRANFHCONFIG *pFhCfg; - xRANPrachCPConfigStruct *pPrachCPConfig = &(xran_lib_get_ctx()->PrachCPConfig); - pFhCfg = &(xran_lib_get_ctx()->xran_fh_cfg); - memcpy(pFhCfg, pConf, sizeof(XRANFHCONFIG)); - PXRANPRACHCONFIG pPRACHConfig = &pFhCfg->prach_conf; - uint8_t nPrachConfIdx = pPRACHConfig->nPrachConfIdx; - const xRANPrachConfigTableStruct *pxRANPrachConfigTable = &gxranPrachDataTable_mmw[nPrachConfIdx]; - uint8_t preambleFmrt = pxRANPrachConfigTable->preambleFmrt[0]; - const xRANPrachPreambleLRAStruct *pxranPreambleforLRA = &gxranPreambleforLRA[preambleFmrt - FORMAT_A1]; - memset(pPrachCPConfig, 0, sizeof(xRANPrachCPConfigStruct)); + memcpy(pFhCfg, pConf, sizeof(struct xran_fh_config)); - //setup PRACH configuration for C-Plane - pPrachCPConfig->filterIdx = XRAN_FILTERINDEX_PRACH_ABC; // 3, PRACH preamble format A1~3, B1~4, C0, C2 - pPrachCPConfig->startSymId = pxRANPrachConfigTable->startingSym; - pPrachCPConfig->startPrbc = pPRACHConfig->nPrachFreqStart; - pPrachCPConfig->numPrbc = (preambleFmrt >= FORMAT_A1)? 12 : 70; - pPrachCPConfig->numSymbol = pxRANPrachConfigTable->duration; - pPrachCPConfig->timeOffset = pxranPreambleforLRA->nRaCp; - pPrachCPConfig->freqOffset = xran_get_freqoffset(pPRACHConfig->nPrachFreqOffset, pPRACHConfig->nPrachSubcSpacing); - pPrachCPConfig->occassionsInPrachSlot = pxRANPrachConfigTable->occassionsInPrachSlot; - pPrachCPConfig->x = pxRANPrachConfigTable->x; - pPrachCPConfig->y[0] = pxRANPrachConfigTable->y[0]; - pPrachCPConfig->y[1] = pxRANPrachConfigTable->y[1]; + nNumerology = xran_get_conf_numerology(pHandle); - pPrachCPConfig->isPRACHslot[pxRANPrachConfigTable->slotNr[0]] = 1; - for (i=1; i < XRAN_PRACH_CANDIDATE_SLOT; i++) + if (pConf->nCC > XRAN_MAX_SECTOR_NR) { - slotNr = pxRANPrachConfigTable->slotNr[i]; - if (slotNr > 0) - pPrachCPConfig->isPRACHslot[slotNr] = 1; + if(pConf->log_level) + printf("Number of cells %d exceeds max number supported %d!\n", pConf->nCC, XRAN_MAX_SECTOR_NR); + pConf->nCC = XRAN_MAX_SECTOR_NR; + + } + if(pConf->ru_conf.iqOrder != XRAN_I_Q_ORDER + || pConf->ru_conf.byteOrder != XRAN_NE_BE_BYTE_ORDER ){ + + print_err("Byte order and/or IQ order is not suppirted [IQ %d byte %d]\n", pConf->ru_conf.iqOrder, pConf->ru_conf.byteOrder); + return XRAN_STATUS_FAIL; } + //setup PRACH configuration for C-Plane + xran_init_prach(pConf, p_xran_dev_ctx); + xran_cp_init_sectiondb(pHandle); xran_init_sectionid(pHandle); xran_init_seqid(pHandle); + interval_us = xran_fs_get_tti_interval(nNumerology); + + if(pConf->log_level){ + printf("%s: interval_us=%ld\n", __FUNCTION__, interval_us); + } + timing_set_numerology(nNumerology); + + for(i = 0 ; i nCC; i++){ + xran_fs_set_slot_type(i, pConf->frame_conf.nFrameDuplexType, pConf->frame_conf.nTddPeriod, + pConf->frame_conf.sSlotConfig); + } + + xran_fs_slot_limit_init(xran_fs_get_tti_interval(nNumerology)); + + if(xran_ethdi_get_ctx()->io_cfg.bbdev_mode != XRAN_BBDEV_NOT_USED){ + p_xran_dev_ctx->bbdev_dec = pConf->bbdev_dec; + p_xran_dev_ctx->bbdev_enc = pConf->bbdev_enc; + } + + /* Start packet processing thread */ + if((uint16_t)xran_ethdi_get_ctx()->io_cfg.port[XRAN_UP_VF] != 0xFFFF && + (uint16_t)xran_ethdi_get_ctx()->io_cfg.port[XRAN_CP_VF] != 0xFFFF ){ + if(pConf->log_level){ + print_dbg("XRAN_UP_VF: 0x%04x\n", xran_ethdi_get_ctx()->io_cfg.port[XRAN_UP_VF]); + print_dbg("XRAN_CP_VF: 0x%04x\n", xran_ethdi_get_ctx()->io_cfg.port[XRAN_CP_VF]); + } + if (rte_eal_remote_launch(xran_timing_source_thread, xran_dev_get_ctx(), xran_ethdi_get_ctx()->io_cfg.timing_core)) + rte_panic("thread_run() failed to start\n"); + } else + if(pConf->log_level){ + printf("Eth port was not open. Processing thread was not started\n"); + } + + + return 0; } @@ -1288,8 +2230,10 @@ int32_t xran_stop(void *pHandle) int32_t xran_close(void *pHandle) { xran_if_current_state = XRAN_STOPPED; - xran_cp_free_sectiondb(pHandle); - rte_eal_mp_wait_lcore(); + //TODO: fix memory leak xran_cp_free_sectiondb(pHandle); + //rte_eal_mp_wait_lcore(); + //xran_ethdi_ports_stats(); + return 0; } @@ -1299,19 +2243,19 @@ int32_t xran_mm_destroy (void * pHandle) return -1; } -int32_t xran_reg_sym_cb(void *pHandle, XRANFHSYMPROCCB symCb, void * symCbParam, uint8_t symb, uint8_t ant) +int32_t xran_reg_sym_cb(void *pHandle, xran_callback_sym_fn symCb, void * symCbParam, uint8_t symb, uint8_t ant) { /* functionality is not yet implemented */ return -1; } -int32_t xran_reg_physide_cb(void *pHandle, XRANFHTTIPROCCB Cb, void *cbParam, int skipTtiNum, enum callback_to_phy_id id) +int32_t xran_reg_physide_cb(void *pHandle, xran_fh_tti_callback_fn Cb, void *cbParam, int skipTtiNum, enum callback_to_phy_id id) { - struct xran_lib_ctx * p_xran_lib_ctx = xran_lib_get_ctx(); + struct xran_device_ctx * p_xran_dev_ctx = xran_dev_get_ctx(); - p_xran_lib_ctx->ttiCb[id] = Cb; - p_xran_lib_ctx->TtiCbParam[id] = cbParam; - p_xran_lib_ctx->SkipTti[id] = skipTtiNum; + p_xran_dev_ctx->ttiCb[id] = Cb; + p_xran_dev_ctx->TtiCbParam[id] = cbParam; + p_xran_dev_ctx->SkipTti[id] = skipTtiNum; return 0; } @@ -1329,24 +2273,15 @@ int32_t xran_get_slot_idx (uint32_t *nFrameIdx, uint32_t *nSubframeIdx, uint32_ return tti; } -/** - * @brief Get supported maximum number of sections - * - * @return maximum number of sections - */ -inline uint8_t xran_get_max_sections(void *pHandle) -{ - return (XRAN_MAX_NUM_SECTIONS); -} /** * @brief Get the configuration of eAxC ID * * @return the pointer of configuration */ -inline XRANEAXCIDCONFIG *xran_get_conf_eAxC(void *pHandle) +inline struct xran_eaxcid_config *xran_get_conf_eAxC(void *pHandle) { - return (&(xran_lib_get_ctx()->eAxc_id_cfg)); + return (&(xran_dev_get_ctx()->eAxc_id_cfg)); } /** @@ -1372,7 +2307,7 @@ inline uint8_t xran_get_conf_fftsize(void *pHandle) /** * @brief Get the configuration of nummerology * - * @return subcarrier spacing value for PRACH + * @return Configured numerology */ inline uint8_t xran_get_conf_numerology(void *pHandle) { @@ -1386,7 +2321,7 @@ inline uint8_t xran_get_conf_numerology(void *pHandle) */ inline uint8_t xran_get_conf_iqwidth(void *pHandle) { - XRANFHCONFIG *pFhCfg; + struct xran_fh_config *pFhCfg; pFhCfg = xran_lib_get_ctx_fhcfg(); return ((pFhCfg->ru_conf.iqWidth==16)?0:pFhCfg->ru_conf.iqWidth); @@ -1402,30 +2337,11 @@ inline uint8_t xran_get_conf_compmethod(void *pHandle) return (xran_lib_get_ctx_fhcfg()->ru_conf.compMeth); } -/** - * @brief Get the configuration of lls-cu ID - * - * @return Configured lls-cu ID - */ -inline uint8_t xran_get_llscuid(void *pHandle) -{ - return (xran_lib_get_ctx()->llscu_id); -} - -/** - * @brief Get the configuration of lls-cu ID - * - * @return Configured lls-cu ID - */ -inline uint8_t xran_get_sectorid(void *pHandle) -{ - return (xran_lib_get_ctx()->sector_id); -} /** * @brief Get the configuration of the number of component carriers * - * @return Configured the number of componen carriers + * @return Configured the number of component carriers */ inline uint8_t xran_get_num_cc(void *pHandle) { @@ -1442,4 +2358,15 @@ inline uint8_t xran_get_num_eAxc(void *pHandle) return (xran_lib_get_ctx_fhcfg()->neAxc); } +int32_t xran_get_common_counters(void *pXranLayerHandle, struct xran_common_counters *pStats) +{ + struct xran_device_ctx* pDev = (struct xran_device_ctx*)pXranLayerHandle; + + if(pStats && pDev) { + *pStats = pDev->fh_counters; + return XRAN_STATUS_SUCCESS; + } else { + return XRAN_STATUS_INVALID_PARAM; + } +} diff --git a/fhi_lib/lib/src/xran_printf.h b/fhi_lib/lib/src/xran_printf.h index 6caee87..8649b01 100644 --- a/fhi_lib/lib/src/xran_printf.h +++ b/fhi_lib/lib/src/xran_printf.h @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief Modules provide debug prints and utility functions * @file xran_printf.h @@ -101,7 +100,16 @@ extern "C" { #endif /* _IASSERT_*/ - +#ifdef CHECK_PARAMS +#define CHECK_NOT_NULL(param, returnValue) \ +if (param == NULL) \ +{ \ + print_err("%s is NULL!\n", #param); \ + return returnValue; \ +} +#else +#define CHECK_NOT_NULL(param, returnValue) +#endif #ifdef __cplusplus } diff --git a/fhi_lib/lib/src/xran_sync_api.c b/fhi_lib/lib/src/xran_sync_api.c index e030a86..c45d83c 100644 --- a/fhi_lib/lib/src/xran_sync_api.c +++ b/fhi_lib/lib/src/xran_sync_api.c @@ -15,7 +15,6 @@ * limitations under the License. * *******************************************************************************/ - /** * @brief This file provides implementation of synchronization related APIs (PTP/1588) * for XRAN. @@ -31,11 +30,6 @@ #include #include #include -#include -#include -#include -#include -#include #include "xran_sync_api.h" #include "xran_printf.h" @@ -67,30 +61,27 @@ static int is_process_running(char *pname) char full_path[BUF_LEN] = {0}; char read_proc_name[BUF_LEN] = {0}; int res = 1; - int null = 0; - int dir_fd = dirfd((DIR*)PROC_DIR); - DIR *dir = fdopendir(dir_fd); + DIR *dir = opendir(PROC_DIR); if (NULL == dir) { return 1; } struct dirent *entry = NULL; - while ((entry = readdir(dir))) { + while (entry = readdir(dir)) { long pid = atol(entry->d_name); if (0 == pid) continue; - - snprintf(full_path, BUF_LEN,"%s/%ld/%s", PROC_DIR, pid, COMM_FILE); - int proc_name_file = open(full_path, O_RDONLY); - if (null == proc_name_file) + sprintf(full_path, "%s/%ld/%s", PROC_DIR, pid, COMM_FILE); + FILE *proc_name_file = fopen(full_path, "r"); + if (NULL == proc_name_file) continue; - fgets( read_proc_name, BUF_LEN, (FILE*)proc_name_file); + fgets( read_proc_name, BUF_LEN, proc_name_file); if (0 == strncmp(read_proc_name, pname, strlen(pname))) { res = 0; - close(proc_name_file); + fclose(proc_name_file); break; } - close(proc_name_file); + fclose(proc_name_file); } closedir(dir); return res; diff --git a/fhi_lib/lib/src/xran_timer.c b/fhi_lib/lib/src/xran_timer.c index 875b0b6..c77a39e 100644 --- a/fhi_lib/lib/src/xran_timer.c +++ b/fhi_lib/lib/src/xran_timer.c @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides implementation to Timing for XRAN. * @@ -33,20 +32,18 @@ #include "xran_timer.h" #include "xran_printf.h" -#ifndef MLOG_ENABLED -#include "mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif +#include "xran_mlog_lnx.h" #include "xran_lib_mlog_tasks_id.h" #include "ethdi.h" +#include "xran_fh_o_du.h" +#include "xran_common.h" #define NSEC_PER_SEC 1000000000L #define NSEC_PER_USEC 1000L #define THRESHOLD 35 /**< the avg cost of clock_gettime() in ns */ #define TIMECOMPENSATION 2 /**< time compensation in us, avg latency of clock_nanosleep */ -#define SEC_MOD_STOP (30) +#define SEC_MOD_STOP (60) static struct timespec started_time; static struct timespec last_time; @@ -59,20 +56,46 @@ static struct timespec* p_temp_time; static unsigned long current_second = 0; static unsigned long started_second = 0; +static uint8_t numerlogy = 0; extern uint32_t xran_lib_ota_sym; extern uint32_t xran_lib_ota_tti; extern uint32_t xran_lib_ota_sym_idx; static int debugStop = 0; +static int debugStopCount = 0; + +static long fine_tuning[5][2] = +{ + {71428L, 71429L}, /* mu = 0 */ + {35714L, 35715L}, /* mu = 1 */ + {0, 0}, /* mu = 2 not supported */ + {8928L, 8929L}, /* mu = 3 */ + {0,0 } /* mu = 4 not supported */ +}; + +static uint8_t slots_per_subframe[4] = +{ + 1, /* mu = 0 */ + 2, /* mu = 1 */ + 4, /* mu = 2 */ + 8, /* mu = 3 */ +}; uint64_t timing_get_current_second(void) { return current_second; } -int timing_set_debug_stop(int value) +int timing_set_numerology(uint8_t value) +{ + numerlogy = value; + return numerlogy; +} + +int timing_set_debug_stop(int value, int count) { debugStop = value; + debugStopCount = count; if(debugStop){ clock_gettime(CLOCK_REALTIME, &started_time); @@ -107,6 +130,14 @@ long poll_next_tick(long interval_ns) clock_gettime(CLOCK_REALTIME, p_cur_time); delta = (p_cur_time->tv_sec * NSEC_PER_SEC + p_cur_time->tv_nsec) - target_time; if(delta > 0 || (delta < 0 && abs(delta) < THRESHOLD)) { + if (debugStop &&(debugStopCount > 0) && (tx_counter >= debugStopCount)){ + uint64_t t1; + printf("STOP:[%ld.%09ld], debugStopCount %d, tx_counter %ld\n", p_cur_time->tv_sec, p_cur_time->tv_nsec, debugStopCount, tx_counter); + t1 = MLogTick(); + rte_pause(); + MLogTask(PID_TIME_SYSTIME_STOP, t1, MLogTick()); + xran_if_current_state = XRAN_STOPPED; + } if(current_second != p_cur_time->tv_sec){ current_second = p_cur_time->tv_sec; xran_lib_ota_sym_idx = 0; @@ -127,12 +158,12 @@ long poll_next_tick(long interval_ns) } p_cur_time->tv_nsec = 0; // adjust to 1pps } else { - xran_lib_ota_sym_idx = XranIncrementSymIdx(xran_lib_ota_sym_idx, 14*8); + xran_lib_ota_sym_idx = XranIncrementSymIdx(xran_lib_ota_sym_idx, XRAN_NUM_OF_SYMBOL_PER_SLOT*slots_per_subframe[numerlogy]); /* adjust to sym boundary */ if(sym_cnt & 1) - sym_acc += 8928L; + sym_acc += fine_tuning[numerlogy][0]; else - sym_acc += 8929L; + sym_acc += fine_tuning[numerlogy][1]; /* fine tune to second boundary */ if(sym_cnt % 13 == 0) sym_acc += 1; @@ -146,6 +177,11 @@ long poll_next_tick(long interval_ns) p_last_time = p_cur_time; p_cur_time = p_temp_time; break; + } else { + if( likely(xran_if_current_state == XRAN_RUNNING)){ + ring_processing_func(); + process_dpdk_io(); + } } } diff --git a/fhi_lib/lib/src/xran_transport.c b/fhi_lib/lib/src/xran_transport.c index 9e1d192..325616b 100644 --- a/fhi_lib/lib/src/xran_transport.c +++ b/fhi_lib/lib/src/xran_transport.c @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file provides the implementation for Transport lyaer (eCPRI) API. * @@ -31,25 +30,42 @@ #include #include -#include "xran_fh_lls_cu.h" +#include "xran_fh_o_du.h" #include "xran_common.h" #include "xran_transport.h" +#include "xran_pkt_cp.h" +#include "xran_cp_api.h" #include "xran_up_api.h" +#include "xran_printf.h" +/** + * @brief return eCPRI header size without eCPRI common header + * + * @ingroup xran + * + * @return the size of eCPRI header without common header + */ +int xran_get_ecpri_hdr_size(void) +{ + return(sizeof(struct xran_ecpri_hdr) - sizeof(struct xran_ecpri_cmn_hdr)); +} + /** * @brief Compose ecpriRtcid/ecpriPcid * + * @ingroup xran + * * @param CU_Port_ID CU Port ID * @param BanbSector_ID Band Sector ID * @param CC_ID Component Carrier ID * @param Ant_ID RU Port ID (antenna ID) * @return uint16_t composed ecpriRtcid/ecpriPcid (network byte order) */ -inline uint16_t xran_compose_cid(uint8_t CU_Port_ID, uint8_t BandSector_ID, uint8_t CC_ID, uint8_t Ant_ID) +uint16_t xran_compose_cid(uint8_t CU_Port_ID, uint8_t BandSector_ID, uint8_t CC_ID, uint8_t Ant_ID) { uint16_t cid; - XRANEAXCIDCONFIG *conf; + struct xran_eaxcid_config *conf; conf = xran_get_conf_eAxC(NULL); @@ -64,13 +80,15 @@ inline uint16_t xran_compose_cid(uint8_t CU_Port_ID, uint8_t BandSector_ID, uint /** * @brief Decompose ecpriRtcid/ecpriPcid * + * @ingroup xran + * * @param cid composed ecpriRtcid/ecpriPcid (network byte order) * @param result the pointer of the structure to store decomposed values * @return none */ -inline void xran_decompose_cid(uint16_t cid, struct xran_eaxc_info *result) +void xran_decompose_cid(uint16_t cid, struct xran_eaxc_info *result) { - XRANEAXCIDCONFIG *conf; + struct xran_eaxcid_config *conf; conf = xran_get_conf_eAxC(NULL); cid = rte_be_to_cpu_16(cid); @@ -86,6 +104,8 @@ inline void xran_decompose_cid(uint16_t cid, struct xran_eaxc_info *result) /** * @brief modify the payload size of eCPRI header in xRAN packet * + * @ingroup xran + * * @param mbuf Initialized rte_mbuf packet which has eCPRI header already * @param size payload size to be updated * @return none @@ -96,6 +116,115 @@ inline void xran_update_ecpri_payload_size(struct rte_mbuf *mbuf, int size) ecpri_hdr = rte_pktmbuf_mtod(mbuf, struct xran_ecpri_hdr *); - ecpri_hdr->ecpri_payl_size = rte_cpu_to_be_16(size); + ecpri_hdr->cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(size); +} + + +/** + * @brief Build ECPRI header and returns added length + * + * @ingroup xran + * + * @param mbuf + * The pointer of the packet buffer to be parsed + * @param CC_ID + * Component Carrier ID for this C-Plane message + * @param Ant_ID + * Antenna ID(RU Port ID) for this C-Plane message + * @param seq_id + * Sequence ID for this C-Plane message + * @param ecpri_hdr + * The pointer to ECPRI header + * @return + * added payload size on success + * XRAN_STATUS_RESOURCE if failed to allocate the space to packet buffer + */ +int xran_build_ecpri_hdr(struct rte_mbuf *mbuf, + uint8_t CC_ID, uint8_t Ant_ID, + uint8_t seq_id, + struct xran_ecpri_hdr **ecpri_hdr) +{ + uint32_t payloadlen; + struct xran_ecpri_hdr *tmp; + + + tmp = (struct xran_ecpri_hdr *)rte_pktmbuf_append(mbuf, sizeof(struct xran_ecpri_hdr)); + if(unlikely(tmp == NULL)) { + print_err("Fail to allocate the space for eCPRI hedaer!"); + return (XRAN_STATUS_RESOURCE); + } + + /* Fill common header */ + tmp->cmnhdr.ecpri_ver = XRAN_ECPRI_VER; + tmp->cmnhdr.ecpri_resv = 0; // should be zero + tmp->cmnhdr.ecpri_concat = 0; + tmp->cmnhdr.ecpri_mesg_type = ECPRI_RT_CONTROL_DATA; + tmp->ecpri_xtc_id = xran_compose_cid(0, 0, CC_ID, Ant_ID); + + /* TODO: Transport layer fragmentation is not supported */ + tmp->ecpri_seq_id.seq_id = seq_id; + tmp->ecpri_seq_id.sub_seq_id = 0; + tmp->ecpri_seq_id.e_bit = 1; + + /* Starts with eCPRI header size */ + payloadlen = xran_get_ecpri_hdr_size(); + + *ecpri_hdr = tmp; + + return (payloadlen); +} + +/** + * @brief Parse ECPRI header + * + * @ingroup xran + * + * @param mbuf + * The pointer of the packet buffer to be parsed + * @param ecpri_hdr + * The pointer to ECPRI header + * @param pkt_info + * The pointer of sturcture to store the information from header + * @return + * XRAN_STATUS_SUCCESS on success + * XRAN_STATUS_INVALID_PACKET if failed to parse the packet + */ +int xran_parse_ecpri_hdr(struct rte_mbuf *mbuf, + struct xran_ecpri_hdr **ecpri_hdr, + struct xran_recv_packet_info *pkt_info) +{ + int ret; + + + *ecpri_hdr = rte_pktmbuf_mtod(mbuf, void *); + if(*ecpri_hdr == NULL) { + print_err("Invalid packet - eCPRI hedaer!"); + return (XRAN_STATUS_INVALID_PACKET); + } + + /* Process eCPRI header */ + ret = XRAN_STATUS_SUCCESS; + if((*ecpri_hdr)->cmnhdr.ecpri_ver != XRAN_ECPRI_VER) { + print_err("Invalid eCPRI version - %d", (*ecpri_hdr)->cmnhdr.ecpri_ver); + ret = XRAN_STATUS_INVALID_PACKET; + } + if((*ecpri_hdr)->cmnhdr.ecpri_resv != 0) { + print_err("Invalid reserved field - %d", (*ecpri_hdr)->cmnhdr.ecpri_resv); + ret = XRAN_STATUS_INVALID_PACKET; + } + + if(pkt_info != NULL) { + /* store the information from header */ + pkt_info->ecpri_version = (*ecpri_hdr)->cmnhdr.ecpri_ver; + pkt_info->msg_type = (enum ecpri_msg_type)(*ecpri_hdr)->cmnhdr.ecpri_mesg_type; + pkt_info->payload_len = rte_be_to_cpu_16((*ecpri_hdr)->cmnhdr.ecpri_payl_size); + + pkt_info->seq_id = (*ecpri_hdr)->ecpri_seq_id.seq_id; + pkt_info->subseq_id = (*ecpri_hdr)->ecpri_seq_id.sub_seq_id; + pkt_info->ebit = (*ecpri_hdr)->ecpri_seq_id.e_bit; + xran_decompose_cid((*ecpri_hdr)->ecpri_xtc_id, &(pkt_info->eaxc)); + } + + return (ret); } diff --git a/fhi_lib/lib/src/xran_ul_tables.c b/fhi_lib/lib/src/xran_ul_tables.c index 99feefb..1af11c4 100644 --- a/fhi_lib/lib/src/xran_ul_tables.c +++ b/fhi_lib/lib/src/xran_ul_tables.c @@ -16,7 +16,6 @@ * *******************************************************************************/ - /** * @brief This file defines those table used in 5G NR spec. * @file xran_ul_tables.c @@ -843,8 +842,12 @@ const xRANPrachConfigTableStruct gxranPrachDataTable_mmw[XRAN_PRACH_CONFIG_TABLE { 255, { FORMAT_A3, FORMAT_B3 }, 1, { 0 }, { 7, 15, 23, 31, 39 }, 5, 2, 1, 2, 6 }, }; -const xRANPrachPreambleLRAStruct gxranPreambleforLRA[XRAN_PRACH_PREAMBLE_FORMAT_OF_ABC] = +const xRANPrachPreambleLRAStruct gxranPreambleforLRA[13] = { + {FORMAT_0, 839, 125, 1 , 3168 }, + {FORMAT_1, 839, 125, 2 ,21024 }, + {FORMAT_2, 839, 125, 4 , 4688 }, + {FORMAT_3, 839, 5, 1 , 3168 }, {FORMAT_A1, 139, 15, 2 , 288 }, {FORMAT_A2, 139, 15, 4 , 576 }, {FORMAT_A3, 139, 15, 6 , 864 }, diff --git a/fhi_lib/lib/src/xran_up_api.c b/fhi_lib/lib/src/xran_up_api.c index a8c71f2..3fb5ba9 100644 --- a/fhi_lib/lib/src/xran_up_api.c +++ b/fhi_lib/lib/src/xran_up_api.c @@ -24,21 +24,19 @@ * @author Intel Corporation * **/ +#include #include -#include -#include "xran_fh_lls_cu.h" +#include + +#include "xran_fh_o_du.h" #include "xran_transport.h" #include "xran_up_api.h" -#ifndef MLOG_ENABLED -#include "mlog_lnx_xRAN.h" -#else -#include "mlog_lnx.h" -#endif +#include "xran_printf.h" +#include "xran_mlog_lnx.h" extern uint32_t xran_lib_ota_tti; - /** * @brief Builds eCPRI header in xRAN packet * @@ -63,22 +61,24 @@ static int build_ecpri_hdr(struct rte_mbuf *mbuf, if (NULL == ecpri_hdr) return 1; - ecpri_hdr->ecpri_ver = XRAN_ECPRI_VER; - ecpri_hdr->ecpri_resv = 0; - ecpri_hdr->ecpri_concat = 0; - ecpri_hdr->ecpri_mesg_type = ECPRI_IQ_DATA; + ecpri_hdr->cmnhdr.ecpri_ver = XRAN_ECPRI_VER; + ecpri_hdr->cmnhdr.ecpri_resv = 0; + ecpri_hdr->cmnhdr.ecpri_concat = 0; + ecpri_hdr->cmnhdr.ecpri_mesg_type = ECPRI_IQ_DATA; if (iq_data_offset + iq_samples_bytes_in_mbuf > iq_data_num_bytes) { - ecpri_hdr->ecpri_payl_size = + ecpri_hdr->cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr) + - (iq_data_num_bytes - iq_data_offset)); + (iq_data_num_bytes - iq_data_offset) + + xran_get_ecpri_hdr_size()); ecpri_hdr->ecpri_seq_id.e_bit = 1; /* last segment */ } else { - ecpri_hdr->ecpri_payl_size = + ecpri_hdr->cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr) + - iq_samples_bytes_in_mbuf); + iq_samples_bytes_in_mbuf + + xran_get_ecpri_hdr_size()); ecpri_hdr->ecpri_seq_id.e_bit = 0; } @@ -108,18 +108,20 @@ static int xran_build_ecpri_hdr_ex(struct rte_mbuf *mbuf, uint8_t Ant_ID, uint8_t seq_id) { - struct xran_ecpri_hdr *ecpri_hdr = (struct xran_ecpri_hdr *) - rte_pktmbuf_append(mbuf, sizeof(struct xran_ecpri_hdr)); + char *pChar = rte_pktmbuf_mtod(mbuf, char*); + struct xran_ecpri_hdr *ecpri_hdr = (struct xran_ecpri_hdr *)(pChar + sizeof(struct ether_hdr)); if (NULL == ecpri_hdr) return 1; - ecpri_hdr->ecpri_ver = XRAN_ECPRI_VER; - ecpri_hdr->ecpri_resv = 0; // should be zero - ecpri_hdr->ecpri_concat = 0; - ecpri_hdr->ecpri_mesg_type = ecpri_mesg_type; - ecpri_hdr->ecpri_payl_size = rte_cpu_to_be_16(payl_size - + sizeof(struct data_section_hdr)+sizeof(struct radio_app_common_hdr)); + ecpri_hdr->cmnhdr.ecpri_ver = XRAN_ECPRI_VER; + ecpri_hdr->cmnhdr.ecpri_resv = 0; // should be zero + ecpri_hdr->cmnhdr.ecpri_concat = 0; + ecpri_hdr->cmnhdr.ecpri_mesg_type = ecpri_mesg_type; + ecpri_hdr->cmnhdr.ecpri_payl_size = rte_cpu_to_be_16(payl_size + + sizeof(struct data_section_hdr) + + sizeof(struct radio_app_common_hdr) + + xran_get_ecpri_hdr_size()); /* one to one lls-CU to RU only and band sector is the same */ ecpri_hdr->ecpri_xtc_id = xran_compose_cid(0, 0, CC_ID, Ant_ID); @@ -146,8 +148,9 @@ static int build_application_layer( struct rte_mbuf *mbuf, const struct radio_app_common_hdr *app_hdr_input) { - struct radio_app_common_hdr *app_hdr = (struct radio_app_common_hdr *) - rte_pktmbuf_append(mbuf, sizeof(struct radio_app_common_hdr)); + char *pChar = rte_pktmbuf_mtod(mbuf, char*); + struct radio_app_common_hdr *app_hdr = (struct radio_app_common_hdr *)(pChar + sizeof(struct ether_hdr) + + sizeof (struct xran_ecpri_hdr)); if (NULL == app_hdr) return 1; @@ -168,8 +171,9 @@ static int build_section_hdr( struct rte_mbuf *mbuf, const struct data_section_hdr *sec_hdr) { + char *pChar = rte_pktmbuf_mtod(mbuf, char*); struct data_section_hdr *section_hdr = (struct data_section_hdr *) - rte_pktmbuf_append(mbuf, sizeof(struct data_section_hdr)); + (pChar + sizeof(struct ether_hdr) + sizeof (struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)); if (NULL == section_hdr) return 1; @@ -190,37 +194,35 @@ static int build_section_hdr( static uint16_t append_iq_samples_ex( struct rte_mbuf *mbuf, const void *iq_data_start, - const uint32_t iq_data_num_bytes) + const uint32_t iq_data_num_bytes, + enum xran_input_byte_order iq_buf_byte_order, + uint32_t do_copy) { - uint16_t free_space_in_pkt = rte_pktmbuf_tailroom(mbuf); + char *pChar = rte_pktmbuf_mtod(mbuf, char*); + void *iq_sam_buf = (pChar + sizeof(struct ether_hdr) + sizeof (struct xran_ecpri_hdr) + + sizeof(struct radio_app_common_hdr) + + sizeof(struct data_section_hdr)); - if(free_space_in_pkt >= iq_data_num_bytes){ - - void *iq_sam_buf = (void *)rte_pktmbuf_append(mbuf, iq_data_num_bytes); - if (iq_sam_buf == NULL) - return 0; -#ifdef XRAN_BYTE_ORDER_SWAP + if (iq_sam_buf == NULL){ + print_err("iq_sam_buf == NULL\n"); + return 0; + } + if(iq_buf_byte_order == XRAN_CPU_LE_BYTE_ORDER){ int idx = 0; - uint16_t *restrict psrc = (uint16_t *)iq_data_start; - uint16_t *restrict pdst = (uint16_t *)iq_sam_buf; + uint16_t *psrc = (uint16_t *)iq_data_start; + uint16_t *pdst = (uint16_t *)iq_sam_buf; /* CPU byte order (le) of IQ to network byte order (be) */ for (idx = 0; idx < iq_data_num_bytes/sizeof(int16_t); idx++){ pdst[idx] = (psrc[idx]>>8) | (psrc[idx]<<8); //rte_cpu_to_be_16(psrc[idx]); } -#else -#error xran spec is network byte order - /* for debug */ - rte_memcpy(iq_sam_buf, (uint8_t *)iq_data_start, iq_data_num_bytes); - -#endif - - return iq_data_num_bytes; + }else if(iq_buf_byte_order == XRAN_NE_BE_BYTE_ORDER){ + if(do_copy) + rte_memcpy(iq_sam_buf, (uint8_t *)iq_data_start, iq_data_num_bytes); } - return 0; + return iq_data_num_bytes; } - /** * @brief Function for appending IQ samples data to the mbuf. * @@ -363,10 +365,17 @@ int xran_extract_iq_samples(struct rte_mbuf *mbuf, uint8_t *subframe_id, uint8_t *slot_id, uint8_t *symb_id, - struct ecpri_seq_id *seq_id) + struct ecpri_seq_id *seq_id, + uint16_t *num_prbu, + uint16_t *start_prbu, + uint16_t *sym_inc, + uint16_t *rb, + uint16_t *sect_id) { +#if XRAN_MLOG_VAR uint32_t mlogVar[10]; uint32_t mlogVarCnt = 0; +#endif struct xran_eaxc_info result; if (NULL == mbuf) @@ -408,11 +417,20 @@ int xran_extract_iq_samples(struct rte_mbuf *mbuf, *symb_id = radio_hdr->sf_slot_sym.symb_id; /* Process data section hdr */ - const struct data_section_hdr *data_hdr = + struct data_section_hdr *data_hdr = (void *)rte_pktmbuf_adj(mbuf, sizeof(*radio_hdr)); if (data_hdr == NULL) return 0; /* packet too short */ + /* cpu byte order */ + data_hdr->fields.all_bits = rte_be_to_cpu_32(data_hdr->fields.all_bits); + + *num_prbu = data_hdr->fields.num_prbu; + *start_prbu = data_hdr->fields.start_prbu; + *sym_inc = data_hdr->fields.sym_inc; + *rb = data_hdr->fields.rb; + *sect_id = data_hdr->fields.sect_id; + #ifdef COMPRESSION const struct data_section_compression_hdr *data_compr_hdr = (void *) rte_pktmbuf_adj(mbuf, sizeof(*data_hdr)); @@ -428,14 +446,19 @@ int xran_extract_iq_samples(struct rte_mbuf *mbuf, if (*iq_data_start == NULL) return 0; - mlogVar[mlogVarCnt++] = 0xBBBBBBB; +#if XRAN_MLOG_VAR + mlogVar[mlogVarCnt++] = 0xBBBBBBBB; mlogVar[mlogVarCnt++] = xran_lib_ota_tti; mlogVar[mlogVarCnt++] = radio_hdr->frame_id; mlogVar[mlogVarCnt++] = radio_hdr->sf_slot_sym.subframe_id; mlogVar[mlogVarCnt++] = radio_hdr->sf_slot_sym.slot_id; mlogVar[mlogVarCnt++] = radio_hdr->sf_slot_sym.symb_id; + mlogVar[mlogVarCnt++] = data_hdr->fields.sect_id; + mlogVar[mlogVarCnt++] = data_hdr->fields.start_prbu; + mlogVar[mlogVarCnt++] = data_hdr->fields.num_prbu; mlogVar[mlogVarCnt++] = rte_pktmbuf_pkt_len(mbuf); MLogAddVariables(mlogVarCnt, mlogVar, MLogTick()); +#endif return rte_pktmbuf_pkt_len(mbuf); } @@ -456,26 +479,34 @@ int xran_extract_iq_samples(struct rte_mbuf *mbuf, int xran_prepare_iq_symbol_portion_no_comp( struct rte_mbuf *mbuf, const void *iq_data_start, + const enum xran_input_byte_order iq_buf_byte_order, const uint32_t iq_data_num_bytes, struct xran_up_pkt_gen_no_compression_params *params, uint8_t CC_ID, uint8_t Ant_ID, - uint8_t seq_id) + uint8_t seq_id, + uint32_t do_copy) { if(xran_build_ecpri_hdr_ex(mbuf, ECPRI_IQ_DATA, iq_data_num_bytes, CC_ID, Ant_ID, - seq_id)) + seq_id)){ + print_err("xran_build_ecpri_hdr_ex return 0\n"); return 0; + } - if (build_application_layer(mbuf, &(params->app_params)) != 0) + if (build_application_layer(mbuf, &(params->app_params)) != 0){ + print_err("build_application_layer return != 0\n"); return 0; + } - if (build_section_hdr(mbuf, &(params->sec_hdr)) != 0) + if (build_section_hdr(mbuf, &(params->sec_hdr)) != 0){ + print_err("build_section_hdr return != 0\n"); return 0; + } - return append_iq_samples_ex(mbuf, iq_data_start, iq_data_num_bytes); + return append_iq_samples_ex(mbuf, iq_data_start, iq_data_num_bytes, iq_buf_byte_order, do_copy); } diff --git a/fhi_lib/readme.txt b/fhi_lib/readme.txt index 43f070c..3168b83 100644 --- a/fhi_lib/readme.txt +++ b/fhi_lib/readme.txt @@ -12,42 +12,53 @@ #* 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. +#* limitations under the License. #* #*******************************************************************************/ 1. Introduction xRAN Lib performs communication between the low-layer split central unit (lls-CU) and RU, it is highly-optimized software implementation based on Intel Architecture to provide the standard interface implementation based on O-RAN front haul interface specification. 2. Supported features -please refer PRD in the table <>, only ICC compiler was supported for this version. +Please refer to the Document ORAN Front Haul Interface Library based on Intel's xRAN Front Haul SW Architecture Specifications Section 4.2 Supported Feature Set, both GCC/ICC compiler are supported for this version. 3. Fixed Issues It's first version of seed code for feature development, future fixed issues will be tracked here. 4. Known Issues -From current unit testing coverage, no Know issues was founded yet. +From current unit testing coverage, no issues have been found yet. 5. Prerequisites for install -5.1 Intel Compiler version +5.1. Prerequisites + +5.1.0 System configuration + +VFIO requires: +linux: + IOMMU=ON +BIOS: + Intel(R) Virtualization Technology Enabled + Intel(R) VT for Directed I/O - Enabled + ACS Control - Enabled + Coherency Support - Disabled +5.1.1 Compiler + icc -v -icc version 18.0.1 (gcc version 4.8.5 compatibility) +icc version 19.0.3.206 (gcc version 4.8.5 compatibility) Link to ICC (community free version): https://software.intel.com/en-us/system-studio/choose-download#technical +5.1.2 DPDK 18.08 -5.2 DPDK version -dpdk_18.08 - -5.3 compile DPDK with command -[dpdk]# ./usertools/dpdk-setup.sh +5.1.3 Compile DPDK with +[root@5gnr-sc12-xran dpdk]# ./usertools/dpdk-setup.sh // Where the root@5gnr-sc12-xran dpdk corresponds to the location in the server for the dpdk installation folder select [16] x86_64-native-linuxapp-icc -select [18] Insert IGB UIO module +select [19] Insert VFIO module exit [35] Exit Script -5.4 Find PCIe device of Fortville port +5.1.4 Find PCIe device of Fortville port lspci |grep Eth 19:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01) @@ -57,42 +68,41 @@ lspci |grep Eth d8:00.0 << Ethernet controller: Intel Corporation Ethernet Controller XL710 for 40GbE QSFP+ (rev 02) <<<< this one d8:00.1 Ethernet controller: Intel Corporation Ethernet Controller XL710 for 40GbE QSFP+ (rev 02) -5.5 Corresponding Eth device via +5.1.5 Corresponding Eth device via ifconfig -a find port Eth with correct PCIe Bus address as per list above -ethtool -i enp216s0f0 +ethtool -i enp218s0f0 driver: i40e -version: 2.4.10 << i40e driver -firmware-version: 6.01 0x800034a4 1.1747.0 +version: 2.4.10 << driver +firmware-version: 6.80 0x80003cfd 1.2007.0 expansion-rom-version: -bus-info: 0000:d8:00.0 <<< this one +bus-info: 0000:da:00.0 << this one supports-statistics: yes supports-test: yes supports-eeprom-access: yes supports-register-dump: yes supports-priv-flags: yes -5.6 install correct 2.4.10 i40e version if different (https://downloadcenter.intel.com/download/28306/Intel-Network-Adapter-Driver-for-PCIe-40-Gigabit-Ethernet-Network-Connections-Under-Linux-) +5.1.6 install correct 2.4.10 i40e version if different (https://downloadcenter.intel.com/download/28306/Intel-Network-Adapter-Driver-for-PCIe-40-Gigabit-Ethernet-Network-Connections-Under-Linux-) -make sure firmare version is +make sure firmare version is at least this version or higher -firmware-version: 6.01 +firmware-version: 6.01 -5.7 make sure that linux boot arguments are correct +5.1.7 make sure that linux boot arguments are correct cat /proc/cmdline -BOOT_IMAGE=/vmlinuz-3.10.0-rt56 root=/dev/mapper/centos_5gnr--skx--sp-root ro crashkernel=auto rd.lvm.lv=centos_5gnr-skx-sp/root rd.lvm.lv=centos_5gnr-skx-sp/swap intel_iommu=off usbcore.autosuspend=-1 selinux=0 enforcing=0 nmi_watchdog=0 softlockup_panic=0 audit=0 intel_pstate=disable cgroup_disable=memory mce=off idle=poll hugepagesz=1G hugepages=20 hugepagesz=2M hugepages=0 default_hugepagesz=1G isolcpus=1-35 rcu_nocbs=1-35 kthread_cpus=0 irqaffinity=0 nohz_full=1-35 - -5.8 enable SRIOV VF port for XRAN +BOOT_IMAGE=/vmlinuz-3.10.0-rt56 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap intel_iommu=on iommu=pt usbcore.autosuspend=-1 selinux=0 enforcing=0 nmi_watchdog=0 softlockup_panic=0 audit=0 intel_pstate=disable cgroup_disable=memory mce=off idle=poll hugepagesz=1G hugepages=20 hugepagesz=2M hugepages=0 default_hugepagesz=1G isolcpus=1-39 rcu_nocbs=1-39 kthread_cpus=0 irqaffinity=0 nohz_full=1-39 +1.10 enable SRIOV VF port for XRAN echo 2 > /sys/class/net/enp216s0f0/device/sriov_numvfs see https://doc.dpdk.org/guides/nics/intel_vf.html -5.9 Check Virtual Function was created +5.1.8 Check Virtual Function was created lspci |grep Eth 19:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01) @@ -104,7 +114,7 @@ d8:00.1 Ethernet controller: Intel Corporation Ethernet Controller XL710 for 40G d8:02.0 Ethernet controller: Intel Corporation XL710/X710 Virtual Function (rev 02) <<<< this is XRAN port (u-plane) d8:02.1 Ethernet controller: Intel Corporation XL710/X710 Virtual Function (rev 02) <<<< this is XRAN port (c-plane) -5.10 Configure VFs +5.1.9 Configure VFs - set mac to 00:11:22:33:44:66 - set Vlan tag to 2 (U-plane) for VF0 - set Vlan tag to 1 (C-plane) for VF1 @@ -136,32 +146,342 @@ d8:02.1 Ethernet controller: Intel Corporation XL710/X710 Virtual Function (rev link/ether 96:fa:4d:04:4d:87 brd ff:ff:ff:ff:ff:ff 13: enp216s2f1: mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000 link/ether a6:67:49:bb:bd:5e brd ff:ff:ff:ff:ff:ff - -6. Install xRAN Lib - -6.1 start matlab and run gen_test.m -copy ant_*.bin to /xran/app - -6.2 build xran sample application -export XRAN_DIR=xRAN folder -export RTE_SDK=dpdk folder -[xRAN root folder]$ ./build.sh -6.3 update Eth port used for XRAN -in ./app/run_lls-cu.sh -ports have to match VF function from step 1.11 (0000:d8:02.0 - U-plane 0000:d8:02.1 C-plane) - -6.4 Run dpdk.sh to assign port to PMD - -[xran root folder]# ./app/dpdk.sh +6. Install + +6.1.1 start matlab and run gen_test.m with correct Numerology, Bandwidth and number of slots +copy ant_*.bin to /xran/app/usecase/mu{X}_{Y}MHz + where X is numerology: 0,1,3 + Y is 5,10,20,100 MHz bandwidth + +6.1.2 compile xran sample application (Please make sure that the export match your install directories for SDK, ORAN_FH_lib (i.e. XRAN_DIR), google test +export RTE_SDK=/opt/dpdk-18.08 +export RTE_TARGET=x86_64-native-linuxapp-icc +export XRAN_DIR= /home/npg_wireless-flexran_xran/ +export export GTEST_ROOT=/opt/gtest/gtest-1.7.0 + + ./build.sh +Number of commandline arguments: 0 +Building xRAN Library +LIBXRANSO=0 + CC ../lib/ethernet/ethdi.o + CC ../lib/ethernet/ethernet.o + CC ../lib/src/xran_up_api.o + CC ../lib/src/xran_sync_api.o + CC ../lib/src/xran_timer.o + CC ../lib/src/xran_cp_api.o + CC ../lib/src/xran_transport.o + CC ../lib/src/xran_common.o + CC ../lib/src/xran_ul_tables.o + CC ../lib/src/xran_frame_struct.o + CC ../lib/src/xran_compression.o + CC ../lib/src/xran_app_frag.o + CC ../lib/src/xran_main.o + AR libxran.a + INSTALL-LIB libxran.a +Building xRAN Test Application + CC ../app/src/common.o + CC ../app/src/sample-app.o +remark #11074: Inlining inhibited by limit max-size +remark #11076: To get full report use -qopt-report=4 -qopt-report-phase ipo + CC ../app/src/config.o + LD sample-app + INSTALL-APP sample-app + INSTALL-MAP sample-app.map + + +6.1.3 update Eth port used for XRAN + + +cat ./run_o_du.sh +#! /bin/bash + +ulimit -c unlimited +echo 1 > /proc/sys/kernel/core_uses_pid + +grep Huge /proc/meminfo +huge_folder="/mnt/huge_bbu" +[ -d "$huge_folder" ] || mkdir -p $huge_folder +if ! mount | grep $huge_folder; then + mount none $huge_folder -t hugetlbfs -o rw,mode=0777 +fi + +#40G +./build/sample-app ./usecase/mu3_100mhz/config_file_o_du.dat 0000:da:02.0 0000:da:02.1 + ^^^^^ ports have to match VF function from step 1.11 (0000:da:02.0 - U-plane 0000:da:02.1 C-plane) + +umount $huge_folder +rmdir $huge_folder + + +cat ./dpdk.sh +... +$RTE_SDK/usertools/dpdk-devbind.py --status +if [ ${VM_DETECT} == 'HOST' ]; then + #HOST + + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:da:02.0 <<< port has to match VF function from step 1.11 + $RTE_SDK/usertools/dpdk-devbind.py --bind=vfio-pci 0000:da:02.1 <<< port has to match VF function from step 1.11 + + 1. +Run + +6.2.1 Run dpdk.sh to assign port to PMD + +[root@5gnr-sc12-xran app]# ./dpdk.sh Network devices using DPDK-compatible driver ============================================ -0000:d8:02.0 'XL710/X710 Virtual Function 154c' drv=igb_uio unused=i40evf -0000:d8:02.1 'XL710/X710 Virtual Function 154c' drv=igb_uio unused=i40evf - - -6.5 Run XRAN lls-CU sample app -setup RU mac address in config_file_lls_cu.dat -[xran root folder]# ./app/run_lls-cu.sh - +0000:da:02.0 'XL710/X710 Virtual Function 154c' drv=vfio-pci unused=i40evf,igb_uio +0000:da:02.1 'XL710/X710 Virtual Function 154c' drv=vfio-pci unused=i40evf,igb_uio + + +6.2.2 Run XRAN sample app +setup RU mac address in config_file_o_du.dat for corespondig usecase + +e.g. +./build/sample-app ./usecase/mu3_100mhz/config_file_o_du.dat 0000:da:02.0 0000:da:02.1 + +ruMac=00:11:22:33:44:55 #RU VF for RU + +execute O-DU sample app + +[root@sc12-xran-ru-1 app]# ./run_o_du.sh +HugePages_Total: 20 +HugePages_Free: 11 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 1048576 kB +Machine is synchronized using PTP! +mu_number: 3 +nDLAbsFrePointA: 27968160 +nULAbsFrePointA: 27968160 +nDLBandwidth: 100 +nULBandwidth: 100 +nULFftSize: 1024 +nULFftSize: 1024 +nFrameDuplexType: 1 +nTddPeriod: 4 +sSlotConfig0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +sSlotConfig1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +sSlotConfig2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +sSlotConfig3: 0 2 2 1 1 1 1 1 1 1 1 1 1 1 +mtu 9600 +lls-CU MAC address: 00:11:22:33:44:66 +RU MAC address: 00:11:22:33:44:55 +numSlots: 40 +antC0: ./usecase/mu3_100mhz/ant_0.bin +antC1: ./usecase/mu3_100mhz/ant_1.bin +antC2: ./usecase/mu3_100mhz/ant_2.bin +antC3: ./usecase/mu3_100mhz/ant_3.bin +antC4: ./usecase/mu3_100mhz/ant_4.bin +antC5: ./usecase/mu3_100mhz/ant_5.bin +antC6: ./usecase/mu3_100mhz/ant_6.bin +antC7: ./usecase/mu3_100mhz/ant_7.bin +antC8: ./usecase/mu3_100mhz/ant_8.bin +antC9: ./usecase/mu3_100mhz/ant_9.bin +antC10: ./usecase/mu3_100mhz/ant_10.bin +antC11: ./usecase/mu3_100mhz/ant_11.bin +antC12: ./usecase/mu3_100mhz/ant_12.bin +antC13: ./usecase/mu3_100mhz/ant_13.bin +antC14: ./usecase/mu3_100mhz/ant_14.bin +antC15: ./usecase/mu3_100mhz/ant_15.bin +Prach enable: 0 +Prach config index: 81 +debugStop: 1 +CPenable: 1 +cp_vlan_tag: 1 +up_vlan_tag: 2 +Tadv_cp_dl: 25 +T2a_min_cp_dl: 50 +T2a_max_cp_dl: 140 +T2a_min_cp_ul: 50 +T2a_max_cp_ul: 140 +T2a_min_up: 25 +T2a_max_up: 140 +Ta3_min: 20 +Ta3_max: 32 +T1a_min_cp_dl: 70 +T1a_max_cp_dl: 100 +T1a_min_cp_ul: 60 +T1a_max_cp_ul: 70 +T1a_min_up: 35 +T1a_max_up: 50 +Ta4_min: 0 +Ta4_max: 45 +115 lines of config file has been read. +numCCPorts 1 num_eAxc4 +set O-DU +IQ files size is 40 slots +app_xran_get_num_rbs: nNumerology[3] nBandwidth[100] nAbsFrePointA[27968160] numRBs[66] +app_xran_get_num_rbs: nNumerology[3] nBandwidth[100] nAbsFrePointA[27968160] numRBs[66] +Loading file ./usecase/mu3_100mhz/ant_0.bin to DL IFFT IN IQ Samples in binary format: Reading IQ samples from file: File Size: 1774080 [Buffer Size: 1774080] +from addr (0x7f62ad088010) size (1774080) bytes num (1774080) +Loading file ./usecase/mu3_100mhz/ant_1.bin to DL IFFT IN IQ Samples in binary format: Reading IQ samples from file: File Size: 1774080 [Buffer Size: 1774080] +from addr (0x7f62aced6010) size (1774080) bytes num (1774080) +Loading file ./usecase/mu3_100mhz/ant_2.bin to DL IFFT IN IQ Samples in binary format: Reading IQ samples from file: File Size: 1774080 [Buffer Size: 1774080] +from addr (0x7f62acd24010) size (1774080) bytes num (1774080) +Loading file ./usecase/mu3_100mhz/ant_3.bin to DL IFFT IN IQ Samples in binary format: Reading IQ samples from file: File Size: 1774080 [Buffer Size: 1774080] +from addr (0x7f62acb72010) size (1774080) bytes num (1774080) +Storing DL IFFT IN IQ Samples in human readable format to file ./logs/o-du-play_ant0.txt: from addr (0x7f62ad088010) size (1774080) IQ num (443520) +Storing DL IFFT IN IQ Samples in binary format to file ./logs/o-du-play_ant0.bin: from addr (0x7f62ad088010) size (887040) bytes num (887040) +Storing DL IFFT IN IQ Samples in human readable format to file ./logs/o-du-play_ant1.txt: from addr (0x7f62aced6010) size (1774080) IQ num (443520) +Storing DL IFFT IN IQ Samples in binary format to file ./logs/o-du-play_ant1.bin: from addr (0x7f62aced6010) size (887040) bytes num (887040) +Storing DL IFFT IN IQ Samples in human readable format to file ./logs/o-du-play_ant2.txt: from addr (0x7f62acd24010) size (1774080) IQ num (443520) +Storing DL IFFT IN IQ Samples in binary format to file ./logs/o-du-play_ant2.bin: from addr (0x7f62acd24010) size (887040) bytes num (887040) +Storing DL IFFT IN IQ Samples in human readable format to file ./logs/o-du-play_ant3.txt: from addr (0x7f62acb72010) size (1774080) IQ num (443520) +Storing DL IFFT IN IQ Samples in binary format to file ./logs/o-du-play_ant3.bin: from addr (0x7f62acb72010) size (887040) bytes num (887040) +TX: Convert S16 I and S16 Q to network byte order for XRAN Ant: [0] +TX: Convert S16 I and S16 Q to network byte order for XRAN Ant: [1] +TX: Convert S16 I and S16 Q to network byte order for XRAN Ant: [2] +TX: Convert S16 I and S16 Q to network byte order for XRAN Ant: [3] +System clock (rdtsc) resolution 1596250371 [Hz] +Ticks per us 1596 + xran_init: MTU 9600 +xran_ethdi_init_dpdk_io: Calling rte_eal_init:wls -c ffffffff -m5120 --proc-type=auto --file-prefix wls -w 0000:00:00.0 +EAL: Detected 40 lcore(s) +EAL: Detected 2 NUMA nodes +EAL: Auto-detected process type: PRIMARY +EAL: Multi-process socket /var/run/dpdk/wls/mp_socket +EAL: No free hugepages reported in hugepages-2048kB +EAL: Probing VFIO support... +EAL: VFIO support initialized +EAL: PCI device 0000:da:02.0 on NUMA socket 1 +EAL: probe driver: 8086:154c net_i40e_vf +EAL: using IOMMU type 1 (Type 1) +initializing port 0 for TX, drv=net_i40e_vf +Port 0 MAC: 00 11 22 33 44 66 + +Checking link status ... done +Port 0 Link Up - speed 40000 Mbps - full-duplex +EAL: PCI device 0000:da:02.1 on NUMA socket 1 +EAL: probe driver: 8086:154c net_i40e_vf +initializing port 1 for TX, drv=net_i40e_vf +Port 1 MAC: 00 11 22 33 44 66 + +Checking link status ... done +Port 1 Link Up - speed 40000 Mbps - full-duplex +Set debug stop 1 +FFT Order 10 +app_xran_get_num_rbs: nNumerology[3] nBandwidth[100] nAbsFrePointA[27968160] numRBs[66] +app_xran_get_num_rbs: nNumerology[3] nBandwidth[100] nAbsFrePointA[27968160] numRBs[66] +app_xran_cal_nrarfcn: nCenterFreq[28015680] nDeltaFglobal[60] nFoffs[24250080] nNoffs[2016667] nNRARFCN[2079427] +DL center freq 28015680 DL NR-ARFCN 2079427 +app_xran_cal_nrarfcn: nCenterFreq[28015680] nDeltaFglobal[60] nFoffs[24250080] nNoffs[2016667] nNRARFCN[2079427] +UL center freq 28015680 UL NR-ARFCN 2079427 +XRAN front haul xran_mm_init +xran_sector_get_instances [0]: CC 0 handle 0xd013380 +Handle: 0x5a07cb8 Instance: 0xd013380 +init_xran [0]: CC 0 handle 0xd013380 +Sucess xran_mm_init +nSectorNum 1 +nSectorIndex[0] = 0 +[ handle 0xd013380 0 0 ] [nPoolIndex 0] nNumberOfBuffers 4480 nBufferSize 3328 +CC:[ handle 0xd013380 ru 0 cc_idx 0 ] [nPoolIndex 0] mb pool 0x24a7ad440 +nSectorIndex[0] = 0 +[ handle 0xd013380 0 0 ] [nPoolIndex 1] nNumberOfBuffers 4480 nBufferSize 2216 +CC:[ handle 0xd013380 ru 0 cc_idx 0 ] [nPoolIndex 1] mb pool 0x24956d100 +[ handle 0xd013380 0 0 ] [nPoolIndex 2] nNumberOfBuffers 4480 nBufferSize 3328 +CC:[ handle 0xd013380 ru 0 cc_idx 0 ] [nPoolIndex 2] mb pool 0x248818dc0 +[ handle 0xd013380 0 0 ] [nPoolIndex 3] nNumberOfBuffers 4480 nBufferSize 2216 +CC:[ handle 0xd013380 ru 0 cc_idx 0 ] [nPoolIndex 3] mb pool 0x2475d8a80 +[ handle 0xd013380 0 0 ] [nPoolIndex 4] nNumberOfBuffers 4480 nBufferSize 8192 +CC:[ handle 0xd013380 ru 0 cc_idx 0 ] [nPoolIndex 4] mb pool 0x246884740 +@@@ NB cell 0 DL NR-ARFCN 0,DL phase comp flag 0 UL NR-ARFCN 0,UL phase comp flag 0 +init_xran_iq_content +xRAN open PRACH config: Numerology 3 ConfIdx 81, preambleFmrt 6 startsymb 7, numSymbol 6, occassionsInPrachSlot 1 +PRACH: x 1 y[0] 0, y[1] 0 prach slot: 3.. 5 .... 7 .... 9 .... 11 .... 13 .. + +PRACH start symbol 7 lastsymbol 12 +xran_cp_init_sectiondb:Allocation Size for Section DB : 128 (1x8x16) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for Section DB : 128 (1x8x16) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_cp_init_sectiondb:Allocation Size for list : 1848 (28x66) +xran_open: interval_us=125 +nSlotNum[0] : numDlSym[14] numGuardSym[0] numUlSym[0] XRAN_SLOT_TYPE_DL + numDlSlots[1] numUlSlots[0] numSpSlots[0] numSpDlSlots[0] numSpUlSlots[0] +nSlotNum[1] : numDlSym[14] numGuardSym[0] numUlSym[0] XRAN_SLOT_TYPE_DL + numDlSlots[2] numUlSlots[0] numSpSlots[0] numSpDlSlots[0] numSpUlSlots[0] +nSlotNum[2] : numDlSym[14] numGuardSym[0] numUlSym[0] XRAN_SLOT_TYPE_DL + numDlSlots[3] numUlSlots[0] numSpSlots[0] numSpDlSlots[0] numSpUlSlots[0] +nSlotNum[3] : numDlSym[1] numGuardSym[2] numUlSym[11] XRAN_SLOT_TYPE_SP + numDlSlots[3] numUlSlots[0] numSpSlots[1] numSpDlSlots[1] numSpUlSlots[1] +xran_fs_set_slot_type: nPhyInstanceId[0] nFrameDuplexType[1], nTddPeriod[4] +DLRate[1.000000] ULRate[0.250000] +SlotPattern: +Slot: 0 1 2 3 + 0 DL DL DL SP + +xran_timing_source_thread [CPU 7] [PID: 292331] +MLogOpen: filename(mlog-o-du.bin) mlogSubframes (0), mlogCores(32), mlogSize(0) mlog_mask (-1) + mlogSubframes (256), mlogCores(32), mlogSize(7168) + localMLogTimerInit +lls-CU: thread_run start time: 06/10/19 21:09:37.000000028 UTC [125] +Start C-plane DL 25 us after TTI [trigger on sym 3] +Start C-plane UL 55 us after TTI [trigger on sym 7] +Start U-plane DL 50 us before OTA [offset in sym -6] +Start U-plane UL 45 us OTA [offset in sym 6] +C-plane to U-plane delay 25 us after TTI +Start Sym timer 8928 ns +interval_us 125 + System clock (CLOCK_REALTIME) resolution 1000037471 [Hz] + Ticks per us 1000 + MLog Storage: 0x7f6298487100 -> 0x7f629bc88d20 [ 58727456 bytes ] + localMLogFreqReg: 1000. Storing: 1000 + Mlog Open successful + +---------------------------------------- +MLog Info: virt=0x00007f6298487100 size=58727456 +---------------------------------------- +Start XRAN traffic ++---------------------------------------+ +| Press 1 to start 5G NR XRAN traffic | +| Press 2 reserved for future use | +| Press 3 to quit | ++---------------------------------------+ +rx_counter 0 tx_counter 1376072 +rx_counter 0 tx_counter 1720112 +rx_counter 0 tx_counter 2064161 +rx_counter 0 tx_counter 2408212 +rx_counter 0 tx_counter 2752232 + +type 3 to stop +3 +rx_counter 0 tx_counter 3096264 +Stop XRAN traffic +get_xran_iq_content +Closing timing source thread... +Closing l1 app... Ending all threads... +MLogPrint: ext_filename((null).bin) + Opening MLog File: mlog-o-du-c0.bin + MLog file mlog-o-du-c0.bin closed + Mlog Print successful + +Failed at xran_mm_destroy, status -2 +Dump IQs... +RX: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [0] +RX: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [1] +RX: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [2] +RX: Convert S16 I and S16 Q to cpu byte order from XRAN Ant: [3] +Storing UL FFT OUT IQ Samples in human readable format to file ./logs/o-du-rx_log_ant0.txt: from addr (0x7f62ac9c0010) size (1774080) IQ num (443520) +Storing UL FFT OUT IQ Samples in binary format to file ./logs/o-du-rx_log_ant0.bin: from addr (0x7f62ac9c0010) size (887040) bytes num (887040) +Storing UL FFT OUT IQ Samples in human readable format to file ./logs/o-du-rx_log_ant1.txt: from addr (0x7f62ac80e010) size (1774080) IQ num (443520) +Storing UL FFT OUT IQ Samples in binary format to file ./logs/o-du-rx_log_ant1.bin: from addr (0x7f62ac80e010) size (887040) bytes num (887040) +Storing UL FFT OUT IQ Samples in human readable format to file ./logs/o-du-rx_log_ant2.txt: from addr (0x7f62ac65c010) size (1774080) IQ num (443520) +Storing UL FFT OUT IQ Samples in binary format to file ./logs/o-du-rx_log_ant2.bin: from addr (0x7f62ac65c010) size (887040) bytes num (887040) +Storing UL FFT OUT IQ Samples in human readable format to file ./logs/o-du-rx_log_ant3.txt: from addr (0x7f62ac4aa010) size (1774080) IQ num (443520) +Storing UL FFT OUT IQ Samples in binary format to file ./logs/o-du-rx_log_ant3.bin: from addr (0x7f62ac4aa010) size (887040) bytes num (887040) -- 2.16.6