1 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
2 .. http://creativecommons.org/licenses/by/4.0
14 This document provides information required to work on O-DU High code-base.
19 O-DU High uses C languages. The coding guidelines followed are:
21 a. A new file should have License Header and Footer with exception of auto-generated files like files generated by
22 ASN tool. Refer to the diagram below for License header.
23 b. Every block must be indented by 3 spaces.
24 c. Any header file must be included only in .c file, not in other header files.
25 d. The line width should not exceed more than 120 characters.
27 .. figure:: LicHeader.jpg
29 :alt: Figure 17 License Header and Footer
31 Figure 17 : License Header and Footer
36 Refer to O-DU High code-base at: https://gerrit.o-ran-sc.org/r/gitweb?p=o-du/l2.git;a=tree
41 Below section references coding specifics of O-DU High components.
49 In O-DU High, multiple threads are created using below macro
51 ODU_CREATE_TASK (priority, stskId)
53 a. Creates a thread by declaring a thread id
56 - priority - Priority of the task
59 Setting a core affinity:
60 ++++++++++++++++++++++++
62 ODU_SET_THREAD_AFFINITY (tskId, mode, coreId, tskAssociatedTskId)
64 a. Sets the processor/core affinity for a thread based on the mode supplied by the caller.
68 - mode - mode according to which the affinity is set
69 - coreId - coreId to which the affinity has to be set
70 - tskAssociatedTskId - thread Id of the associated layer
72 c. Returns ROK on success and RFAILED on failure
75 +++++++++++++++++++++++
77 All logical entities in O-DU High must be registered into the database.
79 ODU_REG_TTSK (ent, inst, ttype, prior, initTsk, actvTsk)
83 - ent - Id of the entity to activate. Example: ENTDUAPP, ENTSCTP, ENTEGTP etc
84 - Inst - Instance of the entity to activate. It distinguishes between multiple instances of the same entity on a
85 given processor. Example: RLC_UL_INST (Instance id 0) and RLC_DL_INST (Instance id 1) belong to the same entity id, ENTRLC.
86 - ttype - Type of entity
87 - prior - Priority, ranges from 0(Highest) to 3(Lowest).
88 - initTsk - Initialization function(xxActvInit) of the entity being registered gets invoked. Example: duActvInit initializes DU APP
89 - actvTsk - This function(xxActvTsk) is responsible to receive any incoming message to that entity. Example: duActvTsk is triggerred when a message comes to DU APP
91 Attaching Entity to Thread:
92 +++++++++++++++++++++++++++
94 Every entity must be attached to a thread to schedule its activation based on priority and incoming events. Any number
95 of entities can be attached to a system task.
97 ODU_ATTACH_TTSK (ent, inst, stskId)
101 - ent - Entity Id of the task
102 - inst - Instance Id of the task
103 - stskId - Thread Id to use
111 Memory is divided into multiple regions(identified by region id) and each region is divided into multiple pools(identified by pool id).
112 The configurations are present in mt_ss.h and mt_ss.c at <rsys_directory>/l2/src/mt.
113 Currently, the number of regions configured are 6 and each region has 5 pools.
115 Region and pool used by each layer is identified by following macros:
117 - MAC - MAC_MEM_REGION and MAC_POOL
118 - SCH - SCH_MEM_REGION and SCH_POOL
119 - RLC UL - RLC_MEM_REGION_UL and RLC_POOL
120 - RLC_DL - RLC_MEM_REGION_DL and RLC_POOL
121 - DU APP - DU_APP_MEM_REGION and DU_POOL
126 Macros are defined at each layer for static memory allocation/deallocation from that layer's region and pool.
128 XX_ALLOC(bufPtr, size)
130 a. Allocates static buffer
133 - bufPtr - pointer to store address of the memory allocated
134 - size - size of memory to be allocated
138 - If allocation is sucessful, butPtr stores memory address
139 - If allocation fails, bufPtr is NULL.
141 XX_FREE(bufPtr, size)
143 a. Frees static buffer
146 - bufPtr - pointer to memory to be freed
147 - size - size of memory to be freed
149 Here, XX stands for various ODU-High entity i.e.
151 - MAC - MAC_ALLOC & MAC_FREE
152 - SCH - SCH_ALLOC & SCH_FREE
153 - RLC - RLC_ALLOC & RLC_FREE
154 - DU APP - DU_ALLOC & DU_FREE
159 One of the methods of communication between layers is through sharabale memory.
160 The sender will allocate sharable buffer from its own region and pool.
161 This memory will be freed by receiving layer and returned back to sender's region and pool.
163 XX_ALLOC_SHRABL_BUF(bufPtr, size)
165 a. Allocates sharable buffer
168 - bufPtr - pointer to store address of the memory allocated
169 - size - size of memory to be allocated
173 - If allocation is sucessful, butPtr stores memory address
174 - If allocation fails, bufPtr is NULL.
176 XX_FREE_SHRABL_BUF(region, pool, bufPtr, size)
178 a. Frees sharabale buffer
181 - region - region where this buffer is allocated from
182 - pool - pool where this buffer is allocated from
183 - bufPtr - pointer to memory to be freed
184 - size - size of memory to be freed
186 Here, XX stands for various ODU-High entities i.e.
188 - MAC - MAC_ALLOC_SHRABL_BUF & MAC_FREE_SHRABL_BUF
189 - SCH - Since scheduler communicates only with MAC and is tightly coupled, sharable buffers are not needed.
190 - RLC - RLC_ALLOC_SHRABL_BUF & RLC_FREE_SHRABL_BUF
191 - DU APP - DU_ALLOC_SHRABL_BUF & DU_FREE_SHRABL_BUF
196 A message is an ordered sequence of bytes. It stores both the control information and the data being communicated.
197 Message buffers are allocated from dynamic memory.
199 ODU_GET_MSG_BUF(region, pool, mBuf)
201 a. Allocates memory for message buffer
204 - region - region of sending layer
205 - pool - pool of sending layer
206 - mBuf - pointer to message buffer
208 ODU_PUT_MSG_BUF(mBuf)
210 a. Frees memory for message
213 - mBuf - message pointer
218 WLS memory is allocated for message exchanges between O-DU High and O-DU Low.
220 LWR_MAC_ALLOC(ptr, size)
222 a. Allocates WLS memory block
225 - ptr - pointer to store address of the memory allocated
226 - size - size of memory to be allocated
230 - If allocation is sucessful, ptr stores memory address
231 - If allocation fails, ptr is NULL.
233 LWR_MAC_FREE(ptr, size)
238 - bufPtr - pointer to memory to be freed
239 - size - size of memory to be freed
241 Intra O-DU High Communication
242 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
244 O-DU high entities communicate with each other through one of the following:
246 Types of Communication
247 ++++++++++++++++++++++
252 Interface APIs invoked from one entity translate into direct function calls into the destination entity.
253 Control returns to the calling entity after the called entity has completed processing the called function.
255 Macro to select this communication mode : ODU_SELECTOR_TC
260 Interface API invoked from one entity is packed into a message and then sent to destination entity through system services.
261 Control returns to the caller immediately after the message is posted, before the destination has seen or processed it.
262 There are two serialization methods supported:
266 - The interface data is packed into the message. Receiver will unpack this, parameter by parameter.
267 - Macro to select this communication mode : ODU_SELECTOR_LC
269 b. Pack/Unpack pointer
271 - The pointer to data is packed and sent. Receiver will unpack the pointer and directly access data at this address.
272 - Macro to select this communication mode : ODU_SELECTOR_LWLC
274 Below figure depicts the mode of communication between various entities registered in O-DU High.
277 - TC stands for Direct API call
278 - LC stands for Serialization by packing/unpacking of data
279 - LWLC stands for Serialization by packing/unpacking of pointers
281 .. figure:: ModeofCommunication.jpg
283 :alt: Figure 18 Mode of communication between O-DU High entities
285 Figure 18: Mode of communication between O-DU High entities
287 Steps of Communication
288 ++++++++++++++++++++++
290 1. Fill Post Structure
292 Information needed by system services to route API to the destination layer is stored in post structure.
296 | ProcId dstProcId; /\* destination processor ID \*/
297 | ProcId srcProcId; /\* source processor ID \*/
298 | Ent dstEnt; /\* destination entity \*/
299 | Inst dstInst; /\* destination instance \*/
300 | Ent srcEnt; /\* source entity \*/
301 | Inst srcInst; /\* source instance \*/
302 | Prior prior; /\* priority \*/
303 | Route route; /\* route \*/
304 | Event event; /\* event \*/
305 | Region region; /\* region \*/
306 | Pool pool; /\* pool \*/
307 | Selector selector; /\* selector \*/
308 | uint16_t spare1; /\* spare for alignment \*/
311 2. Pack API into message
313 At sender, API is packed i.e. the data is stored into a message in ordered sequence of bytes.
314 At receiver, the data is unpacked from the message and its corresponding handler is invoked.
316 a. If pst->selector is LC, each parameter is packed/unpacked one by one using one of the below.
318 - oduPackUInt8(val, mBuf) - Packs 8-bits value(val) into message(mBuf)
319 - oduUnpakcUInt8(val, mBuf) - Unpacks 8-bits from message(mBuf) and stores in val
320 - oduPackUInt16(val, mBuf) - Packs 16-bits value(val) into message(mBuf)
321 - oduUnpakcUInt16(val, mBuf) - Unpacks 16-bits from message(mBuf) and stores in val
322 - oduPackUInt32(val, mBuf) - Packs 32-bits value(val) into message(mBuf)
323 - oduUnpakcUInt32(val, mBuf) - Unpacks 16-bits from message(mBuf) and stores in val
325 The sequence in which the parameters are unpacked must be reverse of the packing sequence.
327 b. If pst->selector is LWLC, pointer to the interface structure is packed/unpacked.
329 - oduPackPointer(ptr, mBuf) - Packs pointer value(ptr) into message(mBuf)
330 - oduUnpackPointer(ptr, mBuf) - Unpacks pointer value from message(mBuf) and stores in ptr
334 Once the post information is filled and API is packed into a message, it is posted to destination using:
336 ODU_POST_TASK(pst, mBuf)
340 - pst - post structure mentioned above
343 Below figure summarized the above steps of intra O-DU High communication
345 .. figure:: StepsOfCommunication.jpg
347 :alt: Figure 19 Communication between entities
349 Figure 19: Steps of Communication between O-DU High entities
352 Communication with Intel O-DU Low
353 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
355 Intel O-DU Low communicates with O-DU High over WLS interface. Hence, Intel's "wls_lib.h" library is required for using
356 the following APIs for communication.
360 *void\* WLS_Open(const char \*ifacename, unsigned int mode, uint64_t \*nWlsMacMemorySize, uint64_t \*nWlsPhyMemorySize)*
364 - Opens the WLS interface and registers as instance in the kernel space driver.
365 - Control section of shared memory is mapped to application memory.
369 - ifacename - pointer to string with device driver name (/dev/wls)
370 - mode - mode of operation (Master or Slave). Here, O-DU High acts as MASTER.
371 - nWlsMacMemorySize - returns the value of WLS MAC memory Size as O-DU High acts as MASTER
372 - nWlsPhyMemorySize - returns the value of WLS PHY memory Size as O-DU High acts as MASTER
374 c. Returns pointer handle to WLS interface for future use by WLS functions
378 *int WLS_Ready(void \*h)*
382 - Checks the state of remote peer of WLS interface
384 b. Inputs - handle of WLS interface
385 c. Returns 0 if peer is available i.e. one to one connection is established
389 *int WLS_Close(void \*h)*
393 - Closes the WLS interface and de-registers as instance in the kernel space driver
394 - Control section of shared memory is unmapped form user space application
396 b. Input - handle of WLS interface to be closed
397 c. Returns 0 if operation is successful
401 *void\* WLS_Alloc(void\* h, unsigned int size)*
405 - Allocates memory block for data exchange shared memory. Memory block is backed by huge pages.
406 - Memory is allocated only once for L2, and divided into various regions.
410 - h - handle of WLS interface
411 - size - size of memory block to allocate
415 - Pointer to allocated memory block
416 - NULL on memory allocation failure
420 *int WLS_Free(void\* h, void\* pMsg)*
424 - Frees memory block for data exchanged on shared memory.
428 - h - handle of WLS interface
429 - pMsg - pointer to WLS memory
431 c. Returns 0 if operation is sucessful
435 *int WLS_Put(void\* h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short
440 - Puts memory block (or group of blocks) allocated from WLS memory into the interface to transfer to remote peer
444 - h - handle of WLS interface
445 - pMsg - pointer to memory block (physical address) with data to be transfered to remote peer
446 - MsgSize - size of memory block to send (should be less than 2 MB)
447 - MsgTypeID - application specific identifier of message type
448 - Flags - Scatter/Gather flag if memory block has multiple chunks
450 c. Returns 0 if operation is successful
454 *int WLS_Check(void\* h)*
458 - Checks if there are memory blocks with data from remote peer
460 b. Input - handle of WLS interface
461 c. Returns number of blocks available for "get" operation
465 *int WLS_Wait(void\* h)*
469 - Waits for new memory block from remote peer
472 b. Input - the handle of WLS interface
473 c. Returns number of blocks available for "get" operation
477 *unsigned long long WLS_Get(void\* h, unsigned int \*MsgSize, unsigned short \*MsgTypeID, unsigned short \*Flags)*
481 - Gets memory block from interface received from remote peer.
482 - Non-blocking operation
486 - h - handle of WLS interface
487 - MsgSize - pointer to set size of memory block
488 - MsgTypeID - pointer to application specific identifier of message type
489 - Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
493 - Pointer to memory block (physical address) with data received from remote peer
494 - NULL if error or no blocks available
498 *unsigned long long WLS_WGet(void\* h, unsigned int \*MsgSize, unsigned short \*MsgTypeID, unsigned short \*Flags)*
502 - Gets memory block from interface received from remote peer
503 - It is a blocking operation and waits for next memory block from remote peer
507 - h - handle of WLS interface
508 - MsgSize - pointer to set size of memory block
509 - MsgTypeID - pointer to application specific identifier of message type
510 - Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
514 - Pointer to memory block (physical address) with data received from remote peer
515 - NULL if error or no blocks available
519 *int WLS_WakeUp(void\* h)*
523 - Performs "wakeup" notification to remote peer to unblock "wait" operations pending
525 b. Input - handle of WLS interface
526 c. Returns 0 if operation is successful
530 *unsigned long long WLS_VA2PA(void\* h, void\* pMsg)*
534 - Converts virtual address (VA) to physical address (PA)
538 - h - handle of WLS interface
539 - pMsg - virtual address of WLS memory block
543 - Physical address of WLS memory block
548 *void\* WLS_PA2VA(void\* h, unsigned long long pMsg)*
552 - Converts physical address (PA) to virtual address (VA)
556 - h - handle of WLS interface
557 - pMsg - physical address of WLS memory block
561 - Virtual address of WLS memory block
564 14. **WLS_EnqueueBlock**
566 *int WLS_EnqueueBlock(void\* h, unsigned long long pMsg)*
570 - Used by the Master to provide memory blocks to slave for next slave-to-master data transfer
574 - h - handle of WLS interface
575 - pMsg - physical address of WLS memory block
577 c. Returns 0 if opertaion is successful
579 15. **WLS_DequeueBlock**
581 *unsigned long long WLS_DequeueBlock(void\* h)*
585 - Used by the Master and Slave to get block from master-to-slave queue of available memory blocks
587 b. Input - handle of WLS interface
590 - Physical address of WLS memory block
593 16. **WLS_NumBlocks**
595 *int WLS_NumBlocks(void\* h)*
599 - Returns number of current available block provided by the Master for new transfer of data from slave
601 b. Input - handle of WLS interface
602 c. Returns number of available blocks in slave to master queue
604 Scheduler Framework with plug-in support
605 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
607 5G NR SCH module is encapsulated within 5G NR MAC of ODU-High. Any communication to/from SCH will happen only through MAC.
608 The scheduler framework in ODU-High provides support to plug-in multiple scheduling algorithms easily.
613 .. figure:: 5G_NR_SCH_Design.PNG
615 :alt: 5G NR Scheduler Framework Design
617 Figure 20: 5G NR Scheduler Framework Design
619 - The code for scheduler has been divided into 2 parts i.e. the common APIs and scheduler-specific APIs.
620 - Any code (structure/API) which is specific to a scheduling algorithm must be within scheduler-specific files such as sch_rr.c and sch_rr.h for round-roubin scheduler.
621 - Function pointers are used to identify and call APIs belonging to the scheduling algorithm in use at any given point in time.
622 - All supported scheduling algorithm are listed in SchType enum in sch.h file.
623 - All function pointers are declared in SchAllApis structure in sch.h
624 - For each scheduling algorithm, function pointers must be initialised to scheduler-specific APIs during scheduler initialisation.
629 - In any call flow, a common API calls the scheduler-specific API using function pointer and its output is returned back to the common API, which will be further processed and communicated to MAC.
631 .. figure:: Multi_Scheduling_Algorithm_Call_Flow.PNG
633 :alt: Call flow example of Multi-Scheduling Algorithm framework
635 Figure 21: Example of a call flow in multi-scheduling algorithm framework
637 Additional Utility Functions
638 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
640 1. ODU_START_TASK(startTime, taskId)
642 a. Gives current time through input parameter
645 - startTime - stores current time to be returned
646 - taskId - task id of calling entity
648 2. ODU_STOP_TASK(startTime, taskId)
650 a. Calculates difference of start time and current time.
653 - startTime - start time of this task
654 - taskId - taskId of calling entity
656 3. ODU_SET_PROC_ID(procId)
658 a. Processors are identified by processor identifiers (ProcId) that are globally unique.
659 It sets the procId for the local processor. In O-DU High, procId is 0 (DU_PROC)
662 - procId - process id to be set
666 a. Finds and returns the local processor id on which the calling task is running
671 5. ODU_CAT_MSG(mbuf1, mbuf2, order)
673 a. Concatenates the given two message.
676 - mbuf1 - pointer to message buffer 1
677 - mbuf2 - pointer to message buffer 2
678 - order - order in which the messages are concatenated
680 6. ODU_GET_MSG_LEN(mBuf, lngPtr)
682 a. Determines length of the data contents of a message
685 - mBuf - pointer to the message buffer
686 - lngPtr - pointer to store length value
690 a. Gracefully exits the process
695 8. ODU_PRINT_MSG(mBuf, src, dst)
697 a. Prints information about message buffer.
700 - mBuf - pointer to the message buffer
702 - dest - destination Id
704 9. ODU_REM_PRE_MSG(dataPtr, mBuf)
706 a. Removes one byte of data from the beginning of a message
709 - dataPtr - pointer to the location where one byte of data is placed
710 - mBuf - pointer to the message buffer
712 10. ODU_REM_PRE_MSG_MULT(dst, cnt, mBuf)
714 a. Removes the specified number of bytes of data from the beginning of a message
717 - dst - pointer to the location where the data bytes are placed.
718 - cnt - number of bytes to be removed from the message.
719 - mBuf- pointer to the message.
721 11. ODU_REG_TMR_MT(ent, inst, period, func)
723 a. Registers timer function of an entity with system services
726 - ent - entity ID of task registering the timer.
727 - inst - instance of task registering the timer.
728 - period - period in system ticks between system service sccessive scheduling
729 of the timer function in the entity
730 - func - timer function.
732 12. ODU_SEGMENT_MSG(mBuf1, idx, mBuf2)
734 a. Segments a message into two messages at the specified index.
737 - mBuf1 - Message 1, original message to be segmented
738 - idx - index in message 1 from which message 2 is created.
739 - mBuf2 - pointer to message buffer 2 (new message).
741 13. ODU_ADD_PRE_MSG_MULT(src, cnt, dst)
743 a. Copies consecutive bytes of data to the beginning of a message
746 - src - source buffer
747 - cnt - number of bytes
748 - dst - destination message
750 14. ODU_ADD_PRE_MSG_MULT_IN_ORDER(src, cnt, dst)
752 a. Copies consecutive bytes of data to the beginning of a message and keeps the bytes order preserved
755 - src - source buffer
756 - cnt - number of bytes
757 - dst - destination message
759 15. ODU_ADD_POST_MSG_MULT(src, cnt, dst)
761 a. Copies consecutive bytes of data to the end of a message
764 - src - source buffer
765 - cnt - number of bytes
766 - dst - destination message
768 16. ODU_COPY_MSG_TO_FIX_BUF(src, srcIdx, cnt, dst, ccnt)
770 a. Copies data from a message buffer into a fixed buffer
773 - src - source message
774 - srcIdx - start index of source buffer to be copied
775 - cnt - number of bytes to be copied
776 - dst - destination buffer
777 - ccnt - number of bytes copied
779 17. ODU_COPY_FIX_BUF_TO_MSG(src, dst, dstIdx, cnt, ccnt)
781 a. Copies data from a fixed buffer to a message buffer
784 - src - source buffer
785 - dst - destination message
786 - dstIdx - index in destination message to starting copying bytes from
787 - cnt - number of bytes to be copied
788 - ccnt - number of bytes copied
796 O1 uses GNU C++ language.
798 ODU - O1 Communication
799 ^^^^^^^^^^^^^^^^^^^^^^
801 O1 module runs as a thread in O-DU High.
803 Alarm communication between the threads happen on a Unix socket.
805 O-DU High sends alarm messages in the following structure using Alarm Interface APIs.
811 | MsgHeader msgHeader; /\* Alarm action raise/clear \*/
812 | EventType eventType; /\* Alarm event type \*/
813 | char objectClassObjectInstance[OBJ_INST_SIZE]; /\* Name of object that raise/clear an alarm \*/
814 | char alarmId[ALRM_ID_SIZE]; /\* Alarm Id \*/
815 | char alarmRaiseTime[DATE_TIME_SIZE]; /\* Time when alarm is raised \*/
816 | char alarmChangeTime[DATE_TIME_SIZE]; /\* Time when alarm is updated \*/
817 | char alarmClearTime[DATE_TIME_SIZE]; /\* Time when alarm is cleared \*/
818 | char probableCause[TEXT_SIZE]; /\* Probable cause of alarm \*/
819 | SeverityLevel perceivedSeverity; /\* Severity level of alarm \*/
820 | char rootCauseIndicator[TEXT_SIZE]; /\* Root cause of alarm \*/
821 | char additionalText[TEXT_SIZE]; /\* Additional text describing alarm \*/
822 | char additionalInfo[TEXT_SIZE]; /\* Any additional information \*/
823 | char specificProblem[TEXT_SIZE]; /\* Any specific problem related to alarm \*/
827 O1 - Netconf Communication
828 ^^^^^^^^^^^^^^^^^^^^^^^^^^
830 O1 communicates with the Netconf server using sysrepo and libyang APIs