Advanced documentation

Build system

Introduction

The build system is based on CMake tool which generates makefiles. Each directory to build shall include a CMakeList.txt file that gives rules “how to” and “what to ” build. Furthermore, some other cmake files provide helping support for common or generic rules. These last files are located in the tools/build_suport directory.

Build Support

toolchain-config.cmake

This file gives rules to select the toolchain to use. It relies on native_toolchain-config.cmake and cross_toolchain-config.cmake.

native_toolchain-config.cmake

Define the minimum to be able to compile for native x86 target.

cross_toolchain-config.cmake

Define the arm-none-eabi-xx as cross-compiler to be used.

config_support-config.cmake

This file provides helper macros or functions to :

  • get_cfg : this macro will include your own platform specific cmake. Based on command line argument BUILD_CFG and BUILD_CFG_DIR.

    • BUILD_CFG :

      The file name of your specific cmake (e.g. -DBUILD_CFG=Nucleo-L476.cmake).

    • BUILD_CFG_DIR :

      The path where is located your specific cmake (e.g. -DBUILD_CFG_DIR=demo/Nucleo-L476). The default path is “demo”.

    Furthermore, this macro will extract the git information and produce the “version.h” file (see git_info_config.cmake).

  • setup_config : this function generates a .h file from .in file.

    setup_config(
       NAME <file>
       SOURCE <src/path>
       DESTINATION <dest/path>
       )
    
    • <file> : the file name (without extension) given for input and output.

    • <src/path> : the source path of the “file”.in

    • <dest/path> : the destination where to write the “file”.h

install_support-config.cmake

  • setup_install : helper function to create an install target

    setup_install(
       TARGET    <target>
       NAMESPACE <namesapce>
       DEPENDS   <depends>
       )
    
    • <target> : the install target name to create

    • <namesapce> : optional namespace

    • <depends> : a list of other target on which our target depend

git_info_config.cmake

This file provides functions to extract information from git and generates a “version.h” file.

  • gitinfo : extract the git information

    gitinfo( <working directory> )
    
    • <working directory> : base directory where to find git information.

  • version_from_git : generates the “version.h” file

    version_from_git( <config_file_path> <version_file> )
    

    Example of version.h content :

    #ifndef _VERSION_H_
    #define _VERSION_H_
    
    #define GIT_RETRIEVED_STATE false
    #define GIT_HEAD_SHA1 7bddf62f904a664a8c56be7904c13a8b57543222
    #define GIT_DESCRIBE 7bddf62
    #define GIT_IS_DIRTY 1
    
    #define GIT_TAG "UNK.99.99.99" // undefined
    #define GIT_FW_VER_TYPE "UNK"
    #define GIT_FW_VER_MAJ 99
    #define GIT_FW_VER_MIN 99
    #define GIT_FW_VER_REV 99
    
    #define GIT_AUTHOR_NAME ""
    #define GIT_AUTHOR_EMAIL ""
    
    #define GIT_COMMIT_DATE_ISO8601 "2022-02-22 07:45:43 +0100"
    #define GIT_COMMIT_SUBJECT "[FEATURE] Add the possibility to auto generate default parameters"
    #define GIT_COMMIT_BODY ""
    
    #endif /* _VERSION_H_ */
    

gen_param-config.cmake

  • gen_param : generate “.c” and “.h” parameter files from xml

    gen_param(
       SOURCE <src/path>
       DESTINATION <dest/path>
       )
    
    • <src/path> : path where to find xml files. Both DefaultParams.xml and DefaultRestr.xml must be defined.

    • <dest/path> : path where the parameters files will be generated

    Command line option :

    • GENERATE_PARAM : boolean, default is OFF

Porting guide

Porting your PHY device

Take a look at the PhyFake demo device that gives a simple example.

Prepare your module

  • Create a directory for your PHY device.

  • Create the file include/phy_layer_private.h.

  • Create the files src/phy_layer.c and src/phy_layer_private.c.

  • Create the CMakeLists.txt

Then, that should give something like :

MyPhy
├── CMakeLists.txt
├── include
│   └── phy_layer_private.h
└── src
    ├── phy_layer_private.c
    └── phy_layer.c

Implement the low level driver

In phy_layer_private.h and phy_layer_private.c integrate all requirements (function, defines, typedef, …) to drive at low level your device. Its content depends strongly on the behavior of your device.

Implement the phy driver

The phy_layer.c is intended to implement the OpenWize phy interface.

Phy interface

Include the phy interafce definition from phy_itf.h. Define the interface implementation :

