dnl (c)INRIA 1999 Christophe.Lavarenne@inria.fr dnl $Id: C40.m4x,v 1.1 2000/04/17 13:01:51 lavarenn Exp $ divert(-1) # SynDEx v5.1 generic executive kernel, adapted for TMS320C40. # (define(`MB') or use "m4 -DMB" for memory-big model) ifdef(`syndex.m4x_already_included',,`errprint( __file__:__line__: m4: syndex.m4x must be included before __file__! )m4exit(1)') # Because `C40' is both a `processorType_' and a `mediaType_', # C40.m4x is first included by the `processor_(C40,...)' macro, # and then included by the `thread_(C40,...)' macros. ifdef(`mediaType_', # included by `thread_', must work with `processorType_' `ifdef(__file__`_already_included',,`error_( mediaType_` media type is not compatible with 'processorType_` processor type' )m4exit(1)')', # otherwise included by `processor_' `ifdef( __file__`_already_included',`error_(`already included')m4exit(1)')') ifdef(__file__`_already_included',,` # `ifdef' closed near end of file... # ---------------------------------------------------------------------------- # This file defines all TARGET LANGUAGE DEPENDENT macros (prefixed by `basic') # which customize the generic executive macros defined in the syndex.m4x file. # SynDEx generates for each processor one macro-executive file starting with a # `processor_' macro taking as argument the processor type, which is stored in # the `processorType_' macro, and used to include the processorType_.m4x file. # Application specific macros may be conditionned by `processorType_'=`C40' # to generate different codes for different target processor types; # they may also be conditionned by `lang_' which may be defined # identically by different processorType_.m4x files: define(`lang_',`asmC40') ########## # COMMENTS # comment{Comments with {balanced} curly braces}comment # Assembler-style till-end-of-line comments: # The TARGET LANGUAGE DEPENDENT start-comment character (# for As) is given # as 3rd argument of the patsubst macro in the following definition: define(`comment_',`changequote({,})patsubst({{$1}},{^},{; })changequote') # use this macro to automatically add an LDP for memory big model: define(`B', `ifelse(index(`$*',`@'),-1,,`ifdef(`MB', ` LDP patsubst(`$*', `.*@\([^,]+\).*', `@\1') ')') $*') ############ # DATA TYPES # -------- # typedef_(name, size) defines a new type with its size in address units: # The TMS320C40 does not distinguish the float and double types. typedef_(`bool', 1) # MUST be defined typedef_(`char', 1) # SHOULD be defined typedef_(`int', 1) # SHOULD be defined typedef_(`float', 1) # SHOULD be defined # WARNING: The type double is not supported by the TMS320C40. ################### # MEMORY ALLOCATION # Data buffers for temporary signals, delays, windows and constants, # are located in the uninitialized .bss section (or in another unitialized # section named "syndexN", N being the "memBank" last optional macro argument) # and are referenced by the symbolic "label" given to the macros. # To pass a buffer address to a subroutine, it is faster to fetch it # from memory (LDP @pointer; LDI @pointer,reg) than to build it # with 16 bits immediates (LDP label,reg; SHL 16,reg; OR label,reg) # (it is a shame that "LDHI label,reg; OR label,reg" is not relocatable). # The constant pointers to data buffers are located in the .data section; # append "_p" to the data buffer label to obtain the pointer label. # ----------- # basicAlloc_(label, memoryBank) define(`basicAlloc_', `ifelse($2,, ` .bss $1,$1_size_*$1_type_()_size_', ` $1: .usect ".syndex$2", $1_size_*$1_type_()_size_') $1_p: .word $1') # ----------- # basicAlias_(newLabel,oldLabel[,offset=0]) define(`basicAlias_', `dnl `$1: .set $2`'ifelse($3,,,`+$3*$2_type_()_size_')' `$1_p: .word '$1') ################## # MEMORY TRANSFERS # ---------- # basicCopy_(destLabel,srceLabel,size) # size=1: 2+2/2+2 10xFFFF),1, `dnl LDHI eval((size_-2)>>16),RC ; (size_-2)>>16 OR eval((size_-2)&0FFFFh),RC ; (size_-2)&0xFFFF RPTS RC', `dnl else ; eval(size_-2)<=0xFFFF RPTS eval(size_-2) ; size_-2') STI R0,*AR0++ ||LDI *AR1++,R0 STI R0,*AR0++ ; flush pipe')popdef(`size_')') #################### # CONTROL STRUCTURES # -------- # basicIf_(bool, tagforElseOrEndif) define(`basicIf_', ` B(LDI @$1,R11) BZ Forward_$2') # ----------- # basicIfnot_(bool, tagForElseOrEndif) define(`basicIfnot_',` B(LDI @$1,R11) BNZ Forward_$2') # ---------- # basicElse_(tagFromIf, tagForEndif) define(`basicElse_', ` BU Forward_$2 Forward_$1:') # ----------- # basicEndif_(tagFromIfOrElse) define(`basicEndif_', ` Forward_$1:') # ---------- # basicLoop_(tagForEndloop) define(`basicLoop_', `ifdef(`NBITERATIONS', ` BU Forward_$1 ; fixed number of iterations .int NBITERATIONS ; iterations counter') Backward_$1:') # ------------- # basicEndloop_(tagFromLoop) define(`basicEndloop_', `ifdef(`NBITERATIONS', ` Forward_$1: ; fixed number of iterations B(LDI @Backward_$1-1,AR1) DBUD AR1,Backward_$1 STI AR1,@Backward_$1-1 NOP NOP', ` BU Backward_$1 ; infinite loop')') ############### # SYNCHRONIZERS # To synchronize main (priority=0) and communication sequences (priority=1). # semaphores_(...) ; allocate and initialize named semaphores n/0 def(`semaphores_', `number_($*) .bss sem_,$# dnl ; each semaphore is one word initially null') define(`number_',`ifelse($1,,,` $1 .set decr($#)`'number_(shift($*))')') # Pre0_(s) ; priority 1->0 precedence 1+1/1+1 def(`Pre0_', ` B(STIK 1,@sem_+$1) ; set semaphore $1') # Suc0_(s) ; priority 0<-1 precedence 3+1/6+1+5* def(`Suc0_', ` B(LDI @sem_+$1,R11) ; test semaphore $1 BZ $-1 ; until set by `Pre0_($1)' on interrupt STIK 0,@sem_+$1 ; reset it') # Pre1_(s) ; priority 0->1 or 1->1 precedence 5/8 def(`Pre1_', ` BD $+4 ; next 3 instructions cannot be interrupted LDP @sem_+$1 ; DO NOT condition this LDP NOT @sem_+$1,R11 ; change and test semaphore $1 and save it: STI R11,@sem_+$1 ; if null (2nd) call, otherwise (1st) continue: CALLZ Suc_$1_ ; label Suc_$1_ defined by `Suc1_($1)'') # Suc1_(s) ; priority 1<-0 or 1<-1 precedence 5/8 def(`Suc1_', ` BD $+4 ; next 3 instructions cannot be interrupted LDP @sem_+$1 ; DO NOT condition this LDP NOT @sem_+$1,R11 ; change and test semaphore $1 and save it: STI R11,@sem_+$1 ; if null (2nd) continue, otherwise (1st) return: RETSNZ ; return after CALL of `Pre1_' or of comINTshared_ Suc_$1_: ; target address of CALL in `Pre1_($1)'') ############### # MAIN sequence # ---------- # basicMain_() define(`basicMain_', ` ; ----------------------- main --------------------------------- ; This is the initial boot code for TMS320C40 programs, ; modified from TI boot.asm (found in rts.src v5.00B). ; The boot entry point for C programs is conventionnally called ; "_c_int00", it is recorded by the linker in the executable file ; header to be retrieved by the loader. ; The following actions are performed during boot: ; 1- allocate and initialize the C call stack ; 2- clear the bss section and perform C auto-initializations ; 3- initialize the status register. ; -------------------------------------------------------------- __stack .usect ".stack",0 ; stack size set by the linker .global __stack ; start address of C call stack .global cinit ; start address of C init tables .global .bss ; start address of C variables .global end ; end address of .bss section, set by linker .global _c_int00 ; linker standard entry point for C programs _c_int00: LDI 0,ST ; clear status: interrupts disabled LDP @.bss ; setup the DP register for the small model ; ------------ memory initializations ---------- 24/ LAJ InitBss ; R11= address of constants setup by the linker LDI R11,AR0 ; AR0= address of next word: LDI *AR0++,SP ; setup the initial C stack pointer LDI SP,FP ; and frame pointer (FP=AR3) .word __stack, .bss, end-.bss-1, cinit InitBss: LDI *AR0++,AR1 ; AR1=.bss LDI *AR0++,RC ; RC = bss section size-1 RPTS RC ; clear bss section STIK 0,*AR1++ LDI *AR0++,AR0 ; AR0= address of C init tables: CMPI -1,AR0 ; set to -1 by the linker for RAM model, BEQ InitDone ; where init is assumed to be done by the loader. LDI *AR0++,R0 ; get first count BU InitNext InitCopy: RPTS RC ; block copy STI R0,*AR1++ ; store previous initialization word ||LDI *AR0++,R0 ; while getting next initialization word CMPI 0,R0 ; auto-initialization table ends with a null count. InitNext: BNZD InitCopy: ; while count!=0: SUBI 1,R0,RC ; RC=count-1 LDI *AR0++,AR1 ; get dest address LDI *AR0++,R0 ; get first word ; BNZ InitCopy ; delayed branch occurs. InitDone: ; ----------- end memory initializations ------------ ; Status Register initialization: 0400h (CF=1) at reset ; +---------------- SetCond=0 do not set condition flags for AR0-7 ; |++-------------- PGIE,GIE=1 globally enable interrupts ; |||+------------- CC=1 Cache Cleared (CC always read 0) ; ||||+------------ CE=1 Cache Enabled ; |||||++---------- CF,PCF=0 Cache NOT Frozen ; |||||||+--------- RM=0 Repeat Mode ; ||||||||+-------- OVM=0 OVerflow Mode: do not saturate integer results ; |||||||||+++++++- LUF,LV,UF,N,Z,V,C condition flags LDI 0011100000000000b,ST ; cache cleared and enabled, interrupts enabled ; ----------- end main initializations -------------- ') # ------------- # basicEndmain_() define(`basicEndmain_', ` .global _exit ; C registered finalizations, see TIs exit.c in rts.src CALL _exit ; although this call usually never returns, RETI ; this RETI balances `_c_int00:' ; -------------- end of main ----------------- ') # ------------------------ # spawn_thread_(mediaName) ; spawn the pseudo-parallel execution of a com.seq. def(`spawn_thread_', ` CALL thread_$1_') # ----------------------- # basicThread_(mediaName) define(`basicThread_', ` thread_$1_: ; --------- start of Media $1 thread ----------') # -------------------------- # basicEndthread_(mediaName) define(`basicEndthread_', ` .bss $1_empty_,1 B(STIK 1,@$1_empty_) RETS ; return after CALL of `Pre1_' or of comINTshared_ ; ------------- end of Media $1 thread ----------------') # -------------------------- # wait_endthread_(mediaName) ; wait for com.seq. thread to terminate def(`wait_endthread_', ` B(LDI @$1_empty_,R11) BZ $-1') ######################### # COMMUNICATION sequences 2/8 ################################ # C40-C40 links: mediaType `C40' # WARNING: `mediaName_', the second argument of the `thread_' macro, # also passed as argument of other communication macros, is expected # to end with a digit, between 0 and 5, identifying the C40 com-port. ifelse(` # ------------------------ # Interrupts Vectors Table # allocated by the linker, initialized by `C40_ini_' .sect".vect" ; aligned on a 512 words boundary by the linker IVT_: .word $ ; +00: reserved .word RETI_ ; +01: NMI .word RETI_ ; +02: TINT0 .word RETI_,RETI_,RETI_,RETI_ ; +03-06: IIOF0 IIOF1 IIOF2 IIOF3 .space 6 ; +07-0C: unused .word RETI_,RETI_,RETI_,RETI_ ; +0D-10: ICFULL0 ICRDY0 OCRDY0 OCEMPTY0 .word RETI_,RETI_,RETI_,RETI_ ; +11-14: ICFULL1 ICRDY1 OCRDY1 OCEMPTY1 .word RETI_,RETI_,RETI_,RETI_ ; +15-18: ICFULL2 ICRDY2 OCRDY2 OCEMPTY2 .word RETI_,RETI_,RETI_,RETI_ ; +19-1C: ICFULL3 ICRDY3 OCRDY3 OCEMPTY3 .word RETI_,RETI_,RETI_,RETI_ ; +1D-20: ICFULL4 ICRDY4 OCRDY4 OCEMPTY4 .word RETI_,RETI_,RETI_,RETI_ ; +21-24: ICFULL5 ICRDY5 OCRDY5 OCEMPTY5 .word comINT0_,comINT1_,comINT2_ ; +25-27: DMAINT0 DMAINT1 DMAINT2 .word comINT3_,comINT4_,comINT5_ ; +28-2A: DMAINT3 DMAINT4 DMAINT5 .word RETI_ ; +2B: TINT1 .space 20 ; +2C-3F: unused .text # ----------- # DIE and IIE initialized by `C40_ini_' ; DMA Interrupt Enable register initialization: 0 at reset ; +++-------------- DMA5 pri Write sync 001=OCRDY5 000=none ; |||+++----------- DMA5 aux Read sync 001=ICRDY5 010=IIOF0 ; ||||||+++-------- DMA4 pri Write sync 001=0CRDY4 011=IIOF1 ; |||||||||+++----- DMA4 aux Read sync 001=ICRDY4 100=IIOF2 ; ||||||||||||+++-- DMA3 pri Write sync 001=0CRDY3 101=IIOF3 ; |||||||||||||||+- DMA3 aux Read sync 001=ICRDY3 110=TIM0 LDHI 0010010010010010b,DIE ; enable all DMA synchro/commports ; ++--------------- DMA3 aux Read sync 001=ICRDY3 111=TIM1 ; ||+++------------ DMA2 pri Write sync 001=OCRDY2 ; |||||+++--------- DMA2 aux Read sync 001=ICRDY2 ; ||||||||++------- DMA1 pri Write sync 01=OCRDY1 10=IIOF3 00=none ; ||||||||||++----- DMA1 aux Read sync 01=ICRDY1 10=IIOF2 11=TIM0 ; ||||||||||||++--- DMA0 pri Write sync 01=OCRDY0 10=IIOF3 00=none ; ||||||||||||||++- DMA0 aux Read sync 01=ICRDY0 10=IIOF2 11=TIM0 OR 0100100101010101b,DIE ; Internal Interrupt Enable register initialization: 0 at reset ; +---------------- EINT1 ; |+++------------- EDMAINT5 EDMAINT4 EDMAINT3 ; ||||+++---------- EDMAINT2 EDMAINT1 EDMAINT0 ; |||||||++++------ EOCEMPTY EOCRDY EICRDY EICFULL channel 5 ; |||||||||||++++-- EOCEMPTY EOCRDY EICRDY EICFULL channel 4 ; |||||||||||||||+- EOCEMPTY channel 3 LDHI 0111111000000000b,IIE ; enable only DMA channels to interrupt CPU ; +++-------------- EOCRDY EICRDY EICFULL channel 3 ; |||++++---------- EOCEMPTY EOCRDY EICRDY EICFULL channel 2 ; |||||||++++------ EOCEMPTY EOCRDY EICRDY EICFULL channel 1 ; |||||||||||++++-- EOCEMPTY EOCRDY EICRDY EICFULL channel 0 ; |||||||||||||||+- EINT0 ; OR 0000000000000000b,IIE ') # ----------- # C40_shared_(mediaName,procrNames) define(`C40_shared_', `ifdef(`C40_shared_once',,`define(`C40_shared_once') ; ---------------- C40 links: shared code -------------------------- ; Each comport is served by a DMA channel in split/synch mode. ; As comports are never used in bidirectionnal mode, the same interrupt handler ; is used for all interrupts of the i-th comport, with AR0=001000i0h, ; and *+AR0(0A6h) (the unused link pointer) pointing to the pending instruction ; of the suspended communication step. ; ------------------------------------------------------------------ comINTshared_: ; 8+2/14+2 shared part of communication interrupts ; PUSH R11 ;| (already saved by comINTi_) ; PUSH AR0 ;| (already saved by comINTi_) PUSH AR1 ;| save the only used registers PUSH ST ;|`'ifdef(`MB', ` PUSH DP ;|') CALLU R11 ; resume communication sequence`'ifdef(`MB', ` POP DP ;|') POP ST ;| POP AR1 ;| restore saved registers POP AR0 ;| POP R11 ;| RETI_: RETI ; ----------------- ; `C40_send_' ; send s cells from address a through link i ; LAJ C40_send_shared ; LDI R11,AR1 ; AR1= address of next word: ; LDI *AR1++,AR0 ; AR0= DMAi registers base address ; LDI *AR1++,R11 ; get transfer start address ;.word DMAi_base_,a,s,DMAi_CRsend_ C40_send_shared: ; 7/10 shared part of `C40_send_' macro STI R11,*+AR0(1) ; set source address reg. LDI *AR1++,R11 ; get transfer word size STI R11,*+AR0(3) ; set primary count reg. LDI *AR1++,R11 ; get rstA+startP control STI R11,*+AR0(0) ; set control reg. STI AR1,*+AR0(6) ; address of pending instruction into pri-link reg. RETS ; return after CALL of `Pre1_' or of comINTshared_ ; ----------------- ; `C40_recv_' ; receive s cells from address a through link i ; LAJ C40_recv_shared ; LDI R11,AR1 ; AR1= address of next word: ; LDI *AR1++,AR0 ; AR0= DMAi registers base address ; LDI *AR1++,R11 ; get transfer start address ;.word DMAi_base_,a,s,DMAi_CRrecv_ C40_recv_shared: ; 7/10 shared part of `C40_recv_' macro STI R11,*+AR0(4) ; set destination address reg. LDI *AR1++,R11 ; get transfer word size STI R11,*+AR0(7) ; set auxiliary count reg. LDI *AR1++,R11 ; get rstP+startA control STI R11,*+AR0(0) ; set control reg. STI AR1,*+AR0(6) ; address of pending instruction into pri-link reg. RETS ; return after CALL of `Pre1_' or of comINTshared_ ; ------------------------------------------------------------------') dnl end of part shared by all 6 C40-C40 links pushdef(`CPid_', substr($1,eval(len($1)-1)))dnl ComPort identifier digit ifelse(translit(CPid_,012345),`',, `error_(`C40 media name "$1" does not end with a digit.')')dnl define(`AR0_', `*+AR0(eval($'`1+16*'CPid_`,16,3)h)')dnl `comINT'CPid_`_': ; 5/5 `DMA'CPid_ interrupt entry PUSH R11 ; save R11 BUD comINTshared_ ; shared part of communication interrupts PUSH AR0 ; save AR0 LDHI 10h,AR0 ; AR0= base address of comport/DMA registers LDI AR0_(0xA6),R11 ; R11= address of pending instruction ; BU comINTshared_ ; delayed branch occurs ; ------------------------------------------------------------------ `DMA'CPid_`_base_' .set eval(0x001000A0+16*CPid_,16,8)h dnl ; `DMA'CPid_ registers base address pushdef(`CR',eval(dnl DMA Channel Control Register (chap.9.3.1,p9.8) dnl +intP&A,+split$1,-autoinitP&A,+synchP&A,stopP&A/tcc=0 dnl CR.31 reserved P=pri(read memory), A=aux(write memory) 0<<30|dnl CR.30 RW priority mode: 0=rot 1=fix dnl CR.29-26 RO A&Pstatus: 0=held/read 1=held/write 2=res. 3=notHeld 0<<24|dnl CR.25-24 RW Astart: 0=rst 1=halt1 2=halt2 3=run 0<<22|dnl CR.23-22 RW Pstart: 0=rst 1=halt1 2=halt2 3=run dnl CR.21-20 RO A&Ptcint: 1 when transfer complete 3<<18|dnl CR.19-18 RW A&Ptcc: 0=disable 1=enable intPulse when tcint CPid_<<15|dnl CR.17-15 RW comport no, must be channel no when SplitMode=1 1<<14|dnl CR.14 RW SplitMode: 0=off 1=on 0<<12|dnl CR.13-12 RW A&PbitRev 0<<10|dnl CR.11-10 RW A&PautoInitSync 000C40D5h 0<< 8|dnl CR.9-8 RW A&PautoInitStatic 3<< 6|dnl CR.7-6 RW A&PsyncMode 1<< 4|dnl CR.5-4 RW AtransferMode: 0=nonStop 1=stop/tc=0 2=autoinit/tc=0 1<< 2|dnl CR.3-2 RW PtransferMode: 3=stop/tc=0+autoinit/start 1<< 0 dnl CR.1-0 RW DMA Pri: 0=CPU>DMA 1=rot 2=reserved 3=DMA>PRI ))dnl ; (P=Prim, A=Aux) split,+intP&A,-autoinitP&A,+synchP&A,+stopP&A/tcc=0 `DMA'CPid_`_CRini_' .set eval(CR,16,8)h ; +rstP&A `DMA'CPid_`_CRsend_' .set eval(CR|3<<22,16,8)h ; +rstA,startP `DMA'CPid_`_CRrecv_' .set eval(CR|3<<24,16,8)h ; +rstP,startA popdef(`CR')dnl ; ------------------------------------------------------------------ media$1_params_: .word $+1 ; 7/0 .word `comINT'CPid_`_' ; interrupt routine for `DMAINT'CPid_ .word `DMA'CPid_`_base_' ; `DMA'CPid_ registers base address .word `DMA'CPid_`_CRini_' ; `DMA'CPid_ initial control reg .word eval(2<<(CPid_+24),16,8)h ; `IIE.EDMAINT'CPid_ mask .word eval(ifelse(CPid_,0,0x05, CPid_,1,0x50, 0x900<<((CPid_-2)*6)),16,8)h dnl ; DIE mask: synch `DMA'CPid_ prim on `OCRDY'CPid_ and aux on `ICRDY'CPid_') # -------- # C40_ini_(mediaName) 15+1/15+1 define(`C40_ini_', ` B(LDI @media$1_params_,AR1) LDI *AR1++,R11 ; R11 = `DMA'CPid_ interrupt routine address LDEP IVTP,AR0 ; AR0 = Interrupt Vector Table base address STI R11,*+AR0(eval(0x25+CPid_,16,2)h) ; `DMAINT'CPid_ interrupt vector LDI *AR1++,AR0 ; AR0 = `DMA'CPid_ registers base address STIK 0,*+AR0(0) ; reset control reg, value 0 at reset LDI *AR1++,R11 STI R11,*+AR0(0) ; preset control reg STIK 1,*+AR0(2) ; preset srce index STIK 1,*+AR0(5) ; preset dest index OR *AR1++,IIE ; `IIE.EDMAINT'CPid_ enable `DMAINT'CPid_ OR *AR1++,DIE ; synch `DMA'CPid_ prim on `OCRDY'CPid_ and aux on `ICRDY'CPid_ ') # -------- # C40_end_(mediaName,procrNames) 5+1/5+1 define(`C40_end_', ` B(LDI `@media'CPid_`_params_',AR1) LDI *AR1++(3),AR0 ; base address of `DMA'CPid_ registers STIK 0,*+AR0(0) ; reset control reg ANDN *AR1++,IIE ; disable `DMAINT'CPid_ ANDN *AR1++,DIE ; `DMA'CPid_ synch = none popdef(`CPid_')') # --------- # C40_send_(bufferName, senderType,senderName {,receiverNames}) 8/14 define(`C40_send_', ` LAJ C40_send_shared LDI R11,AR1 ; AR1= address of next word: LDI *AR1++,AR0 ; AR0= `DMA'CPid_ registers base address LDI *AR1++,R11 ; get transfer start address .word `DMA'CPid_`_base_',$1,$1_size_*$1_type_()_size_,`DMA'CPid_`_CRsend_'') # --------- # C40_recv_(bufferName, senderType,senderName {,receiverNames}) 8/14 define(`C40_recv_', ` LAJ C40_recv_shared LDI R11,AR1 ; AR1= address of next word: LDI *AR1++,AR0 ; AR0= `DMA'CPid_ registers base address LDI *AR1++,R11 ; get transfer start address .word `DMA'CPid_`_base_',$1,$1_size_*$1_type_()_size_,`DMA'CPid_`_CRrecv_'') # --------- # C40_sync_(dataType,dataSize, senderType,senderName {,receiverNames}) # C40_sync_ is not supported, because medias of type C40 are point-to-point. unsupported_(`C40_sync_') # -------------------------------------------------- # Forward-Loader # Once booted through its parent link, this processor may forward boot code # (at the C40 ROM-bootloader format) from its parent link (see loadFrom_ macro) # to its direct descendant links (see loadDnto_ macro). # Each processor answers its parents a 1 to request another boot code to # forward, until it has finished with all its descendants and answers a 0, # in which case the parent may proceed to its next direct descendant. # The "root" processor has no parent and gets boot code from its mass memory; # here, it is not expected to be a C40. # The first executable (resp. the following executables) forwarded through each # link is received by the boot-loader (resp. the forward-loader firstly loaded) # of the processor across the link, therefore it (resp. they) must be forwarded # without its (resp. with their) preceding/framing 32-bits byte-size. # ---------------------- # C40_loadFrom_(procrName {,destMedia}) define(`C40_loadFrom_', ` LDI $#,R11 ; 1+number of destMedia loadFrom_NextMedia: ; CALLed from `loadDnto_' SUBI 1,R11 ; while remaining destMedia RETSNZ ; return after CALL (1st in `spawn_thread_'(mediaName_)) STIK 0,AR0_(0x42) ; request no more executables from parent ;.text 1 ;loadFrom_Req: ; STIK 1,AR0_(0x42) ; request another executable from parent ; RETS define(`loadFrom_Req_', `STIK 1,'AR0_(0x42))dnl ;loadFrom_Get: ; LDI AR0_(0x41),R0 ; get one word from parent ; RETS define(`loadFrom_Get_', `LDI 'AR0_(0x41)`,R0')dnl ;.text ') # ---------------------- # C40_loadDnto_(srceMedia {,procrName}) define(`C40_loadDnto_', ` LDHI 10h,AR0 ; peripheral I/O base address LDI 0,R1 ; NULL marks first time = boot-loader across the link loadDnto_$1_Next: ; CALL loadFrom_Req ; request next executable from parent loadFrom_Req_() ; request next executable from parent (inlined) ; CALL loadFrom_Get ; get 1st word from parent = executable byteSize loadFrom_Get_() ; get 1st word from parent = executable byteSize (inlined) CMPI 0,R1 BZ $+2 ; NULL if boot-loader STI R0,AR0_(0x42) ; forward byteSize to descendant forward-loader LSR 2,R0,RC ; wordSize = byteSize/4 SUBI 1,RC ; -1 for RPTB RPTB loadDnto_$1_GetReq-1 ; CALL loadFrom_Get ; get next word from parent loadFrom_Get_() ; get next word from parent (inlined) STI R0,AR0_(0x42) ; forward it to descendant loadDnto_$1_GetReq: LDI AR0_(0x41),R1 ; get request from descendant BNZ loadDnto_$1_Next ; NULL when download finished for this link CALL loadFrom_NextMedia ') ###################### # CHRONOMETRIC LOGGING ifelse(` # MACROS NOT YET SUPPORTED: # -------- # Chronos_(size) size=number of (label,date) records to allocate def(`Chronos_', ` .bss ChronoBuf_,$1*2 ; (label,date) records buffer, initially null .data .align ; Chrono__+1 and +2 on same page as Chrono__ for (1) Chrono__ ; chronometric (label,date) records buffer .word ChronoBuf_ ; current-record pointer, initialized .word ChronoBuf_ ; to wrap back to buffer start .word ChronoBuf_+$1*2; to check for buffer limit ChronoFreq_ .int 10000000 ; timer frequency (10MHz) .float 1.E-7 ; timer period (100ns=1/10MHz) .text ; LAJ ChronoLap__ ; R11= retAddr ; LDHI 10h,AR0 ; 00100024h = timer 0 counter register address ; LDI *+AR0(24h),R10 ; R10= date (current time) ; LDI $'`1,R9 ; R9 = 16bits label ChronoLap__: ; R11=retAddr R10=date R9=label B(LDI @Chrono__,AR0) ; AR0= current chrono record address STI R9,*AR0++ ; store label STI R10,*AR0++ ; store date BD R11 ; delayed return CMPI @Chrono__+2,AR0; if end of Chrono_limit_ reached: (1) LDIZ @Chrono__+1,AR0; wrap pointer to ChronoBuf_ (1) STI AR0,@Chrono__ ; store back next record address ; B R11 ; delayed return occurs') # ---------- # ChronoLap_(label) def(`ChronoLap_', ` LAJ ChronoLap__ ; R11= retAddr LDHI 10h,AR0 ; 00100024h = timer 0 counter register address LDI *+AR0(24h),R10 ; R10= date (current time) LDI $1,R9 ; R9 = 16bits label') # ------- # Chrono_() initialization def(`Chrono_', `pushtag(`chrono') ANDN 1,IIE ; disable timer 0 interrupt LDHI 10h,AR0 ; 00100020h = timer 0 registers base address LDI 2C2h,R11 ; run internal source, pulse, output TCLK=0 STI R11,*+AR0(20h) ; setup control register STIK -1,*+AR0(28h) ; setup period register, 2^32/10MHz = about 400 seconds STIK 0,*+AR0(24h) ; reset counter register') # OBSOLETE endChrono_ macro, RECORDS COLLECTION TO BE "HETEROGENIZED" # ------------ # endChrono_() finalization: date records rescaling and collection def(`endChrono_', `poptag(`chrono') LDHI 10h,AR0 ; AR0=00100000h comport/DMA/timer registers base B(LDI @tree__p,AR1) ; AR1= list base B(LDI @ChronoFreq_,R0); R0 = local timer frequency ChronoSync__: ;**** set offset from descendant to local time LDI *++AR1,AR7 ; AR7= descendant link comport input register address CMPI 0,AR7 ; null if no more descendant BEQ ChronoDiff__ ; until descendant list end STI R0,*+AR7(1) ; send ready signal to outPort BD ChronoSync__ LDI *+AR7(0),R1 ; R1 = wait for dated ping from inPort SUBI *+AR0(24h),R1 ; R1 = dated pong = ping-timer STI R1,*+AR7(1) ; return dated pong to outPort ChronoDiff__: ;**** get offset from local to parent time B(LDI @tree__p,AR1) LDI *AR1++,AR2 ; AR2= parent link comport input register address LDI *+AR2(0),R0 ; wait for ready signal from parent FLOAT R0,R3 ; R3 = parent timer frequency B(MPYF @ChronoFreq_+1,R3); R3 = ratio parent/local frequency LDI *+AR0(24h),R0 ; R0 = record ping date FLOAT R0,R0 ; \ MPYF R3,R0 ; | convert ping date, from local to parent time scale FIX R0,R0 ; / STI R0,*+AR2(1) ; send dated ping to parent LDI *+AR2(0),R1 ; R1 = wait for dated pong from parent SUBI *+AR0(24h),R0 ; R0 = negative time elapsed between ping and pong ASH -1,R0 ; divide by 2 FLOAT R0,R0 ; \ MPYF R3,R0 ; | convert elapsed time, from local to parent time scale FIX R0,R0 ; / SUBI R1,R0 ; R0 = dt, offset from local to parent time, parent scale ;**** dump oldest part from current to buffer limit B(LDI @Chrono__,AR7) ; AR7= oldest record pointer LDI *AR7++,R1 ; R1 = label, non-null if pointer wrapped, BZ ChronoWrap__ ; null if buffer not full of records ChronoDump1__: FLOAT *AR7++,R2 ; R2 = date CALL ChronoConv__ LDI *AR7++,R1 ; R1 = next label CMPI @Chrono__+2,AR7; until buffer limit crossed (1)(see `Chrono_') BLO ChronoDump1__ ChronoWrap__: ;**** dump newest part from buffer base to current LDI @Chrono__+1,AR7; AR7= start address of buffer (1) ChronoDump2__: LDI *AR7++,R1 ; R1 = label CMPI @Chrono__,AR7 ; until last record BHI ChronoNextDescendant__ FLOAT *AR7++,R2 ; R2 = date CALL ChronoConv__ B ChronoDump2__ ChronoConv__: ; R1=label R2=date R3=scale R0=offset STI R1,*+AR2(1) ; send label to parent MPYF R3,R2 ; R2 = date*scale FIX R2,R2 ADDI R0,R2 ; R2 = date*scale+dt STI R2,*+AR2(1) ; send date*scale+dt to parent RETS ChronoForward__: ;**** forward descendant dumps FLOAT *AR7,R2 ; R2 = date CALL ChronoConv__ ChronoForwardEnd__: LDI *AR7,R1 ; R1 = label BNZ ChronoForward__; until null label marks end of dump ChronoNextDescendant__: LDI *AR1++,AR7 ; AR7= descendant link comport input register address CMPI 0,AR7 ; null if no more descendant BNE ChronoForwardEnd__ ; until descendant list end STIK 0,*+AR2(1) ;**** mark end of dump with null label') END OF MACROS NOT YET SUPPORTED') ################## # Subroutine calls # For interfacing C functions with parameters passed by the stack. # Change these macros for other parameter passing models. # ------ # Cdecl_ generates the declaration of an separately C compiled function: # $1: return # $2: C function name (without the underscore prefix added by the assembler) # $n>2: argument (with `*' if passed by address) # Example m4 declaration with code generated in diversion 1 for `processor_': # Cdecl_(int,my_fun,int,int*,float) # | .global _my_fun ; int my_fun(int, int*, float); def(`Cdecl_', ` .global _$2 ; `$1 $2('shift(shift($*))`);'') # ------ # Ccall_ mimics the ANSI C declaration of a separately compiled function: # $1: return and