John Hansen on NXT firmware multitasking
Posted: 14 Oct 2010, 04:24
The following was originally posted by John Hansen of the now defunct nxtasy forums. I'm making it available here because it well worth preserving.
The standard NXT firmware executes 20 opcodes per thread each time the VM (command module) gets a chance to execute code. It is supposed to do that in less than 1 ms and then let the firmware round robin through all of the other firmware modules. That way every millisecond any of the modules in the firmware get a chance to update the things that they control, such as reading sensor values, updating motor state, sending/receiving I2C messages, etc... The enhanced firmware exposes the field in each "clump" or thread which is used to determine how many opcodes will execute on the current thread before yielding to another thread.
Now, depending on the kind of operations you are executing and how many threads you have in your application you may be able to cycle several times through all threads with each one executing 20 operations before the 1ms time has elapsed. Or you may not even make it through 20 operations on a single thread without exceeding the 1ms limit. Some operations are expensive while others are very fast.
The VM simply round robins through all of the threads on its "run queue". Changing the priority of a thread/task doesn't move it to the front of the queue. The last place the VM was in its round robin when it runs out of 1ms is where it will pick back up again the next time it gets a chance to execute VM code. In the standard firmware a thread will always execute its priority number of opcodes before the VM checks for the 1ms time limit unless one of the opcodes that is executed sets the Status to CLUMP_DONE, CLUMP_SUSPEND, or STOP_REQ, in which case the loop through the priority number of opcodes is ended early. A subroutine return opcode, for example, sets the Status to CLUMP_DONE. Attempting to acuire a mutex that is already acquired sets the Status to CLUMP_SUSPEND as does calling a subroutine. The Stop opcode sets the status to STOP_REQ.
In the 1.05 standard firmware a call to Wait was just a subroutine call to a busy loop. Because this suspends the current thread the VM would rotate the queue and execute up to priority opcodes on another running thread. The wait subroutine is added to the end of the run queue if it is not empty so other running threads will execute before the wait subroutine. In the 1.28 standard firmware and the enhanced NBC/NXC firmware version 1.05+ the Wait operation is a native opcode that suspends the current thread with a specified number of milliseconds that must elapse before the current thread is allowed to execute any more opcodes. In the standard 1.28 firmware there is no way to change the number of operations per thread. A global variable is used instead of a thread-specific field. I kept the thread-specific field in the enhanced 1.28 NBC/NXC firmware and you can still adjust the relative priority of a thread by setting the number of operations to execute before yielding. In the 1.28 firmwares the VM does a better job of enforcing the 1ms total time limit on VM code execution than the 1.0x firmwares did. It was easy, for example, with file IO operations for the 1.0x VM to go well past its alloted 1ms worth of time. In the enhanced firmware and the 1.2x firmwares there is a check after every opcode executes whether there is time left in the 1ms chunk to execute another operation or not.