diff --git a/SystemX-tutorial-design.md b/SystemX-tutorial-design.md new file mode 100644 index 0000000000..6371c81050 --- /dev/null +++ b/SystemX-tutorial-design.md @@ -0,0 +1,264 @@ +# OpenAirInterface for SystemX + +#Terminology + +****This document use the 5G terminology**** + +**Central Unit (CU): **It is a logical node that includes the gNB +functions like Transfer of user data, Mobility control, Radio access +network sharing, Positioning, Session Management etc., except those +functions allocated exclusively to the DU. CU controls the operation of +DUs over front-haul (Fs) interface. A central unit (CU) may also be +known as BBU/REC/RCC/C-RAN/V-RAN/VNF + +**Distributed Unit (DU):** This logical node includes a subset of the +gNB functions, depending on the functional split option. Its operation +is controlled by the CU. Distributed Unit (DU) also known with other +names like RRH/RRU/RE/RU/PNF. + +In OpenAir code, the terminology is often RU and BBU. + +#Usage +##EPC and general environment +### OAI EPC + +Use the stable OAI EPC, that can run in one machine (VM or standalone) + +Draft description: + + +##Standalone 4G + +EPC+eNB on one machine, the UE can be commercial or OAI UE. +### USRP B210 + +Main current issue: traffic is good only on coaxial link between UE and +eNB (probably power management issue). + +### Simulated RF + +Running eNB+UE both OAI can be done over a virtual RF link. + +The UE current status is that threads synchronization is implicit in +some cases. As the RF simulator is very quick, a “sleep()” is required +in the UE main loop + +(line 1744, targets/RT/USER/lte-ue.c). + +Running also the UE in the same machine is possible with simulated RF. + +Running in same machine is simpler, offers about infinite speed for +virtual RF samples transmission. + +A specific configuration is required because the EPC Sgi interface has +the same IP tunnel end point as the UE. + +So, we have to create a network namespace for the UE and to route data +in/out of the namespace. +``` bash +ip netns delete aNameSpace 2> /dev/null + +ip link delete v-eth1 2> /dev/null + +ip netns add aNameSpace + +ip link add v-eth1 type veth peer name v-peer1 + +ip link set v-peer1 netns aNameSpace + +ip addr add 10.200.1.1/24 dev v-eth1 + +ip link set v-eth1 up + +iptables -t nat -A POSTROUTING -s 10.200.1.0/255.255.255.0 -o enp0s31f6 +-j MASQUERADE + +iptables -A FORWARD -i enp0s31f6 -o v-eth1 -j ACCEPT + +iptables -A FORWARD -o enp0s31f6 -i v-eth1 -j ACCEPT + +ip netns exec aNameSpace ip link set dev lo up + +ip netns exec aNameSpace ip addr add 10.200.1.2/24 dev v-peer1 + +ip netns exec aNameSpace ip link set v-peer1 up + +ip netns exec aNameSpace bash +``` + +After the last command, the Linux shell is in the new namespace, ready +to run the UE. + +To make user plan traffic, the traffic generator has to run in the same +namespace + +`ip netns exec aNameSpace bash +` + +The traffic genenrator has to specify the interface: + +`route add default oaitun_ue1 +` + +or specify the outgoing route in the traffic generator (like option “-I” +in ping command). + +## Split 6 DL 4G + +The contract describes to reuse the uplink existing if4p5 and to develop +is this work the downlink “functional split 6”. + +The customer required after signature to develop also the uplink +functional split 6. This is accepted, as long as the whole work is +research with no delivery completeness warranty. +### Simulation + +To be able to verify the new features and to help in all future +developments, Open Cells added and improved the Rf board simulator +during this contract. + +We added the channel modeling simulation, that offer to simulate various +3GPP defined channels. +### Main loop + +The main log is in RF simulator is in + + targets/RT/USER/lte-ru.c and targets/RT/USER/lte-enb.c + +As this piece of SW is very complex and doesn’t meet our goals +(functional split 6), a cleaned version replaces these 2 files in +executables/ocp-main.c (openair1/SCHED/prach\_procedures.c is also +replaced by this new file as it only launching the RACH actual work in a +way not compatible with our FS6). + +The main loop cadences the I/Q samples reception, signal processing and +I/Q samples sending. + +The main loop uses extensively function pointers to call the right +processing function depending on the split case. + +This is enough for RF board dialog, but the FS6 is higher in SW layers, +we need to cut higher functions inside downlink and uplink procedures. + +### DownLink + +The main procedure is phy\_procedures\_eNB\_TX + +This is building the common channels (beacon, multi-UE signaling). + +The FS6 split breaks this function into pieces: + +- The multi-UE signals, built by common\_signal\_procedures(), + subframe2harq\_pid(), generate\_dci\_top(), subframe2harq\_pid() + + - These functions will be executed in the DU, nevertheless all + context has to be sent (it is also needed partially for + UL spitting) + - IT should be in the DU also to meet the requirement of pushing + in DU the data encoded with large redundancy (>3 redundancy) +- the per UE data: pdsch\_procedures() that needs further splitting: + + - dlsch\_encoding\_all() that makes the encoding: turbo code + and lte\_rate\_matching\_turbo() that will be in the DU (some + unlikely cases can reach redundancy up to x3, when MCS is very + low (negative SINR cases)). + + - dlsch\_encoding() output needs to be transmitted between the + DU and the CU for functional split 6. + - dlsch\_scrambling() that will go in the DU + - dlsch\_modulation() that will go in the DU + +### Uplink + +The uplink require configuration that is part of the DL transmission. + +It interprets the signalling to extract the RACH and the per UE data +channels. + +Ocp-main.c:rxtx() calls directly the entry procedure +phy\_procedures\_eNB\_uespec\_RX() calls: + +- rx\_ulsch() that demodulate and extract soft bits per UE. + + - This function runs in the DU + - the output data will be processes in the DU, so it needs to be + transmitted to the DU +- ulsch\_decoding() that do lte\_rate\_matching\_turbo\_rx() + sub\_block\_deinterleaving\_turbo() then turbo decode + + - it runs +- fill\_ulsch\_cqi\_indication() + + - TBD: either runs in DU or output needs to be transmitted to DU +- fill\_crc\_indication() , fill\_rx\_indication() results need also + to be transmitted or made in the DU + +### UDP transport layer + +A general UDP transport layer is in executables/transport\_split.c + +Linux offers a UDP socket builtin timeout, that we use. + +In and out buffers are memory zones that contains compacted +(concatenated) UDP chunks. + +For output, sendSubFrame() sends each UDP chunk + +For input, receiveSubFrame() collects all UDP chunks for a group (a +subframe in OAI LTE case). It returns in the following cases: + +- all chunks are received +- a timeout expired +- a chunk from the next subframe already arrived + +### Functional split 6 usage + +The ocp cleaned main hale to be used: run ocp-softmodem instead of +lte-softmodem. + +The functionality and parameters is the same, enhanced with FS6 mode. + +The environment variable “fs6” enables the fs6 mode and decided to be cu +or du from the fs6 variable content. + +Example: + +`fs6=cu ./ocp-softmodem -O … (regular lte-softmodem paramters) +` + +Run the CU init of the split 6 eNB. + +CU and DU IP address and port are configurable in the eNB configuration +file (as X2, GTP, … interfaces). + +##5G and F1 + +Today 5G achievement is limited to physical layer. + +The available modulation is 40MHz, that require one X310 or N300 for the +gNB and a X310 or N300 for the nrUE. + +### Usage with X310 + +Linux configuration: + + +We included most of this configuration included in OAI source code. + +Remain to set the NIC (network interface card) MTU to 9000 (jumbo +frames). + +### Running 5G + +Usage with RFsimulator: + +gNB + +`sudo RFSIMULATOR=server ./nr-softmodem -O +../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf +--parallel-config PARALLEL\_SINGLE\_THREAD` + +nrUE + +`sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --numerology 1 -r 106 -C +3510000000 -d` diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 62b592673f..76be8cf250 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -183,8 +183,8 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath -Wl,${CMAKE_CU ######################### # set a flag for changes in the source code # these changes are related to hardcoded path to include .h files -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g -DMALLOC_CHECK_=3") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g -DMALLOC_CHECK_=3 -O2") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -DMALLOC_CHECK_=3") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g3 -DMALLOC_CHECK_=3 -O0") set(GIT_BRANCH "UNKNOWN") diff --git a/executables/main-fs6.c b/executables/main-fs6.c index c04290fef9..4e4d7dd54d 100644 --- a/executables/main-fs6.c +++ b/executables/main-fs6.c @@ -653,6 +653,8 @@ void phy_procedures_eNB_TX_extract(uint8_t *bufferZone, PHY_VARS_eNB *eNB, L1_rx hDL(bufferZone)->amp=AMP; } + if (num_mdci) abort(); + if (do_meas==1) stop_meas(&eNB->dlsch_common_and_dci); if (do_meas==1) start_meas(&eNB->dlsch_ue_specific); @@ -741,21 +743,21 @@ void phy_procedures_eNB_TX_extract(uint8_t *bufferZone, PHY_VARS_eNB *eNB, L1_rx return; } -void DL_du_fs6(RU_t *ru, int frame, int subframe, uint64_t TS) { +void DL_du_fs6(RU_t *ru) { RU_proc_t *ru_proc=&ru->proc; for (int i=0; inum_eNB; i++) { initBufferZone(bufferZone); - int nb_blocks=receiveSubFrame(&sockFS6, TS, bufferZone, sizeof(bufferZone) ); - L1_rxtx_proc_t *L1_proc = &ru->eNB_list[i]->proc.L1_proc_tx; - L1_proc->timestamp_tx=hUDP(bufferZone)->timestamp; - L1_proc->frame_tx=hDL(bufferZone)->frame; - L1_proc->subframe_tx=hDL(bufferZone)->subframe; + int nb_blocks=receiveSubFrame(&sockFS6, ru_proc->timestamp_tx, bufferZone, sizeof(bufferZone), CTsentCUv0 ); - if (nb_blocks>0) + if (nb_blocks > 0) { + L1_rxtx_proc_t *L1_proc = &ru->eNB_list[i]->proc.L1_proc_tx; + L1_proc->timestamp_tx=hUDP(bufferZone)->timestamp; + L1_proc->frame_tx=hDL(bufferZone)->frame; + L1_proc->subframe_tx=hDL(bufferZone)->subframe; phy_procedures_eNB_TX_process( bufferZone, nb_blocks, ru->eNB_list[i], &ru->eNB_list[i]->proc.L1_proc, 1); - else - LOG_E(PHY,"DL not received for subframe: %d\n", subframe); + } else + LOG_E(PHY,"DL not received for subframe\n"); } /* OAI data model is wrong: hardcode link ru to enb index 0*/ @@ -799,8 +801,14 @@ void UL_du_fs6(RU_t *ru, int frame, int subframe) { phy_procedures_eNB_uespec_RX_extract(bufferZone, FS6_BUF_SIZE, eNB, &proc->L1_proc ); } + if (hUDP(bufferZone)->nbBlocks==0) { + hUDP(bufferZone)->nbBlocks=1; // We have to send the signaling, even is there is no user plan data (no UE) + hUDP(bufferZone)->blockID=0; + hUDP(bufferZone)->contentBytes=sizeof(fs6_ul_t); + } + for (int i=0; inum_eNB; i++) { - sendSubFrame(&sockFS6,bufferZone, sizeof(fs6_ul_t)); + sendSubFrame(&sockFS6, bufferZone, sizeof(fs6_ul_t), CTsentDUv0); } } @@ -827,12 +835,12 @@ void DL_cu_fs6(RU_t *ru,int frame, int subframe) { hUDP(bufferZone)->contentBytes=sizeof(fs6_dl_t); } - sendSubFrame(&sockFS6, bufferZone, sizeof(fs6_dl_t) ); + sendSubFrame(&sockFS6, bufferZone, sizeof(fs6_dl_t), CTsentCUv0 ); } void UL_cu_fs6(RU_t *ru,int frame, int subframe, uint64_t TS) { initBufferZone(bufferZone); - int nb_blocks=receiveSubFrame(&sockFS6, TS, bufferZone, sizeof(bufferZone) ); + int nb_blocks=receiveSubFrame(&sockFS6, TS, bufferZone, sizeof(bufferZone), CTsentDUv0 ); if (nb_blocks ==0) { LOG_W(PHY, "CU lost a subframe\n"); @@ -878,7 +886,6 @@ void *cu_fs6(void *arg) { void *du_fs6(void *arg) { RU_t *ru = (RU_t *)arg; //RU_proc_t *proc = &ru->proc; - int64_t AbsoluteSubframe=-1; fill_rf_config(ru,ru->rf_config_file); init_frame_parms(&ru->frame_parms,1); phy_init_RU(ru); @@ -887,11 +894,9 @@ void *du_fs6(void *arg) { AssertFatal(createUDPsock(NULL, "7777", "127.0.0.1", "8888", &sockFS6),""); while(1) { - AbsoluteSubframe++; - int subframe=AbsoluteSubframe%10; - int frame=(AbsoluteSubframe/10)%1024; - UL_du_fs6(ru, frame,subframe); - DL_du_fs6(ru, frame,subframe, AbsoluteSubframe); + L1_rxtx_proc_t *proc = &ru->eNB_list[0]->proc; + UL_du_fs6(ru, proc->frame_rx,proc->subframe_rx); + DL_du_fs6(ru); } return NULL; diff --git a/executables/main-ocp.c b/executables/main-ocp.c index 3e9e7bdf64..92546eb018 100644 --- a/executables/main-ocp.c +++ b/executables/main-ocp.c @@ -582,13 +582,11 @@ static inline int rxtx(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc, char *thread_name return(0); } -void eNB_top(PHY_VARS_eNB *eNB, int frame_rx, int subframe_rx, char *string,RU_t *ru) { +void eNB_top(PHY_VARS_eNB *eNB, int dummy1, int dummy2, char *string,RU_t *ru) { L1_proc_t *proc = &eNB->proc; L1_rxtx_proc_t *L1_proc = &proc->L1_proc; LTE_DL_FRAME_PARMS *fp = &ru->frame_parms; RU_proc_t *ru_proc=&ru->proc; - proc->frame_rx = frame_rx; - proc->subframe_rx = subframe_rx; if (!oai_exit) { L1_proc->timestamp_tx = ru_proc->timestamp_rx + (sf_ahead*fp->samples_per_tti); @@ -650,16 +648,6 @@ void rx_rf(RU_t *ru,int *frame,int *subframe) { proc->subframe_tx = (proc->subframe_rx+sf_ahead)%10; proc->frame_tx = (proc->subframe_rx>(9-sf_ahead)) ? (proc->frame_rx+1)&1023 : proc->frame_rx; - if (proc->first_rx == 0) { - AssertFatal( proc->subframe_rx == *subframe && proc->frame_rx == *frame, - "Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->subframe_rx %d, subframe %d) (proc->frame_rx %d frame %d)\n", - (long long unsigned int)proc->timestamp_rx,proc->subframe_rx,*subframe, proc->frame_rx,*frame); - } else { - proc->first_rx = 0; - *frame = proc->frame_rx; - *subframe = proc->subframe_rx; - } - if (rxs != fp->samples_per_tti) { #if defined(USRP_REC_PLAY) exit_fun("Exiting IQ record/playback"); diff --git a/executables/split_headers.h b/executables/split_headers.h index 0b9104fc20..9674541628 100644 --- a/executables/split_headers.h +++ b/executables/split_headers.h @@ -18,6 +18,9 @@ typedef struct { int sockHandler; } UDPsock_t; +#define CTsentCUv0 0xA500 +#define CTsentDUv0 0x5A00 + typedef struct commonUDP_s { uint64_t timestamp; // id of the group (subframe for LTE) uint16_t nbBlocks; // total number of blocks for this timestamp @@ -67,8 +70,8 @@ typedef struct { } fs6_dl_uespec_t; bool createUDPsock (char *sourceIP, char *sourcePort, char *destIP, char *destPort, UDPsock_t *result); -int receiveSubFrame(UDPsock_t *sock, uint64_t expectedTS, void *bufferZone, int bufferSize); -int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize); +int receiveSubFrame(UDPsock_t *sock, uint64_t expectedTS, void *bufferZone, int bufferSize, uint16_t contentType); +int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize, uint16_t contentType); #define initBufferZone(xBuf) \ uint8_t xBuf[FS6_BUF_SIZE];\ @@ -77,12 +80,12 @@ int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize); #define hUDP(xBuf) ((commonUDP_t *)xBuf) #define hDL(xBuf) (((fs6_dl_t*)((commonUDP_t *)xBuf)+1)) -inline size_t alignedSize(uint8_t *ptr) { +static inline size_t alignedSize(uint8_t *ptr) { commonUDP_t *header=(commonUDP_t *) ptr; return ((header->contentBytes+sizeof(commonUDP_t)+blockAlign-1)/blockAlign)*blockAlign; } -inline void *commonUDPdata(uint8_t *ptr) { +static inline void *commonUDPdata(uint8_t *ptr) { return (void *) (((commonUDP_t *)ptr)+1); } diff --git a/executables/transport_split.c b/executables/transport_split.c index 6b13036d11..490f1259a3 100644 --- a/executables/transport_split.c +++ b/executables/transport_split.c @@ -56,7 +56,8 @@ bool createUDPsock (char *sourceIP, char *sourcePort, char *destIP, char *destPo int enable=1; AssertFatal(setsockopt(result->sockHandler, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))==0,""); - struct timeval tv= {0,UDP_TIMEOUT}; + //struct timeval tv= {0,UDP_TIMEOUT}; + struct timeval tv= {2,UDP_TIMEOUT}; //debug: wait 2 seconds for human understanding AssertFatal(setsockopt(result->sockHandler, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) ==0,""); // Make a send/recv buffer larger than a a couple of subframe // so the kernel will store for us in and out paquets @@ -69,9 +70,9 @@ bool createUDPsock (char *sourceIP, char *sourcePort, char *destIP, char *destPo // sock: udp socket // expectedTS: the expected timestamp, 0 if unknown // bufferZone: a reception area of bufferSize -int receiveSubFrame(UDPsock_t *sock, uint64_t expectedTS, void *bufferZone, int bufferSize) { +int receiveSubFrame(UDPsock_t *sock, uint64_t expectedTS, void *bufferZone, int bufferSize, uint16_t contentType) { int rcved=0; - commonUDP_t *tmp=NULL; + commonUDP_t *bufOrigin=(commonUDP_t *)bufferZone; do { //read all subframe data from the control unit @@ -85,22 +86,37 @@ int receiveSubFrame(UDPsock_t *sock, uint64_t expectedTS, void *bufferZone, int return -1; } } else { - tmp=(commonUDP_t *)bufferZone; + if (hUDP(bufferZone)->contentType != contentType) + abort(); - if ( expectedTS && tmp->timestamp != expectedTS) { - LOG_W(HW,"Received a paquet in mixed subframes, dropping it\n"); + if ( hUDP(bufferZone)->timestamp != expectedTS) { + if ( hUDP(bufferZone)->timestamp < expectedTS) { + LOG_W(HW,"Received a paquet from past subframes, dropping it\n"); + } else { + LOG_W(HW,"Received a paquet in future subframes\n"); + + if ( rcved == 0 ) { + LOG_W(HW,"First paquet in the sub-frame, keeping it\n"); + rcved++; + bufferZone+=ret; + expectedTS=hUDP(bufferZone)->timestamp; + } + } } else { rcved++; bufferZone+=ret; } } - } while ( !rcved || rcved < tmp->nbBlocks ); + } while ( !rcved || rcved < hUDP(bufferZone)->nbBlocks ); + LOG_D(HW,"Received: nb_blocks: %d, TS: %lu, frame: %d, subframe: %d\n", + rcved, bufOrigin->timestamp, *(((int *)bufOrigin)+1), *(((int *)bufOrigin)+2)); return rcved; } -int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize) { +int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize, uint16_t contentType) { commonUDP_t *UDPheader=(commonUDP_t *)bufferZone ; + UDPheader->contentType=contentType; int nbBlocks=UDPheader->nbBlocks; int blockId=0; @@ -131,5 +147,7 @@ int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize) { nbBlocks--; } while (nbBlocks); + LOG_D(HW,"Sent: TS: %lu, frame: %d, subframe: %d\n", + UDPheader->timestamp, *(((int *)UDPheader)+1), *(((int *)UDPheader)+2)); return 0; } diff --git a/openair1/SCHED/phy_procedures_lte_eNb.c b/openair1/SCHED/phy_procedures_lte_eNb.c index 261e092737..17d3001205 100644 --- a/openair1/SCHED/phy_procedures_lte_eNb.c +++ b/openair1/SCHED/phy_procedures_lte_eNb.c @@ -409,10 +409,10 @@ void phy_procedures_eNB_TX(PHY_VARS_eNB *eNB, ul_frame = pdcch_alloc2ul_frame (fp, frame, subframe); // clear previous allocation information for all UEs - for (i = 0; i < NUMBER_OF_UE_MAX; i++) { + //for (i = 0; i < NUMBER_OF_UE_MAX; i++) { //if (eNB->dlsch[i][0]) //eNB->dlsch[i][0]->subframe_tx[subframe] = 0; - } + //} /* TODO: check the following test - in the meantime it is put back as it was before */ //if ((ul_subframe < 10)&&