time management module
This commit introduces a time management module. See doc/time_management.md for details.
This commit is contained in:
+1
-1
@@ -298,7 +298,7 @@ Even, a future evolution could remove this global rlc layer: rlc can be only a l
|
||||
When adding a UE, external code have to call `add_rlc_srb()` and/or `add_rlc_drb()`, to remove it: `rrc_rlc_remove_ue()`
|
||||
Inside UE, channels called drd or srb can be created: ??? and deleted: rrc_rlc_config_req()
|
||||
|
||||
nr_rlc_tick() must be called periodically to manage the internal timers
|
||||
nr_rlc_ms_tick() must be called periodically to manage the internal timers
|
||||
|
||||
successful_delivery() and max_retx_reached(): in ??? trigger, the RLC sends a itti message to RRC: RLC_SDU_INDICATION (neutralized by #if 0 right now)
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
# Time management module
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Introduction
|
||||
|
||||
Several parts of the 4G/5G protocol stack keep track of time, for example
|
||||
for timeout events (in RRC, PDCP, or RLC for example).
|
||||
|
||||
The obvious simple way to deal with time is to use the computer's realtime
|
||||
clock. This works well when we run the stack in realtime, with real
|
||||
radio equipment. But in simulation the system may run faster or slower
|
||||
than realtime, so we need another way to deal with time. A good solution
|
||||
is then to base time on the current IQ data processed by the system. So time
|
||||
becomes "IQ samples" based, not realtime.
|
||||
|
||||
The system may be decomposed into various components (CU, DU, RU, various
|
||||
UEs) running either in realtime or simulation. Time may be distributed
|
||||
to all these components from only one time source for accurate execution
|
||||
of the scenario, or each component may have its own time source, either
|
||||
based on realtime or IQ samples.
|
||||
|
||||
## Overall structure
|
||||
|
||||
The time management module is made of three main parts. The first one
|
||||
is the time source. It can either be realtime or "IQ samples" based.
|
||||
In one running scenario of the system, there can be one or more time
|
||||
sources (imagine one for the gNB, and one per each UE). There can also
|
||||
be only one time source, shared between various users of the time
|
||||
manager.
|
||||
|
||||
The second part is the server. One server is attached to one time source
|
||||
and receives a tick every millisecond (real or simulated) from the time
|
||||
source. It distributes the tick to the various connected clients.
|
||||
|
||||
And finally the last part is the client, which connects to a single server
|
||||
and receives ticks from it. The client does not have a time source attached
|
||||
to it. It gets the time from the server.
|
||||
|
||||
It is possible for a program (monolithic gNB for example) to have only
|
||||
a time source, without server or client.
|
||||
|
||||
## Configuration
|
||||
|
||||
To configure the time management module, add the following section in
|
||||
the configuration file.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
#valid time sources: realtime, iq_samples
|
||||
time_source = realtime
|
||||
|
||||
#valid modes: standalone, server, client
|
||||
mode = standalone
|
||||
|
||||
#set ip/port of server (for server mode, this is the address/port to bind to;
|
||||
#for client mode, this is the address/port to connect to)
|
||||
server_ip = "127.0.0.1"
|
||||
server_port = 7374
|
||||
}
|
||||
```
|
||||
|
||||
If not set in the configuration file, some default is applied, depending
|
||||
on the program run. (See the function `time_manager_start()` for details.)
|
||||
|
||||
It is also possible to pass configuration parameters on the command line.
|
||||
For example add `--time_manager.time_source iq_samples` to the command line
|
||||
to set the time source to `iq_samples`. The same syntax is applicable to the
|
||||
other parameters.
|
||||
|
||||
## Examples
|
||||
|
||||
Here come some examples of configuration for typical use cases of OAI.
|
||||
|
||||
### Monolithic gNB realtime
|
||||
|
||||
This is a very simple example. The gNB runs in `standalone` mode with
|
||||
a `realtime` time source.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
time_source = realtime
|
||||
mode = standalone
|
||||
}
|
||||
```
|
||||
### Monolithic gNB "IQ samples time"
|
||||
|
||||
For this configuration, it's the same as the previous example, but the
|
||||
time source is now `iq_samples`.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
time_source = iq_samples
|
||||
mode = standalone
|
||||
}
|
||||
```
|
||||
### RF simulator CU/DU "IQ samples time"
|
||||
|
||||
Let's suppose the CU runs on a machine with IP address 1.2.3.4 and the DU
|
||||
runs on a machine with IP address 10.11.12.13.
|
||||
|
||||
The DU has the time source and so is the server. The CU acts as a client.
|
||||
|
||||
Here comes the configuration for the CU.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
mode = client
|
||||
server_ip = "10.11.12.13"
|
||||
server_port = 7374
|
||||
}
|
||||
```
|
||||
And here is the configuration for the DU.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
time_source = iq_samples
|
||||
mode = server
|
||||
server_ip = "10.11.12.13"
|
||||
server_port = 7374
|
||||
}
|
||||
```
|
||||
|
||||
### RF simulator CU/DU "realtime"
|
||||
|
||||
Let's suppose the CU runs on a machine with IP address 1.2.3.4 and the DU
|
||||
runs on a machine with IP address 10.11.12.13.
|
||||
|
||||
CU and DU can both run in standalone mode, with no exchange between them.
|
||||
Any one of them can also acts as a server and the other one as a client.
|
||||
|
||||
Let's suppose for this example that the CU and the DU run in client/server
|
||||
mode. Let's set the CU as the time source and so as the server. The DU acts
|
||||
as a client.
|
||||
|
||||
Here comes the configuration for the CU.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
time_source = realtime
|
||||
mode = server
|
||||
server_ip = "1.2.3.4"
|
||||
server_port = 7374
|
||||
}
|
||||
```
|
||||
And here is the configuration for the DU.
|
||||
|
||||
```
|
||||
time_management = {
|
||||
mode = client
|
||||
server_ip = "1.2.3.4"
|
||||
server_port = 7374
|
||||
}
|
||||
```
|
||||
|
||||
Note that the time management module is flexible. It's possible to
|
||||
configure more complex setups. For example the time source can be a gNB
|
||||
and several UEs can be connected as client.
|
||||
|
||||
One could also write a simple program acting as a time source and distributing
|
||||
time to everyone (one or several gNB, several UEs).
|
||||
|
||||
## Programming API
|
||||
|
||||
Here comes the internal API of the time manager module. The (simpler) OAI
|
||||
API is described afterwards.
|
||||
|
||||
### Time source
|
||||
|
||||
- opaque type: `time_source_t`
|
||||
- create a time source:
|
||||
```
|
||||
time_source_t *new_time_source(time_source_type_t type)
|
||||
```
|
||||
where `type` is one of:
|
||||
* `TIME_SOURCE_REALTIME`
|
||||
* `TIME_SOURCE_IQ_SAMPLES`
|
||||
- delete a time source:
|
||||
```
|
||||
void free_time_source(time_source_t *time_source)
|
||||
```
|
||||
- set millisecond callback:
|
||||
```
|
||||
void time_source_set_callback(time_source_t *time_source,
|
||||
void (*callback)(void *),
|
||||
void *callback_data)
|
||||
```
|
||||
where `callback` is a function that will be called for each millisecond using
|
||||
the provided `callback_data`
|
||||
- for IQ samples time management, the user of the time source must call
|
||||
this function when it processes IQ samples:
|
||||
```
|
||||
void time_source_iq_add(time_source_t *time_source,
|
||||
uint64_t iq_samples_count,
|
||||
uint64_t iq_samples_per_second)
|
||||
```
|
||||
then the time source will generate a tick for each block for IQ samples
|
||||
lasting one millisecond. (If you pass `iq_samples_count` lasting several
|
||||
milliseconds then the time source will generate several ticks in a row,
|
||||
one for each millisecond.)
|
||||
|
||||
### Server
|
||||
|
||||
- opaque tyoe: `time_server_t`
|
||||
- create a server:
|
||||
```
|
||||
time_server_t *new_time_server(const char *ip,
|
||||
int port,
|
||||
void (*callback)(void *),
|
||||
void *callback_data)
|
||||
```
|
||||
where ip/port is the IP adress and port the server will listen to
|
||||
and `callback` is a function that will be called for each millisecond using
|
||||
the provided `callback_data`
|
||||
- delete a server:
|
||||
```
|
||||
void free_time_server(time_server_t *time_server)
|
||||
```
|
||||
- attach a time source:
|
||||
```
|
||||
void time_server_attach_time_source(time_server_t *time_server,
|
||||
time_source_t *time_source)
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
- opaque type: `time_client_t`
|
||||
- create a client:
|
||||
```
|
||||
time_client_t *new_time_client(const char *server_ip,
|
||||
int server_port,
|
||||
void (*callback)(void *),
|
||||
void *callback_data)
|
||||
```
|
||||
the client will connect on given server ip/port
|
||||
and use the `callback` with `callback_data` for each tick received
|
||||
- delete a client:
|
||||
```
|
||||
void free_time_client(time_client_t *time_client)
|
||||
```
|
||||
|
||||
### Threading
|
||||
|
||||
Each component (time source, server, client) runs its own thread which will
|
||||
be the one calling the configured callback. Be careful of using proper
|
||||
synchronization techniques in your callback to have correct behavior with
|
||||
the other threads of the program.
|
||||
|
||||
## OAI API
|
||||
|
||||
OAI uses the time manager through a simplified API. All the code is contained
|
||||
in `time_manager.c`, together with some global variables (so that there is no
|
||||
need to pass objects around, to limit risks of misuse).
|
||||
|
||||
Each program (for example: gnb, ue, cu, du) calls the `time_manager_start()`
|
||||
function, passing the kind of program it is. Based on this type and the
|
||||
configuration, `time_manager_start()` initializes what is needed.
|
||||
|
||||
The function `time_manager_iq_samples()` is to be called by programs that
|
||||
read IQ samples. It is called unconditionally. It may do nothing if the
|
||||
configuration is to use realtime ticks and not IQ samples ticks.
|
||||
|
||||
When the program exits, it calls `timer_manager_finish()` which in turns stops
|
||||
the various threads created by `time_manager_start()` and releases all the
|
||||
allocated data.
|
||||
|
||||
Here comes the API.
|
||||
|
||||
- init the time manager:
|
||||
```
|
||||
void time_manager_start(time_manager_client_t client_type,
|
||||
time_manager_mode_t running_mode)
|
||||
```
|
||||
(for valid values of `client_type` and `running_mode` see in
|
||||
`time_manager.h`)
|
||||
- update IQ samples' based time manager:
|
||||
```
|
||||
void time_manager_iq_samples(uint64_t iq_samples_count,
|
||||
uint64_t iq_samples_per_second)
|
||||
```
|
||||
- terminate the time manager:
|
||||
```
|
||||
void time_manager_finish(void)
|
||||
```
|
||||
Reference in New Issue
Block a user