Claudio Lanconelli's WEB pages - Updated 26 Aug 98
;*************************************************************************** ;* 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 ;* e-mail: lanconel@cs.unibo.it ;* WWW: 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