IOC control of EPICS RTEMS Initialize

EPICS RTEMS Initialization

The EPICS RTEMS Initialization effort provides a new way to initialize EPICS for RTEMS. The works is under review and can be found in the EPICS base PR #905.

Get the branch from my repo and add to your EPICS base local site configuration:

RTEMS_INIT = new

EPICS code or RTEMS code?

A key goal of this work is to move BSP related code from EPICS to the BSPs in RTEMS and to provide an API in RTEMS the RTEMS project is prepared to support.

RTEMS is a static linked executable that runs in a single address and if you have a header file with a call signature you can call any function. RTEMS provides a number of headers that form internal RTEMS APIs and EPCIS should not depend on them. BSP code is hard spread across projects as RTEMS does change internal interfaces BSPs depend on. The best approach is to add BSP code to RTEMS and to use a driver interface to get access.

Network Initialization

Network initialization has the following steps:

  1. Declare the network configuration
  2. Create the LibBSD /etc/rc.conf and optionally the /etc/resolv.conf files
  3. Initialize LibBSD
  4. Run /etc/rc.conf
  5. Wait for a valid network interface

Network Configuration

The network is configured using environment variables. Environment variables:

  1. Are supported via the standards base calls getenv() and setenv()
  2. Can be easily checked using the RTEMS shell command getenv -a
  3. Simple means to export from a BSP any boot loader or boot monitor configurations in a standard way without a complex driver or set of calls.

Network Environment Variables

Environment Variable Description
RTEMS_NET_HOSTNAME Name of this host
RTEMS_NET_IFACE_1 Network interface 1 device label
RTEMS_NET_IFACE_2 Network interface 2 device label
RTEMS_NET_IFACE_3 Network interface 3 device label
RTEMS_NET_IFACE_4 Network interface 4 device label
RTEMS_NET_IF_1_MAC Network interface 1 MAC
RTEMS_NET_IF_2_MAC Network interface 2 MAC
RTEMS_NET_IF_3_MAC Network interface 3 MAC
RTEMS_NET_IF_4_MAC Network interface 4 MAC
RTEMS_NET_IF_1_OPTS Network interface 1 options
RTEMS_NET_IF_2_OPTS Network interface 2 options
RTEMS_NET_IF_3_OPTS Network interface 3 options
RTEMS_NET_IF_4_OPTS Network interface 4 options
RTEMS_NET_IF_1_IP_ADDR Network interface 1 static IP address
RTEMS_NET_IF_2_IP_ADDR Network interface 2 static IP address
RTEMS_NET_IF_3_IP_ADDR Network interface 3 static IP address
RTEMS_NET_IF_4_IP_ADDR Network interface 4 static IP address
RTEMS_NET_IF_1_NETMASK Network interface 1 static IP subnet mask
RTEMS_NET_IF_2_NETMASK Network interface 2 static IP subnet mask
RTEMS_NET_IF_3_NETMASK Network interface 3 static IP subnet mask
RTEMS_NET_IF_4_NETMASK Network interface 4 static IP subnet mask
RTEMS_NET_SERVER_IP Network Server IP address
RTEMS_NET_GATEWAY_IP Network Gateway IP address
RTEMS_NET_NTP_IP Network Time Protocol IP address
RTEMS_NET_DNS_IP Network Domain Name Server IP address
RTEMS_NET_DOMAIN_NAME Network Domain Name
RTEMS_NET_BOOT_FILE Network Download Boot File
RTEMS_BOOT_CMD_LINE Boot Command Line
RTEMS_BOOT_SCRIPT Boot script
RTEMS_NFS_MOUNT_PATH Network File System mount path
TZ Timezone
  1. Up to 4 interfaces can be configured and supported
  2. Interface specific options such as hardware check sums can be supported using the RTEMS_NET_IS_1_OPT variables. The options are passed to the LibBSD ifconfig command so you can experiment with the options in an RTEMS shell first
  3. DHCP is automatically support if no static data is provided

EPICS VME PowerPC BSP Support

This new initialization moves BSP specific probes and set up from EPICS base to the related RTEMS BSPs. If a BSP you use is not supported please ask on Discord about adding the support. The Beatnik and MVME2700 BSPs have been moved.

The BSPs read GEV data from the boot loader and populate the related network environment variables.

Custom Configurations

Some boot loaders and boards do not have a way to export a network set up so the application may have to provide a way. The new EPICS RTEMS Initialization supports this by adding a custom handler.

Custom Initialization Handlers

Customer handlers have the following parts:

  1. A function that is called at the stage of the initialization you specify
  2. A global static object linked to your IOC automatically registers your custom handler
  3. A domain and name to help you review and audit the initialization sequence

To access the types you need add to your IOC this include:

#ifdef __rtems__ 
#include <epicsRtemsInit.h>
#endif

Note: if this include is in common IOC code guard the include with the RTEMS define. You will also need to guard any code and objects else where in the file.

Custom Handler Function

The function has the type:

using epicsRtemsInitHandler = std::function<int()>;

The handler can be function:

static int myHandler() {
  std::cout << "My handler function has been called" << std::endl;
  return 0;
}

or a lambda:

auto myHandler = []() {
  std::cout << "My handler lamdba has been called" << std::endl;
  return 0;
};

Custom Handler Registration

The handler function is registered automatically by creating a global registration object of type epicsRtemsInitRegister. Global objects are constructed before EPICS runs registering your handler when the registration object’s constructs.

