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