Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


No Transmission from GMAC using +TCP on SAME70

Posted by tominvert on April 12, 2017


I'm trying to port the provided SAM4E network interface to use with the SAME70, but I'm not getting any data out of the GMAC peripheral. I can see ARP requests being received, processed and responded to, but the packet does not appear on the ethernet bus. The data appears to be correctly formed when xNetworkInterfaceOutput and gmacdevwrite are called. I suspect that the issue is due to the additional prioritiy queues introduced with the SAME70 that weren't present on the SAM4E, but I'm a novice at both low-level ethernet communications and DMA data transfer so I'm not sure where to look for issues. I can provide my full NetworkInterface.c and modfied gmac.c files if it helps - I can't see how to attach files here though. I've posted my initialisation functions and send functions below. My functions don't include any Phy initialisation or communications as our MCU connects directly to an ethernet switch over RMII. I'm running FreeRTOS V9.0.0, compiling in Atmel Studio with GCC.

Any help is greatly appreciated. Cheers.

~~~ static BaseTypet prvGMACInit( void ) { /* Wait for PHY to be ready (CAT811: Max400ms). */ volatile uint32t uldelay = sysclkgetcpuhz() / 1000 / 3 * 400; while (ul_delay--) { }

uint32_t ncfgr;
gmac_options_t gmac_option;


memset( &gmac_option, '\0', sizeof( gmac_option ) );
gmac_option.uc_copy_all_frame = 0;
gmac_option.uc_no_boardcast = 0;
memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) );

gs_gmac_dev.p_hw = GMAC;
gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option );
gmac_set_mdc_clock(GMAC, sysclk_get_peripheral_hz());
gmac_set_speed(GMAC, true);
gmac_enable_full_duplex(GMAC, true);
gmac_select_mii_mode(GMAC, GMAC_PHY_RMII);


/* The GMAC driver will call a hook prvRxCallback(), which
in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */
gmac_dev_set_rx_callback( &gs_gmac_dev, GMAC_QUE_0, prvRxCallback );
gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address );



// Set Link Up
bInterfaceUp = true;
ioport_set_pin_level(PIN_LED_LINK, true);

return 1;


BaseTypet xNetworkInterfaceOutput( NetworkBufferDescriptort * const pxDescriptor, BaseTypet bReleaseAfterSend ) { if( bInterfaceUp ) { /* Not interested in a call-back after TX. */ iptraceNETWORKINTERFACETRANSMIT(); printf("debug: outgoing packet length = %lu bytesrn", pxDescriptor->xDataLength); gmacdevwrite( &gsgmacdev, GMACQUE_0, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, NULL ); }

if( bReleaseAfterSend != pdFALSE )
	vReleaseNetworkBufferAndDescriptor( pxDescriptor );
return pdTRUE;

} ~~~

