Multi-tasking

Questions about the BASICtools and MakeItC
Post Reply
basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Multi-tasking

Post by basicchip »

While I tend to favor interrupt drive or callbacks, both are possible in BASIC. One user seemed determine to get multitasking into the BASIC. After some thinking on my part, I could see interrupts could be used to switch between tasks. So I took a look at a simple example. Using timer interrupts and separate stacks for each task, it is possible to time share them. These tasks are ones that never end. For tasks that do end, they can still be driven by interrupts and will have a higher priority if only because the time shared tasks are not blocking interrupts.

This was a first, I posted a blog entry before the forum entry.

https://www.coridium.us/coridium/blog/m ... ic-why-not

Usually deeper details will be at the forum, and discussion can happen here with users. The blog is driven by markdown, so I can post user comments to the blog, if they are in markdown.

This task switch was done with 4 lines of inline assembly, needed to copy the SP or to set the SP to the new context.

Source for the simple example below, inline ASM macros here viewtopic.php?f=9&t=1303 --

Code: Select all

#ifdef LPC1756
 #include "LPC17xx.bas"
#elif defined LPC11U37
 #include "LPC11U3x.bas"
#elif defined LPC54102
 #include "LPC54102.bas"
#else
 #error this CPU not supported yet
#endif

#include "ASMinlineBASIC.bas"

#define MAX_TASK	4
#define INT_FRAME	(17-1)			' 8 registers + lr INTSUB pushes + 8 registers of INT

#define PT_PC		(INT_FRAME-1)

dim taskSP 	(MAX_TASK-1)			' saved SP here
dim taskTIME(MAX_TASK-1)			' in msec

task_num = 0		' cycles through the tasks

INTERRUPT SUB TIMER1IRQ
	dim x			
	
	if (task_num = -1) then	
		' special case on startup -- don't save the main SP
	else
		x = ADDRESSOF(taskSP) + (task_num<<2)
		ASM_MOVRloRhi(0,REG_SP)		' r0 now has value of SP
		ASM_STRoff(0,7,0)			' save it in taskSP(tasknum)
	endif
	
	task_num += 1
	if task_num = MAX_TASK then task_num = 0
	
	x = ADDRESSOF(taskSP) + (task_num<<2)
	ASM_LDRoff(0,7,0)				' get it from taskSP(tasknum)
	ASM_MOVRhiRlo(REG_SP,0)			' move SP to next task stack

	T1_TC  = 0      				' otherwise might be past MR0
	T1_MR0 = taskTIME(task_num)-1	' set the time alotted to the task
	
	T1_IR = 1	      ' Clear interrupt	
	
ENDSUB

SUB ON_TIMER ( max_cnt, dothis )		' max_cnt in msec
#if defined LPC1756 || defined LPC1751
	TIMER1_ISR   = dothis + 1              	'set function of VIC   -- need the +1 for Thumb operation
	T1_PR  = 24999                         	'1 ms prescale
	VICIntEnable or= (1<<2)  				'Enable interrupt
#elif defined LPC11U37
	TIMER1_ISR   = dothis + 1              	'set function of VIC   -- need the +1 for Thumb operation
	SYSCON_SYSAHBCLKCTRL OR= (1<<10)		' enable TIMER1
	T1_PR  = 24999                         	'1 ms prescale
	VICIntEnable OR= (1<<TIMER1_IRQn)  	 	'Enable interrupt
#elif defined LPC54102
	TIMER1_ISR   = dothis + 1              	'set function of VIC   -- need the +1 for Thumb operation
	ASYNCAPBCLKCTRLSET = SYSCON_CLOCK_TIMER1		' enable TIMER1
	T1_PR  = 12499                         	'1 ms prescale
	VICIntEnSet0 = (1<<TIMER1_IRQn) 	 	'Enable interrupt
#else
	#warning -- need special setup for this CPU
#endif

	T1_MR0 = max_cnt-1 		' set up match number of ms
	T1_MCR = 3      		' Interrupt and Reset on MR0
	T1_IR  = 1      		' clear interrupt
	T1_TC  = 0      		' clear timer counter
	
	T1_TCR = 1      		' TIMER1 Enable
	
ENDSUB


#define STACK_SZ	127	' probably don't need this much room -- each task needs a seperate stack
DIM stack0 (STACK_SZ)		
DIM stack1 (STACK_SZ)		
DIM stack2 (STACK_SZ)		
DIM stack3 (STACK_SZ)		

'	tasks can not share subroutines/functions that have parameters or locals, as those are NOT re-entrant



sub task0
	while 1					' tasks SHALL NEVER return -- equivalent of crossing the streams
		IO(6)=0
		IO(6)=1
	loop
							' if we do finish early someday -- a software interrupt could be used ???
end sub

sub task1
	while 1
		IO(7)=0
		IO(7)=1
	loop
end sub

sub task2
	while 1
		IO(8)=0		
		IO(8)=1
	loop
end sub

sub task3
	while 1
		IO(9)=0
		IO(9)=1
	loop
