Claudio Lanconelli's WEB pages - Updated 26 Aug 98

Lanconelli Open Systems

Home page ] Projects ] Useful links ] Who am I? ]

 ;***************************************************************************
 ;* MTHREADS.ASM
 ;*
 ;* AVR MINI THREADS v.1.02
 ;* ---> nanoKernel for the AVR AT90S1200
 ;*
 ;*
 ;* To try this code you need AvrTools from ATMEL, PonyProg and a simple
 ;* hardware (See schematics in PDF).
 ;*
 ;* Copyright (c) 1998 by Claudio Lanconelli
 ;* http://www.lancos.com
 ;*
 ;/ This program is free software; you can redistribute it and/or
 ;/ modify it under the terms of the GNU General Public License
 ;/ as published by the Free Software Foundation; either version2 of
 ;/ the License, or (at your option) any later version.
 ;/
 ;/ This program is distributed in the hope that it will be useful,
 ;/ but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;/ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 ;/ General Public License for more details.
 ;/
 ;/ You should have received a copy of the GNU General Public License
 ;/ along with this program (see COPYING); if not, write to the
 ;/ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ;***************************************************************************
 
 .DEVICE AT90S1200
 
 .include "macro.inc"
 .include "1200def.inc"
 
 
 ;Mini-threads handling
 ;notes:
 ; 1) only a cooperative multithreading is possible, the preemptive is not possible
 ; due to the hardware stack (impossible to read/write), and the PC is not accessible.
 ; A thread can go sleep to wait an event, and a thread can wake up another thread.
 ; A thread can also release the cpu to permit another thread to run (yield).
 ; The cooperative limit is restricted to AT90S1200, the 2313 have its own stack
 ; pointer (but the nanoKernel for the AT90S2313 is not ready yet).
 ; 2) any thread save the status register, so if you want the interrupts are enabled
 ; every time, you have to enable the interrupt in every thread.
 ;
 ; Follows an example of the use of mini-threads (three threads).
 ;-the first thread (main) wait for the timer interrupt. When it wakes up change the
 ; I/O pin of a port, depending on the status of a switch;
 ;-the second thread wait for an event sent from the third thread. When it wakes up
 ; change another I/O pin of the port.
 ;-the third thread wait for an external interrupt, then wakes up the second thread.
 ;
 
 ;Gestione mini-threads.
 ;note:
 ; 1) il multithreading NON puo` essere preemptive (purtroppo), ma solo cooperative.
 ; in quanto non e` possibile accedere ne` allo stack hardware, ne`
 ; al PC. In pratica un thread si puo` mettere in attesa di un evento,
 ; e un thread puo` risvegliare un altro thread. In piu` un thread
 ; puo` cedere il controllo momentaneamente ad un altro thread senza
 ; essere messo in attesa (yield). Con l'AT90S2313 la storia sarebbe diversa...
 ; 2) ogni thread possiede la sua copia del registro di stato. Di
 ; conseguenza se un thread disabilita gli interrupt. Gli interrupt
 ; resteranno disabilitati finche` e` in esecuzione lui, quando dara`
 ; il controllo ad un nuovo thread la abilitazione degli interrupt
 ; dipendera` dal flag 7-I del suo registro di stato.
 ;
 ;Come esempio implemento una gestione a 3 thread.
 ;-il primo (principale) attende un interrupt del timer, e quando avviene
 ; cambia lo stato di una porta di I/O, in base allo stato di un pulsante.
 ;-il secondo attende un evento da il terzo thread, quando avviene
 ; cambia lo stato di un'altra porta di I/O.
 ;-il terzo attende un interrupt esterno, e quando avviene risveglia
 ; il secondo.

.CSEG

;***************************************************************************
;* VARIABLE ASSIGNEMENTS
;***************************************************************************

	.def	SaveThr1Status	=	r0      ;status copy for thread 1
	.def	SaveThr2Status	=	r1	;status copy for thread 2
	.def	SaveThr3Status	=	r2	;status copy for thread 3
	.def	SaveStatus	=	r3	;status copy for interrupts

	.def    Main1		=	r21     ;Temp variable used by main program
	.def	MiscFlags	=	r31	;threads status flags + signal flags

	;flags bit definition
	.equ	Thread1Status =		0	; 1 --> ready, 0 --> waiting
	.equ	Thread2Status =		1
	.equ	Thread3Status =		2
;	.equ	Thread4Status =		3
	.equ	Thread1Signal =		4
	.equ	Thread2Signal =		5
	.equ	Thread3Signal =		6
;	.equ	Thread4Signal =		7

	.equ	THRSTATUSMASK =		7
	.equ	SIGNALMASK =		112


;***************************************************************************
;* Port Pin Assignements
;***************************************************************************

	;port D bit definitions (OUTPUTS)
	.equ	Led1	=	0
	.equ	Led2	=	1

	;port B bit definitions (INPUTS)
	.equ	Key1	=	1
	

;***************************************************************************
;* VECTORS
;***************************************************************************

		rjmp    RESET			;Reset Handle
		rjmp    INT0DRV			;Ext. interrupt request 0
		rjmp    TIMERDRV		;Timer


;***************************************************************************
;*
;* MAIN PROGRAM
;*
;***************************************************************************
;* INITIALIZATION
;***************************************************************************

