Перейти к содержанию
    

вложенные прерывания - архитектура

Подскажите пожалуйста а почему здесь вложенность реализована через программное прерывание?

https://github.com/MIPS/FreeRTOS-mips64/blob/master/FreeRTOS/Source/portable/GCC/MIPS64/port.c

Получается сначала я захожу в программное и далее из программное в аппаратное

Правильно ли я понимаю что программное прерывание сделано чтобы переключать контекст?

А разве во FreeRTOS контекст переключается только в этом случае?(как я понимаю в случае вызова yeild самой ОС)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 hours ago, addi II said:

Подскажите пожалуйста а почему здесь вложенность реализована через программное прерывание?

 

В port.c нет никаких вложенных прерываний.

5 hours ago, addi II said:

А разве во FreeRTOS контекст переключается только в этом случае?

Да, особенность архитектуры FreeRTOS. Контекст сохраняется при входе в прерывание, и восстанавливается при выходе. Других мест сохранения/восстановления нет. Поэтому для переключения задач нужно войти в прерывание (иначе контект текущей задачи не сохранить и контект другой не восстановить)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, а в каком портровом файле они(сохранение контекста при вложенном прерывании) должны быть??

И что может служить программным прерыванием для переключения контекста -- обычный периферийный таймер???

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 hours ago, addi II said:

Спасибо, а в каком портровом файле они(сохранение контекста при вложенном прерывании) должны быть??

 

В том же самом, что и для не вложенных. Даже больше - это одно и тоде сохранение контекста.

Ваш код влоденные прерывания не поддерживает 😞

Манипуляции с контекстом находятся в файле ISR_Support.h - это макросы portSAVE_CONTEXT/portRESTORE_CONTEXT и portYIELD_SAVE/portYIELD_RESTORE (при этом само сохранение находится в макросах _gpctx_load/_gpctx_save в файле ctx.S)

