;*************************************************************************** ;* 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 ;*************************************************************************** .defSaveThr1Status=r0 ;status copy for thread 1 .defSaveThr2Status=r1;status copy for thread 2 .defSaveThr3Status=r2;status copy for thread 3 .defSaveStatus=r3;status copy for interrupts .def Main1=r21 ;Temp variable used by main program .defMiscFlags=r31;threads status flags + signal flags ;flags bit definition .equThread1Status =0; 1 --> ready, 0 --> waiting .equThread2Status =1 .equThread3Status =2 ;.equThread4Status =3 .equThread1Signal =4 .equThread2Signal =5 .equThread3Signal =6 ;.equThread4Signal =7 .equTHRSTATUSMASK =7 .equSIGNALMASK =112 ;*************************************************************************** ;* Port Pin Assignements ;*************************************************************************** ;port D bit definitions (OUTPUTS) .equLed1=0 .equLed2=1 ;port B bit definitions (INPUTS) .equKey1=1 ;*************************************************************************** ;* VECTORS ;*************************************************************************** rjmp RESET;Reset Handle rjmp INT0DRV;Ext. interrupt request 0 rjmp TIMERDRV;Timer ;*************************************************************************** ;* ;* MAIN PROGRAM ;* ;*************************************************************************** ;* INITIALIZATION ;*************************************************************************** RESET: ldiMain1, 0b01111011;set port D bits to outputs (INT0 as an input) outDDRD, Main1 ldiMain1, 0b00000111;preset output state (activate INT0 pull-up) outPortD, Main1 ldiMain1, 0b00000000;set port B to inputs outDDRB, Main1 ldiMain1, 0b11111111;turn on pullups on inputs outPortB, Main1 ldiMain1, 5 outTCCR0, Main1;set prescaler to CK/1024 ldiMain1, 32+2 outMCUCR, Main1;enable sleep idle mode (ext int on falling edge) ldiMain1, 2 outTIMSK, Main1;enable timer interrupt ldiMain1, 64 outGIMSK, Main1;enable external interrupt ldiMiscFlags,THRSTATUSMASK sei;enable interrupts inSaveThr1Status,SREG;initialize status (interrupts enabled in all thread) movSaveThr2Status,SaveThr1Status movSaveThr3Status,SaveThr1Status ;===================================== ;THREAD 1 ; main loop THR1LOOP: rcallWAIT1 sbisPinB,0 rjmpL001 sbiPortD,Led1 rjmpTHR1LOOP L001: cbiPortD,Led1 rjmpTHR1LOOP ;----------------- WAIT1: ;save status before switch to another thread inSaveThr1Status,SREG sbrsMiscFlags, Thread1Signal;check for reeceived signal cbrMiscFlags, EXP2(Thread1Status);thread1 in wait state cbrMiscFlags, EXP2(Thread1Signal);reset signal rjmpSCHEDULER2 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 inSaveThr2Status,SREG sbrsMiscFlags, Thread2Signal;check for received signal, if we have already ;received the signal don't go sleep cbrMiscFlags, EXP2(Thread2Status);thread2 in wait state cbrMiscFlags, EXP2(Thread2Signal) rjmpSCHEDULER3 THR2WAKEUP: brtsL005 set sbiPortD,Led2 rjmpTHR2LOOP L005: clt cbiPortD,Led2 rjmpTHR2LOOP ;=================================== ;THREAD 3 ; main loop THR3LOOP: ;thread 3 WAIT ;save status before switch to another thread inSaveThr3Status,SREG sbrsMiscFlags, Thread3Signal;check for received signal cbrMiscFlags, EXP2(Thread3Status);thread3 in wait state cbrMiscFlags, EXP2(Thread3Signal) rjmpSCHEDULER1 THR3WAKEUP: sbrMiscFlags, EXP2(Thread2Signal);wakeup thread 2 rjmpTHR3LOOP ;--------------- ;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: inSaveStatus,SREG sbrMiscFlags, EXP2(Thread1Signal);wakeup thread 1 outSREG,SaveStatus reti ;--------------- INT0DRV: inSaveStatus,SREG sbrMiscFlags, EXP2(Thread3Signal);wakeup thread 3 outSREG,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 tstMiscFlags brneSCHEDULER1 sleep SCHEDULER1: sbrcMiscFlags, Thread1Status;test if thread1 is ready rjmpSCHEDRDY1 ;check for wakeup sbrsMiscFlags, Thread1Signal;test if thread1 received a signal rjmpSCHEDULER2 cbrMiscFlags, EXP2(Thread1Signal);clear signal SCHEDRDY1: ;switch to thread 1 sbrMiscFlags, EXP2(Thread1Status) outSREG,SaveThr1Status rjmpTHR1WAKEUP SCHEDULER2: sbrcMiscFlags, Thread2Status;test if thread2 is ready rjmpSCHEDRDY2 ;check for wakeup sbrsMiscFlags, Thread2Signal;test if thread1 received a signal rjmpSCHEDULER3 cbrMiscFlags, EXP2(Thread2Signal);clear signal SCHEDRDY2: ;switch to thread 2 sbrMiscFlags, EXP2(Thread2Status) outSREG,SaveThr2Status rjmpTHR2WAKEUP SCHEDULER3: sbrcMiscFlags, Thread3Status;test if thread3 is ready rjmpSCHEDRDY3 ;check for wakeup sbrsMiscFlags, Thread3Signal;test if thread1 received a signal rjmpSCHEDULER cbrMiscFlags, EXP2(Thread3Signal);clear signal SCHEDRDY3: ;switch to thread 3 sbrMiscFlags, EXP2(Thread3Status) outSREG,SaveThr3Status rjmpTHR3WAKEUP