end sub


main:
	
	task_num = -1		' special case for initial task
	
	taskTIME(0) = 50		' time in msec for each task
	taskTIME(1) = 10
	taskTIME(2) = 15
	taskTIME(3) = 20
	
	taskSP(0) = ADDRESSOF(stack0) + (STACK_SZ-INT_FRAME)*4 
	stack0(STACK_SZ)   = &H61000000					'  seems to be initial PSR
	stack0(STACK_SZ-1) = ADDRESSOF(task0) + 1
	stack0(STACK_SZ-8) = &HFFFFFFF9					' INTERRUPT SUB pushes LR here
		
	taskSP(1) = ADDRESSOF(stack1) + (STACK_SZ-INT_FRAME)*4 
	stack1(STACK_SZ)   = &H61000000					'  seems to be initial PSR
	stack1(STACK_SZ-1) = ADDRESSOF(task1) + 1
	stack1(STACK_SZ-8) = &HFFFFFFF9					' INTERRUPT SUB pushes LR here
		
	taskSP(2) = ADDRESSOF(stack2) + (STACK_SZ-INT_FRAME)*4 
	stack2(STACK_SZ)   = &H61000000					'  seems to be initial PSR
	stack2(STACK_SZ-1) = ADDRESSOF(task2) + 1
	stack2(STACK_SZ-8) = &HFFFFFFF9					' INTERRUPT SUB pushes LR here
		
	taskSP(3) = ADDRESSOF(stack3) + (STACK_SZ-INT_FRAME)*4 
	stack3(STACK_SZ)   = &H61000000					'  seems to be initial PSR
	stack3(STACK_SZ-1) = ADDRESSOF(task3) + 1
	stack3(STACK_SZ-8) = &HFFFFFFF9					' INTERRUPT SUB pushes LR here

	ASYNCAPBCLKCTRLSET = SYSCON_CLOCK_TIMER1		' enable TIMER1	
	
	ON_TIMER (taskTIME(0), ADDRESSOF(TIMER1IRQ))
		
	ASYNCAPBCLKCTRLSET = SYSCON_CLOCK_TIMER1		' enable TIMER1	
	print hex(ASYNCAPBCLKCTRL), hex(SYSCON_CLOCK_TIMER1)
	
	while 1			' never comes back here after the first timer IRQ
	loop



basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Re: Multi-tasking

Post by basicchip »

This was the first pass, waiting to get feedback.

The task0 could be setup to use the main stack, eliminating stack0, and by disabling the timer, it could return to the main: or wherever it was called from.

Not sure this is a real necessary improvement, but could be done. For simplicity push ASM instructions would be needed.

BryantRen
Posts: 1
Joined: Thu Mar 21, 2019 10:00 am

Re: Multi-tasking

Post by BryantRen »

Did you play around with this idea any further, Basicchip?
Here are some strong lightweight wheelchairs that are affordable too.

TodWulff
Posts: 70
Joined: Fri Oct 19, 2012 4:03 am
Location: The Mitten State - Shores of Lake Huron

Re: Multi-tasking

Post by TodWulff »

BryantRen wrote:
Fri Mar 22, 2019 11:43 am
Did you play around with this idea any further, Basicchip?
Good day, BryantRen.

Sir B has been very helpful in assisting me/us with implementing a working solution.

After a couple of iterations of context switch code massages, it seems to be quite stable.

I'm working on a couple of projects that will lean on what I am coining as ABmt (ArmBasic MultiTasking).

Those projects will eventually include:
  • A pendant controller (think VR Headset Control) for a small boat's trolling motor.
  • Touch Screen LCD Weather Station (with LORA/SIGFOX connectivity to remote sensors)
  • Wireless Model Rocket Launcher
  • A SDR (Software Defined Radio)
  • ADS-B sniffer (the day job is Aerospace/Defense, so this rings true on the value scale)
  • IMU for a maritime autopilot (building on the controller above) using BST goods
  • ... and more, until I am no longer vertical and ventilating
ABmt is what I perceive as a natural progression of the initial implementation that enables task addition with low-effort compile-time self-configuration/ingestion of task code, etc. It is a precursor to a proper implementation of an RTOS, ABrtos (a.k.a. Project Storm), where a formal scheduler will be implemented to enable proper run-time task sequencing/scheduling via some form of structured approach (rate monotonic, preemptive, cooperative, ...). ABmt implements a fixed hybrid form of Cooperative/Preemptive, where tasks yield their time slice when they haven't anything to accomplish during the current pass, with unilateral/preemptive task switching in the event a task doesn't yield prior to its time slice being consumed.

Anyways, that is getting down into the weeds a bit. Here is a screen shot of an early implementation of ABmt:

Image

There is a dprintf debug serial stream that is bitbanged and is able to emit info at some 4.4Mbps via a gpio on a Coridium 54102 target (bit banged to mitigate impact of interrupts inside an ISR). The emissions in the image are highly verbose, just for testing/dev purposes.