Note: Make sure you directly link the compiled object (.o) file to your executable or if in a library reference a global symbol in the same source file. If you do not do one of these things the linker will ignore the object file and the global object will not be in your executable.

A handler registration for a function is:

epicsRtemsInitRegister myHandlerInit(
    "me", "init", rtemsInit_Order_early + 3, true, myHandler); 

or for a lambda is:

epicsRtemsInitRegister myHandlerInit(
    "me", "init", rtemsInit_Order_early + 3, true, myHandler); 

In this case the lamdba variable is passed in and copied for you.

You can provide a domain and a name. These are printed during initialization to help you audit and debug any initialization issues. An example is:

] Init (15,1/12,enabled): my.init
My handler function has been called
] Init (200,3/12,enabled): system.log.reset

The start order is an absolute value so you can insert at any point your handler. A few standard blocks have been defined as:

enum epicsRtemsInit_bands {
    rtemsInit_Order_early= 10,
    rtemsInit_Order_filesystems = 200,
    rtemsInit_Order_pre_net_services= 100,
    rtemsInit_Order_net = 500,
    rtemsInit_Order_net_filesystems = 600,
    rtemsInit_Order_post_net_services= 800,
    rtemsInit_Order_commands = 900,
    rtemsInit_Order_ioc = 1000,
};

A handler can be enabled or disabled by default. You can also use the environment variables RTEMS_INIT_ENABLE and RTEMS_INIT_DISABLE to dynamically change what is initialized. For example to enabled the system debugger service:

setenv("RTEMS_INIT_ENABLE", "system.debugger", 1);

You can provide a list as comma (,) separated values:

setnv("RTEMS_INIT_ENABLE", "system.debugger,my.init", 1);

A disable overrides any enables.

DHCP Hook Function

The support for the DHCP hook function was removed. This is a fragile interface that depends heavly on the DHCP implementation in RTEMS and it may change or be removed in future RTEMS releases.

If you depend on parsing the DHCP data for a specific option you can implement a custom initialize handler to do this task. The handler can then update any network environment variables and support any functionality you need.

This approach is an improvement because you can add code you need and not depend on EPICS base changes.

An example is:

#ifdef __rtems__
#include <epicsRtemsInit.h>
#include <rtems/dhcpcd.h>

static void dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env) {
    std::cout << "DHCP Hook:" << std::endl;
    while (*env != nullptr) {
        std::cout << ' ' << *env << std::endl;
        ++env;
    }
}

static rtems_dhcpcd_hook dhcpcd_hook = {
    .name = "ioc boot",
    .handler = dhcpcd_hook_handler
};

static int DHCP_hook() {
    rtems_dhcpcd_add_hook(&dhcpcd_hook);
    return 0;
}

epicsRtemsInitRegister dhcpInit(
    "test", "dhcp", rtemsInit_Order_early, true, DHCP_hook);
#endif /* __rtems__ */

The console output is:

] Init (10,1/12,enabled): bbb.dhcp
] Init (10,2/12,enabled): bbb.environ
] Init (200,3/12,enabled): system.log.reset
Startup
] Init (210,4/12,enabled): system.filesys.root
File System: root set up
  [ snip ]
iocInit
Starting iocInit
############################################################################
## EPICS R7.0.10.1-DEV
## Rev. 8ae316147cffe5c42af8-dirty
## Rev. Date Git: 2026-09-30 10:18:09 +1000
############################################################################
iocRun: All initialization complete
## Start any sequence programs
#seq(sncExample, "user=chris")

myhost > err: cpsw0: ipv4_addroute: File exists
err: cpsw0: ipv4_addroute: Invalid argument
DHCP Hook:
 interface=cpsw0
 reason=RENEW
 pid=1
 ifmetric=201
 ifwireless=0
 ifflags=4294936643
 ifmtu=1500
 interface_order=cpsw0
 if_up=true
 if_down=false
 old_ip_address=10.10.5.120
 old_subnet_cidr=24
 old_broadcast_address=10.10.5.255
 old_network_number=10.10.5.0
 old_subnet_mask=255.255.255.0
 old_routers=10.10.5.1
 old_dhcp_lease_time=6951
 old_dhcp_message_type=5
 old_dhcp_server_identifier=10.10.5.1
 new_ip_address=10.10.5.120
 new_subnet_cidr=24
 new_broadcast_address=10.10.5.255
 new_network_number=10.10.5.0
 new_subnet_mask=255.255.255.0
 new_routers=10.10.5.1
 new_dhcp_lease_time=7200
 new_dhcp_message_type=5
 new_dhcp_server_identifier=10.10.5.1

Custom Network Configuration

This example is a DHCP network set up for a Beagle Bone Black BSP. This BSP does not support any boot loader configurations so you can manage the set up in a network wide configuration using DHCP. The IOC code is:

#ifdef __rtems__
#include <epicsRtemsInit.h>

#if RTEMS_BSP == beagleboneblack
static int set_BBB_Environment() {
    setenv("RTEMS_NET_HOSTNAME", "myhost", 1);
    setenv("RTEMS_NET_IFACE_1", "cpsw0", 1);
    setenv("RTEMS_NFS_MOUNT_PATH",
           "10.10.5.4:/opt/src:epics/ntp/ntp-test/iocBoot/iocntp/st.cmd",
           1);
    return 0;
}

epicsRtemsInitRegister bbbInit(
    "bbb", "environ", rtemsInit_Order_early, true, set_BBB_Environment);
#endif /* beagleboneblack */
#endif /* __rtems__ */