Первая пара (portSAVE_CONTEXT/portRESTORE_CONTEXT) используется для прерываний из которых нельзя стелать таск свитч. Эти макросы не перключают стек, и всё было бы хорошо, но они сохраняют и восстанавливают стек поинтер через одну глобальную переменную - uxSavedTaskStackPointer. Соотвественно вложенное прерывание перепишет эту пременную и менее вложенное прерывание снесёт стек к чертям :(

Вторая пара макросов при входе в прерывание переключает стеки процессов при входе/выходе.

Отдельный стек для ядра вообще не используется - ядро пасётся на стеке текущего процесса.

 

В общем, если убрать uxSavedTaskStackPointer (заменить стеком или вообще убрать - не совсем понятно для чего указатель стека сохраняли, он не должен меняться) то наверное вложенные прерывания будут работать

 

 

9 hours ago, addi II said:

И что может служить программным прерыванием для переключения контекста -- обычный периферийный таймер???

Нет, там только yield

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 hours ago, xvr said:

Нет, там только yield

Не понял, а как же timer tick interrtupt?

Он же как раз выщывает yield

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, addi II said:

Не понял, а как же timer tick interrtupt?

Он же как раз выщывает yield

Именно. Yield и вызывает переключение контекстов. Откуда он при этом пришёл все равно. Мог из таймера, мог из нити исполнения. Этот порт поддерживает только вложенность yield прерывания в чего угодно (в том числе и в другие прерывания). Вложенность других прерываний друг в друга не поддерживает (да их там и нет - один таймер, нечему вкладываться)

Полная поддержка вложенности прерываний подразумевает возможность произвольной вложенности (на любую глубину) любых прерываний (возможно с некоторыми ограничениями в виде приоритетов).

Возможна ограниченная поддержка в виде групп прерываний, при этом группы могут как то вкладываться друг в друга (у вас как раз такая). yield - одна группа, остальные прерывания - вторая.

 

В некоторых других портах FreeRTOS есть полная поддержка вложенных прерываний. Например в ESP32 - там есть отдельный стек для ядра и отслеживание уровня вложенности прерываний. Можете посмотреть, для общего развития 🙂

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо!, можно еще спросить?

 

У меня почти так как вы описываете, за исключением добавления вложенности

Прошу посмотреть на мой порт для MIPS64 аппаратного прерывания для ticlincrement и программного для yield

Сейчас работа FreeRTOS не полноценна, первая задача запускается, далее вторая, и все.., нет циклического запуска, хотя задачи с одинаковым приоритетом

В отладке вижу что когда наступает восстановление EPC по portRestoreYIELD после первой задачи в EPC вижу адрес второй задачи, но далее при следующем восстановлении по  portRestoreYIELD EPC содержит адрес перед последнем exception, тесть адрес во второй задаче(адрес первый задачи который должен где-то храниться после xtaskcreate не перезаписывается в EPC)

Вот мой вектор инкрементирования тика:

		_CP0_TIM_VEC4_handler:

		// Cause_DC = 1
		mfc0    a0, C0_CAUSE
		li      a1, ~(1 << 27)
		and     a0, a0, a1
		mtc0    a0, C0_CAUSE
		ehb

		di
		ehb
		ld		sp, xISRStackTop /*Swap to the system stack ????? */
		// LA      sp, __stack

		LA		gp, _gp
		/*.macro  portSAVE_CONTEXT*/
		// Make room for the context. First save the current status so it can be
		//manipulated, and the cause and EPC registers so their original values are
		//captured. 
		ADDU        sp, sp, -CTX_SIZE
		REG_S       k1, CTX_K1(sp)

		// k1 is used as the frame pointer. 
		ADDU        k1, zero, sp
		
		// Save the context into the space just created. 
		/*.macro _gpctx_save*/

		/*ld		sp, xISRStackTop*/
  
		/* Save general registers.  */
		/* $27/k1 must be saved prior to using this macro.  */
		.irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 \
			20,21,22,23,24,25,26,28,29,30,31
		REG_S	$\num, CTX_REG(\num) (k1)
		.endr

		PTR_S   $0, CTX_LINK(k1)	/* Clear the link field.  */

		mfhi	$9
		REG_S	$9, CTX_HI0(k1)
		mflo	$10
		REG_S	$10, CTX_LO0(k1)

		/* Save CP0 registers.  */
		PTR_MFC0 $31, C0_EPC
		ehb
		/* 	mfc0	$10, C0_STATUS */
		/* 	ehb */
		REG_S	$31, CTX_EPC(k1)
		ehb
		/* 	sw		$10, CTX_STATUS(k1) */
		.endm

		// Save the stack pointer. 
		LA          s6, uxSavedTaskStackPointer
		REG_S       k1, (s6)
		REG_L       k1, CTX_K1(sp)

		/*.endm*/ 
		jal		vPortIncrementTick
		nop
		nop
		/*.macro  portRESTORE_CONTEXT*/

		LA          s6, uxSavedTaskStackPointer
		REG_L       k0, (s6)

		// Restore the context. 
		/*.macro _gpctx_load*/
		REG_L	$9, CTX_HI0(k0)
		REG_L	$10, CTX_LO0(k0)
		mthi	$9
		mtlo	$10

		/* Restore the general registers.  */
		.irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, \
			20,21,22,23,24,25
		REG_L	$\num, CTX_REG(\num) (k0)
		.endr

		/* Restore CP0 registers, kernel registers and stack with
	   interrupts disabled.  */
		/*  	di */
		/* 	lw		k1, CTX_STATUS(k0) */
		/* 	mtc0	k1, C0_STATUS */
		REG_L	k1, CTX_EPC(k0)
		PTR_MTC0 k1, C0_EPC

		REG_L	$27, CTX_REG(27) (k0)
		REG_L	$28, CTX_REG(28) (k0)
		REG_L	$29, CTX_REG(29) (k0)
		REG_L	$30, CTX_REG(30) (k0)
		REG_L	$31, CTX_REG(31) (k0)

		/* Finally restore k0/$26.  */
		REG_L	$26, CTX_REG(26)(k0)

		/*.endm*/

		LA          sp, uxSavedTaskStackPointer
		REG_L       sp, (sp)

		ADDU        sp, sp, CTX_SIZE


		/*.endm*/
		// Count = 0
		mtc0	zero, C0_COUNT
		ehb

		mfc0    a0, C0_CAUSE
		li      a1, (1 << 27)
		or      a0, a0, a1
		mtc0    a0, C0_CAUSE
		ehb
		ei
		ehb

		eret
		.end _CP0_TIM_VEC4_handler

Вот вектор переключения

	

		#mfc0    a0, C0_CAUSE
		
		di
		ehb

		ld		sp, xISRStackTop
		LA		gp, _gp
		/*portYIELD_SAVE*/
		// Make room for the context. First save the current status so it can be
		//manipulated. 
		daddiu      sp, sp, -CTX_SIZE
		REG_S       k1, CTX_K1(sp)

		// k0cd is used as the frame pointer. 
		move        k1, sp

		// Save the context into the space just created. 
		/*_gpctx_save*/
		.irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 \
			20,21,22,23,24,25,27,28,29,30,31
        rdpgpr  k1, $\num
		REG_S   k1, CTX_REG(\num) (k0)
		.endr

		rdpgpr  k1, $26
		nop
		nop
		REG_S   k1, CTX_REG(26) (k0)

		mfhi	k1
		REG_S	k1,  CTX_HI0(k0)
		mflo	k1
		REG_S	k1,  CTX_LO0(k0)

		/* Save CP0 registers.  */
		PTR_MFC0   k1, C0_EPC
		REG_S      k1, CTX_EPC(k0)

		// Save the stack pointer to the task. 
		LA			s7, pxCurrentTCB
		REG_L		s7, (s7)
		REG_S		k1, (s7) 
		
		jal		vTaskSwitchContext
		nop
		nop

		/*portYIELD_RESTORE*/
		
		// Set the context restore register from the TCB. 
		LA			s0, pxCurrentTCB
		REG_L		s0, (s0)
		REG_L		k0, (s0)

	
		// The _gpctx_load restore code just wholesale copies the
		// status register from the context back to the register loosing
		// any changes that may have occured, 'status' is really global state
		// You dont enable interrupts on one thread and not another...
		// So we just copy the current status value into the saved value
		// so nothing changes on the restore
		//
		/***/
		mfc0	k1, C0_STATUS
		sw		k1, CTX_STATUS(k0)
		/***/
		/*_gpctx_load*/
		REG_L	$9, CTX_HI0(k0)
		REG_L	$10, CTX_LO0(k0)
		mthi	$9
		mtlo	$10

		/* Restore the general registers.  */
		.irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, \
			20,21,22,23,24,25
		REG_L	$\num, CTX_REG(\num) (k0)
		.endr

		/* Restore CP0 registers, kernel registers and stack with
	   interrupts disabled.  */
		/*  	di */
		/* 	lw		k1, CTX_STATUS(k0) */
		/* 	mtc0	k1, C0_STATUS */
		REG_L	k1, CTX_EPC(k0)
		PTR_MTC0 k1, C0_EPC

		REG_L	$27, CTX_REG(27) (k0)
		REG_L	$28, CTX_REG(28) (k0)
		REG_L	$29, CTX_REG(29) (k0)
		REG_L	$30, CTX_REG(30) (k0)
		REG_L	$31, CTX_REG(31) (k0)

		/* Finally restore k0/$26.  */
		REG_L	$26, CTX_REG(26)(k0)


		// Restore the stack pointer from the TCB. 
		LA			sp, pxCurrentTCB
		REG_L		sp, (sp)
		REG_L		sp, (sp)

		// Remove stack frame. 
		daddiu		sp, sp, CTX_SIZE
	
		ei
		ehb
	        
		eret
		.end _SW_VEC1_handler

Возможна ли причина отсутствия периодического переключения задач как раз в отсутствии обработки вложенности?

Я только не совсем понял что Вы имели ввиду когда говорили что в yield происходит переключение стеков процессов при входе/выходе???

И я не совсем понял нужно ли в обоих векторах делать обработку вложенности или только в yield??

К сожалению не нашел пример на ESP32, взял за основу для PIC32MZ

И там в обоих векторах происходит обработка вложенности при этом по коду наскольку я понял если переменная вложенности ноль то идем по топовому указателю стека если не ноль то согласно uxSavedTaskStackPointe, но не понятно что является причиной для инкрекментирования самой переменной

Вот хендлер на инкрементировании тика от пика:

vPortTickInterruptHandler:

	/*.macro	portSAVE_CONTEXT*/

	/* Make room for the context. First save the current status so it can be
	manipulated, and the cause and EPC registers so their original values are
	captured. */
	mfc0		k0, _CP0_CAUSE
	addiu		sp, sp, -portCONTEXT_SIZE

	mfc0		k1, _CP0_STATUS

	/* Also save s7, s6 and s5 so they can be used.  Any nesting interrupts
	should maintain the values of these registers across the ISR. */
	sw			s7, 48(sp)
	sw			s6, 44(sp)
	sw			s5, 40(sp)
	sw			k1, portSTATUS_STACK_LOCATION(sp)

	/* Prepare to enable interrupts above the current priority. */
	srl			k0, k0, 0xa
	ins 		k1, k0, 10, 7
	srl			k0, k0, 0x7 /* This copies the MSB of the IPL, but it would be an error if it was set anyway. */
	ins 		k1, k0, 18, 1
	ins			k1, zero, 1, 4

	/* s5 is used as the frame pointer. */
	add			s5, zero, sp

	/* Check the nesting count value. */
	la			k0, uxInterruptNesting
	lw			s6, (k0)

	/* If the nesting count is 0 then swap to the the system stack, otherwise
	the system stack is already being used. */
	bne			s6, zero, 1f
	nop

	/* Swap to the system stack. */
	la			sp, xISRStackTop
	lw			sp, (sp)

	/* Increment and save the nesting count. */
1:	addiu		s6, s6, 1
	sw			s6, 0(k0)

	/* s6 holds the EPC value, this is saved after interrupts are re-enabled. */
	mfc0 		s6, _CP0_EPC

	/* Re-enable interrupts. */
	mtc0		k1, _CP0_STATUS

	/* Save the context into the space just created.  s6 is saved again
	here as it now contains the EPC value.  No other s registers need be
	saved. */
	sw			ra, 120(s5)
	sw			s8, 116(s5)
	sw			t9, 112(s5)
	sw			t8, 108(s5)
	sw			t7, 104(s5)
	sw			t6, 100(s5)
	sw			t5, 96(s5)
	sw			t4, 92(s5)
	sw			t3, 88(s5)
	sw			t2, 84(s5)
	sw			t1, 80(s5)
	sw			t0, 76(s5)
	sw			a3, 72(s5)
	sw			a2, 68(s5)
	sw			a1, 64(s5)
	sw			a0, 60(s5)
	sw			v1, 56(s5)
	sw			v0, 52(s5)
	sw			s6, portEPC_STACK_LOCATION(s5)
	sw			$1, 16(s5)

	

	
	/* Update the task stack pointer value if nesting is zero. */
	la			s6, uxInterruptNesting
	lw			s6, (s6)
	addiu		s6, s6, -1
	bne			s6, zero, 1f
	nop

	/* Save the stack pointer. */
	la			s6, uxSavedTaskStackPointer
	sw			s5, (s6)
1:
	.endm



	jal 		vPortIncrementTick
	nop

	/*.macro	portRESTORE_CONTEXT*/

	/* Restore the stack pointer from the TCB.  This is only done if the
	nesting count is 1. */
	la			s6, uxInterruptNesting
	lw			s6, (s6)
	addiu		s6, s6, -1
	bne			s6, zero, 1f
	nop
	la			s6, uxSavedTaskStackPointer
	lw			s5, (s6)


1:

	

	/* s6 is loaded as it was used as a scratch register and therefore saved
	as part of the interrupt context. */
	lw			s7, 48(s5)
	lw			s6, 44(s5)
	lw			v0, 52(s5)
	lw			v1, 56(s5)
	lw			a0, 60(s5)
	lw			a1, 64(s5)
	lw			a2, 68(s5)
	lw			a3, 72(s5)
	lw			t0, 76(s5)
	lw			t1, 80(s5)
	lw			t2, 84(s5)
	lw			t3, 88(s5)
	lw			t4, 92(s5)
	lw			t5, 96(s5)
	lw			t6, 100(s5)
	lw			t7, 104(s5)
	lw			t8, 108(s5)
	lw			t9, 112(s5)
	lw			s8, 116(s5)
	lw			ra, 120(s5)

	/* Protect access to the k registers, and others. */
	di
	ehb

	/* Decrement the nesting count. */
	la			k0, uxInterruptNesting
	lw			k1, (k0)
	addiu		k1, k1, -1
	sw			k1, 0(k0)

	
	

		/* Restore the frame when there is no hardware FP support. */
		lw			k0, portSTATUS_STACK_LOCATION(s5)
		lw			k1, portEPC_STACK_LOCATION(s5)

		/* Leave the stack in its original state.  First load sp from s5, then
		restore s5 from the stack. */
		add			sp, zero, s5
		lw			s5, 40(sp)

		addiu		sp, sp,	portCONTEXT_SIZE



	mtc0		k0, _CP0_STATUS
	mtc0 		k1, _CP0_EPC
	ehb
	eret
	nop

	.endm


	/*.end vPortTickInterruptHandler*/

и вот yield:

vPortYieldISR:

	
		/* Code sequence for no FPU support, the context save requires advance
		knowledge of the stack frame size when no FPU is being used */

		/* Make room for the context. First save the current status so it can be
		manipulated, and the cause and EPC registers so thier original values are
		captured. */
		addiu	sp, sp, -portCONTEXT_SIZE
		mfc0	k1, _CP0_STATUS

		/* Also save s6 and s5 so they can be used.  Any nesting interrupts should
		maintain the values of these registers across the ISR. */
		sw		s6, 44(sp)
		sw		s5, 40(sp)
		sw		k1, portSTATUS_STACK_LOCATION(sp)

		/* Prepare to re-enabled interrupts above the kernel priority. */
		ins 	k1, zero, 10, 7         /* Clear IPL bits 0:6. */
		ins 	k1, zero, 18, 1         /* Clear IPL bit 7.  It would be an error here if this bit were set anyway. */
		ori		k1, k1, ( configMAX_SYSCALL_INTERRUPT_PRIORITY << 10 )
		ins		k1, zero, 1, 4          /* Clear EXL, ERL and UM. */

		/* s5 is used as the frame pointer. */
		add		s5, zero, sp

		/* Swap to the system stack.  This is not conditional on the nesting
		count as this interrupt is always the lowest priority and therefore
		the nesting is always 0. */
		la		sp, xISRStackTop
		lw		sp, (sp)

		/* Set the nesting count. */
		la		k0, uxInterruptNesting
		addiu	s6, zero, 1
		sw		s6, 0(k0)

		/* s6 holds the EPC value, this is saved with the rest of the context
		after interrupts are enabled. */
		mfc0 	s6, _CP0_EPC

		/* Re-enable interrupts above configMAX_SYSCALL_INTERRUPT_PRIORITY. */
		mtc0	k1, _CP0_STATUS

		/* Save the context into the space just created.  s6 is saved again
		here as it now contains the EPC value. */
		sw		ra, 120(s5)
		sw		s8, 116(s5)
		sw		t9, 112(s5)
		sw		t8, 108(s5)
		sw		t7, 104(s5)
		sw		t6, 100(s5)
		sw		t5, 96(s5)
		sw		t4, 92(s5)
		sw		t3, 88(s5)
		sw		t2, 84(s5)
		sw		t1, 80(s5)
		sw		t0, 76(s5)
		sw		a3, 72(s5)
		sw		a2, 68(s5)
		sw		a1, 64(s5)
		sw		a0, 60(s5)
		sw		v1, 56(s5)
		sw		v0, 52(s5)
		sw		s7, 48(s5)
		sw		s6, portEPC_STACK_LOCATION(s5)
		/* s5 and s6 has already been saved. */
		sw		s4, 36(s5)
		sw		s3, 32(s5)
		sw		s2, 28(s5)
		sw		s1, 24(s5)
		sw		s0, 20(s5)
		sw		$1, 16(s5)

		/* s7 is used as a scratch register as this should always be saved across
		nesting interrupts. */

	
		/* Save the stack pointer to the task. */
		la		s7, pxCurrentTCB
		lw		s7, (s7)
		sw		s5, (s7)

		/* Set the interrupt mask to the max priority that can use the API.  The
		yield handler will only be called at configKERNEL_INTERRUPT_PRIORITY which
		is below configMAX_SYSCALL_INTERRUPT_PRIORITY - so this can only ever
		raise the IPL value and never lower it. */
		di
		ehb
		mfc0	s7, _CP0_STATUS
		ins 	s7, zero, 10, 7
		ins 	s7, zero, 18, 1
		ori		s6, s7, ( configMAX_SYSCALL_INTERRUPT_PRIORITY << 10 ) | 1

		/* This mtc0 re-enables interrupts, but only above
		configMAX_SYSCALL_INTERRUPT_PRIORITY. */
		mtc0	s6, _CP0_STATUS
		ehb

		/* Clear the software interrupt in the core. */
		mfc0	s6, _CP0_CAUSE
		ins		s6, zero, 8, 1
		mtc0	s6, _CP0_CAUSE
		ehb

		/* Clear the interrupt in the interrupt controller. */
		la		s6, IFS0CLR
		addiu	s4, zero, 2
		sw		s4, (s6)

		jal		vTaskSwitchContext
		nop

		/* Clear the interrupt mask again.  The saved status value is still in s7. */
		mtc0	s7, _CP0_STATUS
		ehb

		/* Restore the stack pointer from the TCB. */
		la		s0, pxCurrentTCB
		lw		s0, (s0)
		lw		s5, (s0)

		
		lw		$1, 16(s5)
		lw		s0, 20(s5)
		lw		s1, 24(s5)
		lw		s2, 28(s5)
		lw		s3, 32(s5)
		lw		s4, 36(s5)

		/* s5 is loaded later. */
		lw		s6, 44(s5)
		lw		s7, 48(s5)
		lw		v0, 52(s5)
		lw		v1, 56(s5)
		lw		a0, 60(s5)
		lw		a1, 64(s5)
		lw		a2, 68(s5)
		lw		a3, 72(s5)
		lw		t0, 76(s5)
		lw		t1, 80(s5)
		lw		t2, 84(s5)
		lw		t3, 88(s5)
		lw		t4, 92(s5)
		lw		t5, 96(s5)
		lw		t6, 100(s5)
		lw		t7, 104(s5)
		lw		t8, 108(s5)
		lw		t9, 112(s5)
		lw		s8, 116(s5)
		lw		ra, 120(s5)

		/* Protect access to the k registers, and others. */
		di
		ehb

		/* Set nesting back to zero.  As the lowest priority interrupt this
		interrupt cannot have nested. */
		la		k0, uxInterruptNesting
		sw		zero, 0(k0)

		/* Switch back to use the real stack pointer. */
		add		sp, zero, s5

		/* Restore the real s5 value. */
		lw		s5, 40(sp)

		/* Pop the status and epc values. */
		lw		k1, portSTATUS_STACK_LOCATION(sp)
		lw		k0, portEPC_STACK_LOCATION(sp)

		/* Remove stack frame. */
		addiu	sp, sp, portCONTEXT_SIZE



	/* Restore the status and EPC registers and return */
	mtc0	k1, _CP0_STATUS
	mtc0 	k0, _CP0_EPC
	ehb
	eret
	nop

	.end	vPortYieldISR

Прошу помочь, а то я немного запутался

И самое худшее в этом я не могу понять где храниться адреса задач для переключения контекста

Согласно документации после xtaskCreate создаются два блока памяти - данных задачи, и стека задачи

Но не понятно куда привязать тогда pxCurrentTCB и "Task Frame"??

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 hours ago, addi II said:

Я только не совсем понял что Вы имели ввиду когда говорили что в yield происходит переключение стеков процессов при входе/выходе???

Именно то, чего у вас нет 🙂

При переключении контекста вы должны сохранить контекст текущей задачи в стек задачи, НЕ В ISR! Потом текущий указатель стека (пользовательской задачи) сохранить в поле в текущем TCB (на него указывает глобальная переменная pxCurrentTCB).

И только потом переключаться на стек ISR. Восстановление аналогично - загрузка пользовательского стека из TCB, восстановление контекста и возврат.

Переключение задач обеспечивается изменением этого самого pxCurrentTCB на другой TCB.

18 hours ago, addi II said:

Но не понятно куда привязать тогда pxCurrentTCB и "Task Frame"??

 

pxCurrentTCB включает пользовательский SP первым полем (если у вас нет поддержки SMP). А Task Frame - это и есть пользовательский стек. Создаётся при создании задачи и указатель на него (на верх или низ - зависит от архитектуры) кладётся в TCB, куда потом будет указывать pxCurrentTCB. Оттуда и стартует пользовательская задача (стековый фрейм для её запуска формируется при создании задачи). В дальнейшем он будет живым стеком задачи, и SP задачи (пока FreeRTOS обрабатывает прерывание) будет храниться в TCB

 

Для обработки вложенных прерываний нужно перед тем, как сохранять состояние понять откуда вас позвали - из пользовательского кода или из ISR. Если из пользовательского кода, то делаете всё как описанно выше. Если из ISR, то никаких стеков вообще переключать не нужно - сохраняете контекст прямо по текущему SP

18 hours ago, addi II said:

но не понятно что является причиной для инкрекментирования самой переменной

Вход в ISR. При первом входе (от пользователя) она будет 0, при дальнейших вложенных ISR - больше нуля.

При выходе из ISR - декрементируется

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

спасибо большое за ответ!

 

немного запутался в определениях

Как я понял Task Frame - это и есть пользовательский стек(сам буфер находящийся  по пользовательскому SP)
  А стек задачи = контекст текущей задачи =  текущий указатель стека =>pxCurrentTCB =  пользовательский SP

 

А есть ли сама структура Task Frame (хотелось бы знать где именно находиться адрес задач для переключения)?

И я не совсем понял что подразумевается под стеком ISR

Как я понял мне нужно в tickinterrupthandler добавить стек ISR или/и стек задачи

А в yield также?

И Вы не могли бы дать ссылку на пример отражающий вышесказанное

 

Спасибо!!! 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/19/2023 at 1:58 AM, addi II said:

А есть ли сама структура Task Frame

У каждого порта она своя, т.к. хранит регистры процессора, которые очевидно разные. И опять же, от порта зависит - есть для него явно описанная структура или нет.

On 2/19/2023 at 1:58 AM, addi II said:

И я не совсем понял что подразумевается под стеком ISR

 

То, на что указывает xISRStackTop

On 2/19/2023 at 1:58 AM, addi II said:

Как я понял мне нужно в tickinterrupthandler добавить стек ISR или/и стек задачи

 

Они у вас там есть. Но переключение между ними неправильное.

 

У FreeRTOS есть 2 вида стеков - стек задачи (он же пользовательский). Этот вид существует по 1 штуке на каждую задачу. Когда задача испольняется он активен (загружен в sp)

И стек прерываний - он всего один на всех, на нём запускаются прерывания самой FreeRTOS.

Соотвественно в обработчике прерываний вы должны сначала сохранить все регистры по текущему sp, а потом выяснить, на каком имеено стеке вы сейчас находитесь (анализом уровня вложенности прерываний в спеу. переменной). Если на пользовательском - то сохранить sp в текущий TCB и переключить sp на xISRStackTop. Если нет, то ничего делать не надо.

При выходе обратный процесс - переключение стека (если надо), восстановление регистров, возврат из прерывания

On 2/19/2023 at 1:58 AM, addi II said:

А в yield также?

 

yield ничем не отличается от обычного прерывания.

On 2/19/2023 at 1:58 AM, addi II said:

И Вы не могли бы дать ссылку на пример отражающий вышесказанное

 

ESP32 порт это делает - https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/bb6071e1df3168a64dc2ce79de8aa91b7995ba23/portable/ThirdParty/GCC/Xtensa_ESP32

PS. Он ещё и SMP - некоторые вещи могут быть не совсем такие, как в обычном FreeRTOS

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, получается от пика то же переключение не правильное(я приводил выше пример также)?

Я старался брать основу пример от пика.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, addi II said:

Спасибо, получается от пика то же переключение не правильное(я приводил выше пример также)?

 

Yield полностью соотвествует тому, что я написал.

Таймерное прерывание не соотвествует - оно не обновляет TCB, а вместо этого сохраняет пользовательский стек в глобальной переменной uxSavedTaskStackPointer

Это значит, что либо из него невозможно переключение задач (для этого нужно переключить TCB), либо код переключений в port.c берёт на себя манипуляцию по перекладыванию данных между TCB и uxSavedTaskStackPointer

В принципе это (невозможность переключения задач из прерываний) тоже вполне возможный вариант, но тогда порт должен обеспечить вызов yield прерывания (откуда и происходит переключения задач) сразу после окончания обработки всей цепочки вложенных прерываний. Для этого скорее всего потребуется аппаратная поддержка.

 

Я не знаком со всеми вариантами архитектур FreeRTOS, увы. Я разбирался с ESP32 и RISC-V. Про остальные могу только догадываться 😞

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понял спасибо, а принцип обновления TCB в таймерном прерывании должен быть таким же как для Yeild(у пика)?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

и еще вопрос, - по поводу SP и GP(global pointer) и их инициализацию

Нужно ли инициализировать Task Frame?, какую инициализацию должна делать vPortStartFirstTask ?

И как динамически передавать в ISR GP?(в примерах от пика этого нет, но gp FreeRTOS использует как минимум в инкременте тика)

 

 

Изменено пользователем addi II

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 hours ago, addi II said:

Понял спасибо, а принцип обновления TCB в таймерном прерывании должен быть таким же как для Yeild(у пика)?

Желательно. Иначе вам придётся устраивать танцы с бубном если захочется переключить задачи по таймеру (а оно обязательно захочестя)

5 hours ago, addi II said:

Нужно ли инициализировать Task Frame?, какую инициализацию должна делать vPortStartFirstTask ?

Она создаёт специальный фрейм, который при выходе из него (в момент выхода из прерывания) запустит main таска

5 hours ago, addi II said:

И как динамически передавать в ISR GP?(в примерах от пика этого нет, но gp FreeRTOS использует как минимум в инкременте тика)

 

Загружать явно в нужное значение (и не забыть его созранить и восстеновить в контексте)

Либо проинициализировать один раз в стартапе и больше НИГДЕ не трогать (вполне нормальный подход, если весь код ваш, и вы нигде руками gp не меняете)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...