RESET:  
		ldi	Main1, 0b01111011	;set port D bits to outputs (INT0 as an input)
		out	DDRD, Main1
		ldi	Main1, 0b00000111	;preset  output state (activate INT0 pull-up)
		out	PortD, Main1
		ldi	Main1, 0b00000000	;set port B to inputs
		out	DDRB, Main1
		ldi	Main1, 0b11111111	;turn on pullups on inputs
		out	PortB, Main1

		ldi	Main1, 5
		out	TCCR0, Main1		;set prescaler to CK/1024
		ldi	Main1, 32+2
		out	MCUCR, Main1		;enable sleep idle mode (ext int on falling edge)
		ldi	Main1, 2
		out	TIMSK, Main1		;enable timer interrupt 
		ldi	Main1, 64
		out	GIMSK, Main1		;enable external interrupt

		ldi	MiscFlags,THRSTATUSMASK 

		sei				;enable interrupts

		in	SaveThr1Status,SREG		;initialize status (interrupts enabled in all thread)
		mov	SaveThr2Status,SaveThr1Status
		mov	SaveThr3Status,SaveThr1Status
		
;=====================================
;THREAD 1
; main loop
THR1LOOP:
		rcall	WAIT1

		sbis	PinB,0
		rjmp	L001
		sbi	PortD,Led1
		rjmp	THR1LOOP
L001:
		cbi	PortD,Led1
		rjmp	THR1LOOP

;-----------------
WAIT1:
		;save status before switch to another thread
		in	SaveThr1Status,SREG
		sbrs	MiscFlags, Thread1Signal		;check for reeceived signal
		cbr	MiscFlags, EXP2(Thread1Status)		;thread1 in wait state
		cbr	MiscFlags, EXP2(Thread1Signal)		;reset signal
		rjmp	SCHEDULER2
THR1WAKEUP:
		ret


;=================================
;THREAD 2
; main loop
THR2LOOP:
		;thread 2 WAIT
		;we can't do a separate Wait subroutine for every thread. Note that
		;the stack is hardware, and not accessible.
		;save status before switch to another thread
		in	SaveThr2Status,SREG
		sbrs	MiscFlags, Thread2Signal		;check for received signal, if we have already
								;received the signal don't go sleep
		cbr	MiscFlags, EXP2(Thread2Status)		;thread2 in wait state
		cbr	MiscFlags, EXP2(Thread2Signal)
		rjmp	SCHEDULER3
THR2WAKEUP:
		brts	L005
		set
		sbi	PortD,Led2
		rjmp	THR2LOOP
L005:
		clt
		cbi	PortD,Led2
		rjmp	THR2LOOP


;===================================
;THREAD 3
; main loop
THR3LOOP:
		;thread 3 WAIT
		;save status before switch to another thread
		in	SaveThr3Status,SREG
		sbrs	MiscFlags, Thread3Signal		;check for received signal
		cbr	MiscFlags, EXP2(Thread3Status)		;thread3 in wait state
		cbr	MiscFlags, EXP2(Thread3Signal)
		rjmp	SCHEDULER1
THR3WAKEUP:
		sbr	MiscFlags, EXP2(Thread2Signal)		;wakeup thread 2
		rjmp	THR3LOOP


;---------------
;Interrupts routine just send a signal to a thread. If you need a VERY short interrupt
;response you can put some instructions here, then send the signal to the thread
;---------------
TIMERDRV:
		in	SaveStatus,SREG
		sbr	MiscFlags, EXP2(Thread1Signal)		;wakeup thread 1
		out	SREG,SaveStatus
		reti


;---------------
INT0DRV:
		in	SaveStatus,SREG
		sbr	MiscFlags, EXP2(Thread3Signal)		;wakeup thread 3
		out	SREG,SaveStatus
		reti


;-----------------
;This is the scheduler, the core of the nanoKernel. You can decide to go sleep
; when there's nothing to do. You can also put there the watchdog reset instruction.
;-----------------
SCHEDULER:
		sei						;be sure interrupts on here

		;if you don't want to go sleep remove next 3 instr
		tst	MiscFlags
		brne	SCHEDULER1
		sleep
SCHEDULER1:
		sbrc	MiscFlags, Thread1Status		;test if thread1 is ready
		rjmp	SCHEDRDY1
		;check for wakeup
		sbrs	MiscFlags, Thread1Signal		;test if thread1 received a signal
		rjmp	SCHEDULER2
		cbr	MiscFlags, EXP2(Thread1Signal)		;clear signal
SCHEDRDY1:
		;switch to thread 1
		sbr	MiscFlags, EXP2(Thread1Status)
		out	SREG,SaveThr1Status
		rjmp	THR1WAKEUP

SCHEDULER2:
		sbrc	MiscFlags, Thread2Status		;test if thread2 is ready
		rjmp	SCHEDRDY2
		;check for wakeup
		sbrs	MiscFlags, Thread2Signal		;test if thread1 received a signal
		rjmp	SCHEDULER3
		cbr	MiscFlags, EXP2(Thread2Signal)		;clear signal
SCHEDRDY2:
		;switch to thread 2
		sbr	MiscFlags, EXP2(Thread2Status)
		out	SREG,SaveThr2Status
		rjmp	THR2WAKEUP
SCHEDULER3:
		sbrc	MiscFlags, Thread3Status		;test if thread3 is ready
		rjmp	SCHEDRDY3
		;check for wakeup
		sbrs	MiscFlags, Thread3Signal		;test if thread1 received a signal
		rjmp	SCHEDULER
		cbr	MiscFlags, EXP2(Thread3Signal)		;clear signal
SCHEDRDY3:
		;switch to thread 3
		sbr	MiscFlags, EXP2(Thread3Status)
		out	SREG,SaveThr3Status
		rjmp	THR3WAKEUP