The UI that you're seeing in the image is a project I've been working on, with help from BasicChip and Olzeke51. As a small bit of a spoiler alert, this will be a topic of a blog post where it is formally introduced, with links to an introductory video(s), and instantiation of a thread herein to offer it up to the user community.

I've gotta run for the day job. Take care and have a great day.

-t

olzeke51
Posts: 414
Joined: Sat May 17, 2014 4:22 pm
Location: South Carolina

Re: Multi-tasking - JUST CURIOUS

Post by olzeke51 »

OFF HAND - - - Do you know why there are so many 'Power ON' messages on a multi-tasking program
when you use BT<button> to <STOP> it ??. . . . JUST CURIOUS -( not concerned about the number of blank lines)
My BT window copy::
'
83400 34794 83460 34794 83520 34794 83580 34858 <<< PROGRAM DATA

*****PUSHED THE <STOP> BUTTON IN BT

Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37

.... [5 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37

.... [5 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37
[2 blank lines]
toggleReset <<< BT TRIGGERED <<< BT TRIGGERED <<< BT TRIGGERED <<< BT TRIGGERED
[4 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37
[4 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37
[6 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37
[5 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37
[7 blank lines]
Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37

.................... this part is normal
stopButton <<***SECOND <STOP> BUTTON A COUPLE OF SECONDS LATER
[5 blank lines]
toggleReset





Welcome to ARMbasic Kernel[8.40c] with Floating Point Copyright 2018, Coridium Corp.
for the LPC11U37

basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Re: Multi-tasking

Post by basicchip »

Not seeing that here with the stock Multitask example. But looks like another interrupt active, though I am surprise it would act quite like that.

basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Re: Multi-tasking

Post by basicchip »

Multitasking like interrupts have some limitations.

You should not do WAIT or WAITMICRO as the task switch might occur in the middle of the wait. But you can read the TIMER and look for elapsed time up to 35 seconds. It is beat to exit the task rather than waiting in a loop.

Code: Select all

sub task3
	dim startTime
	dim toggle
	toggle = 0
	startTime = TIMER
	while 1
		if TIMER - startTime > 1000000 then
			toggle = not toggle
			startTime = TIMER
		endif
		IO(1)=toggle
		VICIntSetPend0 = (1<<TIMER1_IRQn) ' force Timer 1. Interrupt -- ending this task slot
	loop              ' when this task is executed again it starts here inside the while 1 loop
end sub
PRINT and PRINTF are not re-entrant so should be avoided in more than one task, without disabling the task switch interrupt.

While TXD would work, multiple tasks may jumble the output.

There is only one STRING accumulator, so if more than one task are doing STRING operations, you need to disable interrupts around that operation.

Floating point operations are also not re-entrant so should not be done in interrupts, or in multitasking you need to disable the task switch interrupt while they are being done.

For multitasking using TIMER1 for task switch --

Code: Select all

  VICIntEnClear0 = (1<<TIMER1_IRQn) 	 	'Disable interrupt
	
  ' ... code that can not be interrupted -- print, prtinf, sprintf, string, floating point
  
  VICIntEnSet0 = (1<<TIMER1_IRQn) 	 	'Enable interrupt

olzeke51
Posts: 414
Joined: Sat May 17, 2014 4:22 pm
Location: South Carolina

Re: Multi-tasking - STOP command issue

Post by olzeke51 »

Hey Y'all
I was trying to debug a 7-segment display Task0 that shows/displays junk in a MultiTask.bas program,
so I used a STOP command to be able to dump the "Tools/Debug/Variables"
'
I assumed (without any/much thought) that AB would stop processing program flow -
but the MT interrupt for time slice would just keep changing the stack pointers
(and such) but not 'DO' (continue running) the TaskX_Subroutines. ARMBasic was never
designed to be a multitasking/RTOS but Bruce has done a fine job of providing a framework
for doing multiple tasks - [it is a different mindset!!] - btw - Thanks
.
I am just pointing out the situation...just something to be aware of while you are debugging a program..
Also be aware that after several iterations of modifications;
the board (or BasicTools) gets confused and you could get incomplete information- reboot both of them.
The STOP command works but not sure of other effects.

will try later with PRINTs — going to do one big loop with IF/THEN flags that will
cause some GOSUBs to handle the events - the flags are generated by a seperate
Interrupt Service Routine. The PRINTs need to be short as there are several tasks
all using Interrupts and other resources such as RAM and IO.
'
FWIW - a tight loop; checking for an input would be the normal interactive way
of getting operator data via a terminal or touch-screen; not the STOP command!!!!!

Code: Select all

Print "Press SW7 to proceed"
While IN(51) <> 0		'SW7/enter/
     flag = 0
loop
flag = 1
:
Hope this helps save someone some time
Gary Olzeke
stop_err.jpg
stop_err.jpg (192.19 KiB) Viewed 18482 times

basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Re: Multi-tasking

Post by basicchip »

You can always turn off the task switch interrupt, if you want to stop the other processes.

I have not tried it, but variable dumps will probably work in the STOP

Post Reply