Compatible cooperative multitasking – do a task_yield and actively give up the CPU.

The lowest level of the MSIP register of each HART is actually the MISP bit mapped to the MIP register, indicating whether soft interrupt occurs

void task_yield(a)
{
	/* trigger a machine-level software interrupt */
	intid = r_mhartid(); * (uint32_t*)CLINT_MSIP(id) = 1;
}
Copy the code

When we write 1 to the MSIP of this HART, it means that there is a soft interrupt to be processed. The specific process is shown in the figure below:

In this case, switch_to is essentially executed.

trap_vector:
	# save context(registers).
	csrrw	t6, mscratch, t6	# swap t6 and mscratch
	reg_save t6
	csrw	mscratch, t6

	# save mepc to context of current task
	csrr	a0, mepc
	sw	a0, 124(t6)

	# call the C trap handler in trap.c
	csrr	a0, mepc
	csrr	a1, mcause
	call	trap_handler

	# trap_handler will return the return address via a0.
	csrw	mepc, a0

	# restore context(registers).
	csrr	t6, mscratch
	reg_restore t6

	# return to whatever we were doing before trap.
	mret
Copy the code
  • The value of mscrach is the address of the context, and reg_save t6 is called to save the value of the generic register into the context.

.macro reg_save base
	sw ra, 0(\base)
	sw sp, 4(\base)
	sw gp, 8(\base)
	sw tp, 12(\base)
	sw t0, 16(\base)
	sw t1, 20(\base)
	sw t2, 24(\base)
	sw s0, 28(\base)
	sw s1, 32(\base)
	sw a0, 36(\base)
	sw a1, 40(\base)
	sw a2, 44(\base)
	sw a3, 48(\base)
	sw a4, 52(\base)
	sw a5, 56(\base)
	sw a6, 60(\base)
	sw a7, 64(\base)
	sw s2, 68(\base)
	sw s3, 72(\base)
	sw s4, 76(\base)
	sw s5, 80(\base)
	sw s6, 84(\base)
	sw s7, 88(\base)
	sw s8, 92(\base)
	sw s9, 96(\base)
	sw s10, 100(\base)
	sw s11, 104(\base)
	sw t3, 108(\base)
	sw t4, 112(\base)
	sw t5, 116(\base)
	sw t6, 120(\base)
.endm
Copy the code

Store is to store the data in memory to registers, so here we are writing the value of the universal register to memory.

  • Save the value of mePC to the PC of the context

    struct context { /* ignore x0 */ reg_t ra; . Omit... reg_t t6; // upon is trap frame // save the pc to run in next schedule cycle reg_t pc; // offset: 31 *4 = 124 };Copy the code

Make parameter 1: A0 = mePC, parameter 2: a1= McAuse, and then call trap_handler to handle interrupts and exceptions

reg_t trap_handler(reg_t epc, reg_t cause)
{
	reg_t return_pc = epc;	// Store the return address. This function returns the PC
	reg_t cause_code = cause & 0xfff;	// The cause of the interruption
	
	if (cause & 0x80000000) {	// The highest bit is 1, indicating that this is an interrupt
		/* Asynchronous trap - interrupt */
		switch (cause_code) {
		case 3:	// Software is interrupted
			uart_puts("software interruption! \n");
			/* * acknowledge the software interrupt by clearing * the MSIP bit in mip. */
			intid = r_mhartid(); * (uint32_t*)CLINT_MSIP(id) = 0;

			schedule();

			break;
		case 7:	// The timer is interrupted
			uart_puts("timer interruption! \n");
			timer_handler();
			break;
		case 11:	/ / uart interrupt
			uart_puts("external interruption! \n");
			external_interrupt_handler();
			break;
		default:	// Other interrupts
			uart_puts("unknown async exception! \n");
			break; }}else {	/ / exception
		/* Synchronous trap - exception */
		printf("Sync exceptions! , code = %d\n", cause_code);
		panic("OOPS! What can I do!");
		//return_pc += 4;
	}

	return return_pc;
}

Copy the code

If it is a timer interrupt, the timer_handler function is executed:

void timer_handler(a) 
{
	_tick++;
	printf("tick: %d\n", _tick);

	timer_load(TIMER_INTERVAL);

	schedule();
}
Copy the code

Essentially, schedule is executed.