Commit 88d57390 authored by laurent's avatar laurent

continue timestamp

parent ebb52e38
# 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:
<https://open-cells.com/index.php/2017/08/22/all-in-one-openairinterface-august-22nd/>
##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&gt; /dev/null
ip link delete v-eth1 2&gt; /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 (&gt;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:
<https://files.ettus.com/manual/page_usrp_x3x0_config.html>
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`
......@@ -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")
......
......@@ -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; i<ru->num_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; i<ru->num_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;
......
......@@ -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");
......
......@@ -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);
}
......
......@@ -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 ( expectedTS && tmp->timestamp != expectedTS) {
LOG_W(HW,"Received a paquet in mixed subframes, dropping it\n");
if (hUDP(bufferZone)->contentType != contentType)
abort();
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;
}
......@@ -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)&&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment