LPC3250 porting

georgbat wrote on Thursday, October 05, 2017:

Hi! I try porting FreeRTOS kernel on NXP LPC3250.
I use arm-none-eabi-gcc. I took arm7 lpc23xx port for gcc as base. Then I compared assembler parts whith iar at91sam9xe port. They are the same.
Now, my LPC use only one interrupt - TIMER0_MATCH. All other are disabled. So I installed the vPortTick function as ARM IRQ handler. (I do not need a IRQ dispather because, there is no other interupts.) Then I installed vPortYieldProcessor function as SWI handler.

I try to run simple blink task

static BOOL_8 blink2 = TRUE;
void task1(void *) {
	while(true) {
		if (blink2)
			cpld_set_bits(ECPLDLeds, CPLD_LED_0);
		else
			cpld_clear_bits(ECPLDLeds, CPLD_LED_0);
		blink2 = (~blink2) & 0x1;
		vTaskDelay(500 / portTICK_PERIOD_MS);
	}
}

But this works unstable. Led blinks whith different periods. Sometimes stops and after some time starts again.

I think that it is a result of execution timer0 handler over swi. Can anyone explain, how should handlers priorities be defined for correctly work?

Handlers are:

void vPortYieldProcessor( void )
{
	/* Within an IRQ ISR the link register has an offset from the true return
	address, but an SWI ISR does not.  Add the offset manually so the same
	ISR return code can be used in both cases. */
	__asm volatile ( "ADD		LR, LR, #4" );

	/* Perform the context switch.  First save the context of the current task. */
	portSAVE_CONTEXT();

	/* Find the highest priority task that is ready to run. */
	__asm volatile ( "bl vTaskSwitchContext" );

	/* Restore the context of the new task. */
	portRESTORE_CONTEXT();
}

void vTickISR( void ) __attribute__((naked));
void vTickISR( void )
{
	extern INT_32 portTickTimerId;

	/* Save the context of the interrupted task. */
	portSAVE_CONTEXT();

	/* Increment the RTOS tick count, then look for the highest priority
	task that is ready to run. */
	__asm volatile
	(
		"	bl xTaskIncrementTick	\t\n" \
		"	cmp r0, #0				\t\n" \
		"	beq SkipContextSwitch	\t\n" \
		"	bl vTaskSwitchContext	\t\n" \
		"SkipContextSwitch:			\t\n"
	);

	/* Ready for the next interrupt. */
	timer_ioctl(portTickTimerId, TMR_CLEAR_INTS, 1);

	/* Restore the context of the new task. */
	portRESTORE_CONTEXT();
}

rtel wrote on Thursday, October 05, 2017:

A couple of notes:

  1. Is you vPortYieldProcessor() also a naked function? If not, it needs
    to be.

  2. The call to timer_ioctl() needs to be in assembly too.
    portTickTimerId in register r0, TMR_CLEAR_INTS in r1 and 1 in r2. It
    might just be simpler to write the code inline using assembly, or have a
    function that takes no parameters that calls
    timer_ioctl(portTickTimerId, TMR_CLEAR_INTS, 1) so you can simply bl to
    that function rather than load the registers and bl to timer_ioctl.

thomask wrote on Sunday, October 08, 2017:

Hi George!

I already ported FreeRTOS for a custom LPC3240 board:

https://interactive.freertos.org/hc/en-us/community/posts/210027446-LPC32xx-ARM9-with-FPU-support-using-GCC

best regards,
thomas

georgbat wrote on Tuesday, October 10, 2017:

Thank you, Thomas.
I imported your port files to my project. Аnd had tryed to run following:

void task1(void *) {
    BOOL_8 blink1 = TRUE;
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 500;
    xLastWakeTime = xTaskGetTickCount();
    while(true) {
        if (blink1)
            cpld_set_bits(ECPLDLeds, CPLD_LED_1);
        else
            cpld_clear_bits(ECPLDLeds, CPLD_LED_1);
        blink1 = (~blink1) & 0x1;
        //		for (volatile int i=0; i < 8000000; ++i);
        //		vTaskDelay(500 / portTICK_PERIOD_MS);
        vTaskDelayUntil( &xLastWakeTime, xFrequency );
    }
}

void task2(void *) {
    BOOL_8 blink2 = FALSE;
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 500;
    xLastWakeTime = xTaskGetTickCount();
    while(true) {
        if (blink2)
            cpld_set_bits(ECPLDLeds, CPLD_LED_2);
        else
            cpld_clear_bits(ECPLDLeds, CPLD_LED_2);
        blink2 = (~blink2) & 0x1;
        //		for (volatile int i=0; i < 8000000; ++i);
        //		vTaskDelay(500 / portTICK_PERIOD_MS);
        vTaskDelayUntil( &xLastWakeTime, xFrequency );
    }
}

extern "C" int c_entry()
{
	bool blink5 = true;
	isens_board_init();

	BaseType_t result = xTaskCreate(
		task1,
		"task1",
		configMINIMAL_STACK_SIZE,
		NULL,
		tskIDLE_PRIORITY + 1,
		&task1Handle
	);
	(void)result;

	BaseType_t result2 = xTaskCreate(
		task2,
		"task2",
		configMINIMAL_STACK_SIZE,
		NULL,
		tskIDLE_PRIORITY + 1,
		&task2Handle
	);
	(void)result2;

	vTaskStartScheduler();

	while(true) {
		if (blink5)
			cpld_set_bits(ECPLDLeds, CPLD_LED_0);
		else
			cpld_clear_bits(ECPLDLeds, CPLD_LED_0);
		blink5 = !blink5;
		for (volatile int i=0; i < 16000000; ++i);
	}
	return 0;
}

If I run tasks delayed by for() cycle - it works correctly. If I run tasks delayed by vTaskDelay or vTaskDelayUntil functions - it works bad. I see folowing behavior: two times it switch well, but then task1’s led stops. After few secconds (5-10) lights again, then turns off again after random period. Task2’s led blinks stable with 500ms period. This behavior looks like the same as a problem in my first post.