~~~ static void gmacresettxmem(gmacdevicet* pdev, gmacquelistt queueidx) { Gmac *phw = pdev->phw; uint8t *ptxbuff = pdev->gmacqueuelist[queueidx].ptxbuffer; gmactxdescriptort *ptd = pdev->gmacqueuelist[queueidx].ptx_dscr;

uint32_t ul_index;
uint32_t ul_address;

/* Disable TX */
gmac_enable_transmit(p_hw, 0);

/* Set up the TX descriptors */
CIRC_CLEAR(p_dev->gmac_queue_list[queue_idx].us_tx_head, p_dev->gmac_queue_list[queue_idx].us_tx_tail);
for (ul_index = 0; ul_index < p_dev->gmac_queue_list[queue_idx].us_tx_list_size; ul_index++) 
	ul_address = (uint32_t) (&(p_tx_buff[ul_index * GMAC_TX_UNITSIZE]));
	p_td[ul_index].addr = ul_address;
	p_td[ul_index].status.val = GMAC_TXD_USED;
p_td[p_dev->gmac_queue_list[queue_idx].us_tx_list_size - 1].status.val =

/* Set transmit buffer queue */
if(queue_idx == GMAC_QUE_0) gmac_set_tx_queue(p_hw, (uint32_t) p_td);
else						gmac_set_tx_priority_queue(p_hw, (uint32_t) p_td, queue_idx);


/** * brief Disable receiver, reset registers and descriptor list. * * param pdrv Pointer to GMAC Driver instance. */ static void gmacresetrxmem(gmacdevicet* pdev, gmacquelistt queueidx) { Gmac *phw = pdev->phw; uint8t *prxbuff = pdev->gmacqueuelist[queueidx].prxbuffer; gmacrxdescriptort *pRd = pdev->gmacqueuelist[queueidx].prx_dscr;

uint32_t ul_index;
uint32_t ul_address;

/* Disable RX */
gmac_enable_receive(p_hw, 0);

/* Set up the RX descriptors */
p_dev->gmac_queue_list[queue_idx].us_rx_idx = 0;
for (ul_index = 0; ul_index < p_dev->gmac_queue_list[queue_idx].us_rx_list_size; ul_index++) {
	ul_address = (uint32_t) (&(p_rx_buff[ul_index * GMAC_RX_UNITSIZE]));
	pRd[ul_index].addr.val = ul_address & GMAC_RXD_ADDR_MASK;
	pRd[ul_index].status.val = 0;
pRd[p_dev->gmac_queue_list[queue_idx].us_rx_list_size - 1].addr.val |= GMAC_RXD_WRAP;

/* Set receive buffer queue */
if(queue_idx == GMAC_QUE_0) {
	gmac_set_rx_queue(p_hw, (uint32_t) pRd);
	} else {
	gmac_set_rx_priority_queue(p_hw, (uint32_t) pRd, queue_idx);


/** * brief Initialize the allocated buffer lists for GMAC driver to transfer data. * Must be invoked after gmacdevinit() but before RX/TX starts. * * note If input address is not 8-byte aligned, the address is automatically * adjusted and the list size is reduced by one. * * param pgmac Pointer to GMAC instance. * param pgmacdev Pointer to GMAC device instance. * param pdevmm Pointer to the GMAC memory management control block. * param ptxcb Pointer to allocated TX callback list. * * return GMACOK or GMACPARAM. */ static uint8t gmacinitmem(gmacdevicet* pgmacdev, gmacquelistt queueidx, gmacdevmemt* pdevmm, gmacdevtxcbt* ptxcb) { if (pdevmm->usrxsize <= 1 || pdevmm->ustxsize <= 1 || ptxcb == NULL) return GMAC_PARAM;

gmac_queue_t* p_gmac_queue = &p_gmac_dev->gmac_queue_list[queue_idx];

/* Assign RX buffers */
if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7) || ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) p_dev_mm->us_rx_size--;

p_gmac_queue->p_rx_buffer = (uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);
p_gmac_queue->p_rx_dscr =	(gmac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr & 0xFFFFFFF8);
p_gmac_queue->us_rx_list_size = p_dev_mm->us_rx_size;

/* Assign TX buffers */
if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7) || ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) p_dev_mm->us_tx_size--;

p_gmac_queue->p_tx_buffer = (uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);
p_gmac_queue->p_tx_dscr = (gmac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr & 0xFFFFFFF8);
p_gmac_queue->us_tx_list_size = p_dev_mm->us_tx_size;
p_gmac_queue->func_tx_cb_list = p_tx_cb;

/* Reset TX & RX */
gmac_reset_rx_mem(p_gmac_dev, queue_idx);
gmac_reset_tx_mem(p_gmac_dev, queue_idx);

return GMAC_OK;


static void gmacinitqueue(Gmac* pgmac, gmacdevicet* pgmacdev) { gmacdevmemt gmacdevmm;

/* Clear interrupts */
gmac_get_priority_interrupt_status(p_gmac, GMAC_QUE_2);
gmac_get_priority_interrupt_status(p_gmac, GMAC_QUE_1);

gmac_set_tx_priority_queue(p_gmac, (uint32_t)&gs_tx_desc_null, GMAC_QUE_2);
gmac_set_tx_priority_queue(p_gmac, (uint32_t)&gs_tx_desc_null, GMAC_QUE_1);
gmac_set_rx_priority_queue(p_gmac, (uint32_t)&gs_rx_desc_null, GMAC_QUE_2);
gmac_set_rx_priority_queue(p_gmac, (uint32_t)&gs_rx_desc_null, GMAC_QUE_1);

/* Clear interrupts */

/* Fill in GMAC device memory management */
gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
gmac_dev_mm.p_rx_dscr = gs_rx_desc;
gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS;

gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
gmac_dev_mm.p_tx_dscr = gs_tx_desc;
gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS;

gmac_init_mem(p_gmac_dev, GMAC_QUE_0, &gmac_dev_mm, gs_tx_callback);

/* Enable Rx and Tx, plus the statistics register */
gmac_enable_transmit(p_gmac, true);
gmac_enable_receive(p_gmac, true);
gmac_enable_statistics_write(p_gmac, true);

/* Set up the interrupts for transmission and errors */
GMAC_IER_RLEX  | /* Enable retry limit exceeded interrupt. */
GMAC_IER_RCOMP | /* Enable receive complete interrupt. */
GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
GMAC_IER_ROVR  | /* Enable receive overrun interrupt. */
GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
GMAC_IER_TUR   | /* Enable transmit underrun interrupt. */
GMAC_IER_TFC   | /* Enable transmit buffers exhausted in mid-frame interrupt. */
GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
GMAC_IER_PFNZ  | /* Enable pause frame received interrupt. */
GMAC_IER_PTZ);   /* Enable pause time zero interrupt. */


/** * brief Initialize the GMAC driver. * * param pgmac Pointer to the GMAC instance. * param pgmacdev Pointer to the GMAC device instance. * param popt GMAC configure options. / void gmacdevinit(Gmac pgmac, gmacdevicet* pgmacdev, gmacoptionst* popt) { gmacdevmemt gmacdev_mm;

/* Disable TX & RX and more */
gmac_network_control(p_gmac, 0);
gmac_disable_interrupt(p_gmac, ~0u);

/* Clear all status bits in the receive status register. */
gmac_clear_rx_status(p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA);

/* Clear all status bits in the transmit status register */
gmac_clear_tx_status(p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE

/* Clear interrupts */
/*  Receive Buffer Offset
 * Indicates the number of bytes by which the received data
 * is offset from the start of the receive buffer
 * which can be handy for alignment reasons */
/* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */
#error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0
/* Enable the copy of data into the buffers
   ignore broadcasts, and not copy FCS. */
	( gmac_get_config(p_gmac) & ~GMAC_NCFGR_RXBUFO_Msk ) |
	GMAC_NCFGR_RFCS |   /*  Remove FCS, frame check sequence (last 4 bytes) */
	GMAC_NCFGR_PEN |    /* Pause Enable */

 * GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable.
 * Note: the SAM4E does have RX checksum offloading
 * but TX checksum offloading has NOT been implemented.

gmac_enable_copy_all(p_gmac, p_opt->uc_copy_all_frame);
gmac_disable_broadcast(p_gmac, p_opt->uc_no_boardcast);

/* Fill in GMAC device memory management */
gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
gmac_dev_mm.p_rx_dscr = gs_rx_desc;
gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS;

gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
gmac_dev_mm.p_tx_dscr = gs_tx_desc;
gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS;

gmac_init_queue(p_gmac, p_gmac_dev);
gmac_set_address(p_gmac, 0, p_opt->uc_mac_addr);


/** * brief Send ulLength bytes from pcFrom. This copies the buffer to one of the * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready. * If lEndOfFrame is true then the data being copied is the end of the frame * and the frame can be transmitted. * * param pgmacdev Pointer to the GMAC device instance. * param pbuffer Pointer to the data buffer. * param ulsize Length of the frame. * param functxcb Transmit callback function. * * return Length sent. / uint32t gmacdevwrite(gmacdevice_t pgmacdev, gmacquelistt queueidx, void *pbuffer, uint32t ulsize, gmacdevtxcbt functxcb) { volatile gmactxdescriptort *ptxtd; volatile gmacdevtxcbt *pfunctxcb; Gmac phw = pgmacdev->phw; gmacqueuet pgmacqueue = &pgmacdev->gmacqueuelist[queue_idx];

/* Check parameter */
if (ul_size > GMAC_TX_UNITSIZE) return GMAC_PARAM;

/* Pointers to the current transmit descriptor */
p_tx_td = &p_gmac_queue->p_tx_dscr[p_gmac_queue->us_tx_head];

/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
if (CIRC_SPACE(p_gmac_queue->us_tx_head, p_gmac_queue->us_tx_tail, p_gmac_queue->us_tx_list_size) == 0)
	if ((p_tx_td->status.val & GMAC_TXD_USED) == 0)	return GMAC_TX_BUSY;

/* Pointers to the current Tx callback */
p_func_tx_cb = &p_gmac_queue->func_tx_cb_list[p_gmac_queue->us_tx_head];

/* Set up/copy data to transmission buffer */
if (p_buffer && ul_size) 
	/* Driver manages the ring buffer */
	/* Calculating the checksum here is faster than calculating it from the GMAC buffer
	 * because withing p_buffer, it is well aligned */
	memcpy((void *)p_tx_td->addr, p_buffer, ul_size);

// vGMACGenerateChecksum( ( uint8t * ) ptx_td->addr ); // Done in Hardware on SAME70 }

/* Tx callback */
*p_func_tx_cb = func_tx_cb;

/* Update transmit descriptor status */

/* The buffer size defined is the length of ethernet frame,
   so it's always the last buffer of the frame. */
if( p_gmac_queue->us_tx_head == ( int32_t )( p_gmac_queue->us_tx_list_size - 1 ) )
	/* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked */
	p_tx_td->status.val = ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP;
	p_tx_td->status.val = ul_size | GMAC_TXD_LAST;

circ_inc32( &p_gmac_queue->us_tx_head, p_gmac_queue->us_tx_list_size );

/* Now start to transmit if it is still not done */

return GMAC_OK;

} ~~~

No Transmission from GMAC using +TCP on SAME70

Posted by tominvert on April 12, 2017

Also I have managed to run a TCP server using lwIP with the same hardware, so I'm confident that its a software driver issue. The reason I'm switching to +TCP is that I've struggled to get the lwIP server to function reliably under moderate load. My application already uses FreeRTOS, and +TCP seemed to be documented far better than lwIP, so I decided it was worth the effort to try it.

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 12, 2017

Hi Tom, unfortunately I do not have a comparable hardware platform to test it (only SAM4E). But later today I will have a look at your sources. We'll find the problem :-)

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 12, 2017

I can't see how to attach files here though

While typing, you see "Add attachments" below your message?

Press it, and press Browse, and you can select you file. Could you zip both your NetworkInterface.c and gmac file?

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 12, 2017

I don't include any PHY initialisation or communications as our MCU connects directly to an Ethernet switch over RMII

You do receive ARP messages, so the PHY seems to work. But don't you want to actively start an auto-negotiation after the Link Status goes high? And don't you want to advertise some properties ( duplex / dmix / speed )?

If the packet to be sent is too small, it might not get sent data at all.

The EMAC has a minimum length it can transmit.

For a STM32F4, we've defined:


Shorter packets, like ARP responses, may get extended with zero's. Put the above define into your FreeRTOSIPConfig.h. Regards.

No Transmission from GMAC using +TCP on SAME70

Posted by tominvert on April 13, 2017

I found the issue - amateur mistake, forgot to include the pin multiplexer configuration, as lwIP had included it in the GMAC init function but the equivalent +TCP function left it to be configured elsewhere (board_init in the example). I'm quite surprised I had anything work at all. Thanks for the assistance.

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 13, 2017

Very good Tom, and thanks for reporting back. Pin multiplexers can be confusing. Still you might want to define ipconfigETHERNET_MINIMUM_PACKET_BYTES = 60, because Ethernet doesn't like smaller packets. The 60 bytes is excluding the 4-byte ("hardware") CRC that will be added by the PHY.

No Transmission from GMAC using +TCP on SAME70

Posted by rtimenoob on April 13, 2017

I am also trying to get to run my SAME70 together with freertos and lwip. Could u please tell me your freertos settings in conftasks.h? Although in the datasheet it is said, that the BASPRI register is using bits 7:4, in the CMSIS configPRIO_BITS is defined with 3. I am not sure if 3 or 4 Bits are correct. Of course all other priority settings depend on it. Do u know if it is possible to run a WEB Server with SSI and CGI on freertos+TCP? On an older project I was able to get to run freertos + lwip but the throughput was very poor. I put a lot of time in optimization the lwip stack (user pools, NOCOPY....) but I had unpredictable crashes :( I have a new try now on the SAME70. I am looking forward to your answer. Thank u very much.

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 13, 2017

Hi Werner,

Do u know if it is possible to run a WEB Server with SSI and CGI on freertos+TCP?

FreeRTOS+TCP does have a web-server that is simple and fast. It needs a kind of stdio interface to a medium. You can use FreeRTOS+FAT for this, on a RAM-disk or an SD-card.

~~~ FreeRTOS-Plus-TCP/protocols/HTTP/FreeRTOSHTTPserver.c ~~~

It does not support SSI and CGI explicitly, what it has is an Application Hook:

~~~ sizet uxApplicationHTTPHandleRequestHook( const char *pcURLData, char *pcBuffer, sizet uxBufferLength ) ~~~

It is called for every GET request received. It provides you the URL and a buffer in which the response can be formatted.

~~~ #define ipconfigHTTPHASHANDLEREQUESTHOOK 1 #define ipconfigHTTPREQUESTCHARACTER '?' ~~~

In the above example, all URL's containing a question mark will be forwarded to the application. In case your application does not want to handle a request, just return 0.

How I used it: many GET request need to retrieve a file. That is all handled by the webserver directly. The JS or JQuery code can do a special GET (using 'jQuery.ajax'), to asks or tell things to your application, as for example:

~~~ /question?temperature=? /question?volume=12 /question?remote_access=*&settings={"pass":"OJ8T5n4o%U$No4iun","name":"John"} ~~~

Please have a try with +TCP on your SAME7.

Currently, the Atmel drivers are not yet zero-copy. The functions gmac_dev_read() and gmac_dev_write() must be adapted to make them zero-copy. If you want I can assist with that. In this ZIP file you find an example of a zero-copy +TCP driver ( for STM32F4x )

No Transmission from GMAC using +TCP on SAME70

Posted by heinbali01 on April 20, 2017

I wrote:

Currently, the Atmel drivers are not yet zero-copy

I just checked the +TCP SAM4E driver:

It is impossible to make the RX-path zero-copy. The EMAC uses fixed-size (128-byte) buffers for reception.

It is possible to make transmission zero-copy: the gmac driver has some application-hook GMAC_USES_TX_CALLBACK. But, the call-back from the driver to the application is being done from within the ETH interrupt. That is against the idea of a deferred interrupt handler: let the ISR wake-up a task (prvEMACHandlerTask), and let the task check the DMA descriptors. Also, the ISR gmac_handler() will become quite long when GMAC_USES_TX_CALLBACK is enabled. I would consider rewriting the interrupt handler.

No Transmission from GMAC using +TCP on SAME70

Posted by febe2 on September 14, 2017

Hi Guys, My latest project idea was to transfer a file via ftp server to my microcontroller and save this file on my sd card plugged in to my sam e70 eva board.

On my next step I found a project for the sam4 board (http://www.freertos.org/FreeRTOS...) and I wanted to realzie it on my sam e70 board.

I downloaded an example file from http://www.freertos.org/FreeRTOS... (freerrtos-Plus/Demo/FreeRTOSPlusTCPandFAT_ATSAM4E/RtosDemo.atsln) and compiled it successfully in atmel studio for sam4e.

Next step would be to port the project from sam4e to sam e70.

What exactly do i have to port? only the network interface?

No Transmission from GMAC using +TCP on SAME70

Posted by toastedcpu on September 15, 2017

Hello, I am working on the SAM E70 explained board. I did not get it to work yet.

My approach is to use in Atemel Studio 7 File->New Example project, create a FreeRTOS demo project, create a LWIP demo project and copy the folders from LWIP into FreeRTOS. I managed to run both demos seperately. Perhaps to copy FreeRTOS into LWIP will envolve less work because of Ethernet peripherals will already be fine.

I had to delete the test subfolder of lwip because of compilation problems. I added in the project Proerties Toolchain->ARM/GNU C compiler Directories all the lwip directories of the lwip demo. I dot fully invegate the mismatch of confclock.where in one I have #define CONFIGSYSCLOCKRES set to SYSCLOCKPRES1, while the other is SYSCLOCKPRES_2. Frank

No Transmission from GMAC using +TCP on SAME70

Posted by rtel on September 15, 2017

lwIP will require a different configuration to use in a multi-threaded environment.

I'm afraid I can't answer device specific questions - we use a LOT of different chips and you would be able to find device specific answers faster than us as we would have to start by getting the hardware manual.

We have our own TCP/IP stack running on a SAM4E if that is useful as a reference, or to adapt: http://www.freertos.org/FreeRTOS-Plus/FreeRTOSPlusTCP/TCPIPFATExamplesAtmelSAM4E.html

[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]

Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

Meet us at Embedded World. Hall 3A-525.

Hear from Richard Barry at Embedded World. Feb 28, 16:00, Hall 4-428.

Video: Watch James Gosling & Richard Barry at re:Invent, Las Vegas 2017.

FreeRTOS kernel V10.0.1 is available for immediate download. Now MIT licensed.

FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS