Multi-tasking

Questions about the BASICtools and MakeItC
Post Reply
basicchip
Posts: 944
Joined: Fri Oct 19, 2012 2:39 am
Location: Lake Tahoe, CA
Contact:

Multi-tasking

Post by basicchip » Sun Jan 06, 2019 6:03 pm

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: 944
Joined: Fri Oct 19, 2012 2:39 am
Location: Lake Tahoe, CA
Contact:

Re: Multi-tasking

Post by basicchip » Mon Jan 07, 2019 1:51 am

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 » Fri Mar 22, 2019 11:43 am

Did you play around with this idea any further, Basicchip?

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

Re: Multi-tasking

Post by TodWulff » Fri Mar 22, 2019 2:50 pm

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

Post Reply