static const phy_if_t _phy_if = {
   .pfInit          = _my_init_,
   .pfUnInit        = _my_uninit_,
   .pfTx            = _my_TX_,
   .pfRx            = _my_RX_,
   .pfNoise         = _my_noise_,
   .pfSetSend       = _my_set_send_,
   .pfGetRecv       = _my_get_recv_,
   .pfIoctl         = _my_ioctl_
};

Implement the interface functions :

  • _my_init_ : Driver inititialization function.

    static int32_t _my_init_(phydev_t *pPhydev);
    

    This function must initialize :

    • The pPhydev internal structure.

    • The bsp bus communication (if any).

    • Your my_phy_device_t device internal structure.

  • _my_uninit_ : Driver un-inititialization function.

    static int32_t _my_uninit_(phydev_t *pPhydev)
    

    This function must uninitialize :

    • The bsp bus communication (if any).

  • _my_TX_ : Do the transmit action

    static int32_t _my_TX_(phydev_t *pPhydev, phy_chan_e eChannel, phy_mod_e eModulation);
    

    This function must :

    • Take into account the channel and modulation changes.

    • Call low level sending function.

  • _my_RX_ : Do the listen action.

    static int32_t _my_RX_(phydev_t *pPhydev, phy_chan_e eChannel, phy_mod_e eModulation);
    

    This function must :

    • Take into account the channel and modulation changes.

    • Call low level listening function.

  • _my_noise_ : Measure the noise.

    static int32_t _my_noise_(phydev_t *pPhydev, phy_chan_e eChannel, phy_mod_e eModulation);
    

    This function is optional.

  • _my_set_send_ : Transfert the frame into the device internal transmiting buffer

    static int32_t _my_set_send_(phydev_t *pPhydev, uint8_t *pBuf, uint8_t u8Len);
    

    This function must :

    • Transfert the content of pBuf into the device internal buffer.

  • _my_get_recv_ : Get the frame from the device internal receiving buffer.

    static int32_t _my_get_recv_(phydev_t *pPhydev, uint8_t *pBuf, uint8_t *u8Len);
    

    This function must :

    • Get the content the device internal buffer and copy it into the pBuf.

  • _my_ioctl_ : Get/Set the device configuration

    static int32_t _my_ioctl_(phydev_t *pPhydev, uint32_t eCtl, uint32_t args);
    

    Phy IO control is given by eCtl while args is the value to get/set. See phy_ctl_e for details information.

Setup function

This function is intended to prepare the Phy device with constant configuration.

int32_t Phy_MyPhy_setup(phydev_t *pPhydev, my_phy_device_t *pCtx)

The minimal implementation is the following :

int32_t Phy_MyPhy_setup(phydev_t *pPhydev, my_phy_device_t *pCtx)
{
   int32_t i32Ret = PHY_STATUS_ERROR;
   if (pPhydev && pCtx)
   {
      pPhydev->pIf = &_phy_if;
      pPhydev->pCxt = pCtx;
      i32Ret = PHY_STATUS_OK;
   }
    return i32Ret;
}
Interrupt handler

This handler must be called when an interrupt occurs from your device and should look like the following :

static void _my_frame_it_(void *p_CbParam, void *p_Arg)
{
   uint32_t eEvt = PHYDEV_EVT_NONE;
   phydev_t *pPhydev = (phydev_t *) p_CbParam;
   my_phy_device_t *pDevice;
   uint32_t u32IrqStatus = MY_PHY_EVT_NONE;

   if (pPhydev)
   {
      pDevice = pPhydev->pCxt;
      // ...do something with private structure, if required
      // Get irq status
      // ...from passing argument
      // u32IrqStatus = ((uint32_t)p_Arg);
      // ...or from calling low level function
      // u32IrqStatus = MyPhy_GetIrq(...);
   }

   if (u32IrqStatus == MY_PHY_EVT_RX_STARTED)
   {
      eEvt = PHYDEV_EVT_RX_STARTED;
      // ...implement some action on this event, if any
   }
   if (u32IrqStatus == MY_PHY_EVT_TX_CPLT)
   {
      eEvt = PHYDEV_EVT_TX_COMPLETE;
      // ...implement some action on this event, if any
   }
   if (u32IrqStatus == MY_PHY_EVT_RX_CPLT)
   {
      eEvt = PHYDEV_EVT_RX_COMPLETE;
      // ...implement some action on this event, if any
   }

   // event notification
   if( (eEvt != PHYDEV_EVT_NONE) && pPhydev->pfEvtCb )
   {
      // Notify the higher level
      pPhydev->pfEvtCb(pPhydev->pCbParam, eEvt);
   }
}

Porting to a new board

TBD

Compiler

TBD