Shop OBEX P1 Docs P2 Docs Learn Events
What happened to GETINT? — Parallax Forums

What happened to GETINT?

Did I miss something? What happened to the GETINT instruction? It is mentioned in the P2 documentation, but it seems to no longer exist. PNUT doesn't know about it.

Is there a replacement instruction to allow us to get the interrupt status?

Comments

  • evanhevanh Posts: 15,126
    I don't know anything about its uses but it was added in v4 and removed in v32.
  • Cluso99Cluso99 Posts: 18,066
    Think it might be part of the POLLxxx group
  • evanhevanh Posts: 15,126
    Looking at just the encodings it's become GETBRK and COGBRK.
    v31
    EEEE 1101011 000 DDDDDDDDD 000110100        GETPTR  D
    EEEE 1101011 000 DDDDDDDDD 000110101        GETINT  D
    EEEE 1101011 00L DDDDDDDDD 000110110        SETBRK  D/#
    EEEE 1101011 00L DDDDDDDDD 000110111        SETLUTS D/#
    
    v32
    EEEE 1101011 000 DDDDDDDDD 000110100        GETPTR  D
    EEEE 1101011 CZ0 DDDDDDDDD 000110101    *   GETBRK  D           {WC/WZ/WCZ}
    EEEE 1101011 00L DDDDDDDDD 000110101    *   COGBRK  D
    EEEE 1101011 00L DDDDDDDDD 000110110    *   BRK     D/#
    EEEE 1101011 00L DDDDDDDDD 000110111        SETLUTS D/#
    
  • RossHRossH Posts: 5,336
    How annoying. Without knowing whether we are in an interrupt routine, I can't use SKIPF in any generated code, because it might end up in an interrupt service routine. :(

    I have temporarily removed it.

    Attached is my first attempt at adding C interrupts. As usual, compiled for the P2 EVAL board, serial interface, 230400 baud.

    I have included my proposed interrupt header file (which still mentions "getint") ...
    #ifndef __PROPELLER_INTERRUPTS__H
    #define __PROPELLER_INTERRUPTS__H
    
    /*
     * define the type of an interrupt function. Such a function must have no 
     * arguments, and it must return - but it cannot return a value. It must be 
     * set as an interrupt using one of the _set_int_x functions.
     */
    typedef void (* _interrupt)(void);
    
    /*
     * define the size (in LONGs) of an interrupt block - the block is used to
     * save the registers of the executing C program or thread during the servicing
     * of the interrupt. The block should be long aligned.
     */
    #define INT_BLOCK_SIZE 30
    
    /*
     * define the minimum size (in LONGs) of the interrupt stack - the top of this
     * space is used for the interrupt block, and the rest is used as stack space.
     */
    #define MIN_INT_STACK_SIZE (INT_BLOCK_SIZE + 10)
    
    /*
     * define the possible interrupt sources
     */
    enum int_src {
       OFF=0, // interrupts off, or poll/wait for any interrupt 1/2/3
       CT1,   // CT-passed-CT1, established by _set_ct1
       CT2,   // CT-passed-CT2, established by _set_ct2
       CT3,   // CT-passed-CT3, established by _set_ct3
       SE1,   // SE1 event occurred, established by _set_se1    
       SE2,   // SE2 event occurred, established by _set_se2
       SE3,   // SE3 event occurred, established by _set_se3
       SE4,   // SE4 event occurred, established by _set_se4
       PAT,   // Pin pattern match or mismatch occurred, established by _set_pat
       FBW,   // Hub RAM FIFO interface wrapped and reloaded
       XMT,   // Streamer is ready for another command
       XFI,   // Streamer ran out of commands
       XRO,   // Streamer NCO rolled over
       XRL,   // Streamer read location $1FF of lookup RAM
       ATN,   // Attention requested by other cog(s)
       QMT    // CORDIC read but no results available
    };
    
    /*
     * set up initial counter interrupt source (after this, use _add_CTx)
     */
    void _set_CT1(unsigned long CT1);
    void _set_CT2(unsigned long CT2);
    void _set_CT3(unsigned long CT3);
    
    /*
     * add to counter interrupt source (initially set up by _set_CTx)
     */
    void _add_CT1(unsigned long CT1);
    void _add_CT2(unsigned long CT2);
    void _add_CT3(unsigned long CT3);
    
    /*
     * set up pattern interrupt source (A = 0, B = 1, EQ = 0, NE = 1)
     */
    void _set_PAT(int A_or_B, int EQ_OR_NE, unsigned long MASK, unsigned long MATCH);
    
    /*
     * set up configurable interrupt sources
     */
    void _set_SE1(unsigned long SE1);
    void _set_SE2(unsigned long SE2);
    void _set_SE3(unsigned long SE3);
    
    /*
     * nix (cancel) an interrupt
     */
    void _nix_int_1();
    void _nix_int_2();
    void _nix_int_3();
    
    /*
     * simulate (trigger) an interrupt
     */
    void _sim_int_1();
    void _sim_int_2();
    void _sim_int_3();
    
    /*
     * set up (establish) an interrupt service function
     */
    void _set_int_1(enum int_src SRC, _interrupt SVC, void *stack);
    void _set_int_2(enum int_src SRC, _interrupt SVC, void *stack);
    void _set_int_3(enum int_src SRC, _interrupt SVC, void *stack);
    
    /*
     * clear (remove) an interrupt service function
     */
    void _clr_int_1();
    void _clr_int_2();
    void _clr_int_3();
    
    /* 
     * allow or stall all interrupts
     */
    void _allow_int();
    void _stall_int();
    
    /*
     * get interrupt status
     */
    unsigned long _get_int();
    
    /*
     * poll for interrupt XXX
     * returns 0 if event has not occured, 1 if event has occurred
     */
    unsigned long _poll_ANY(); 
    unsigned long _poll_CT1(); 
    unsigned long _poll_CT2(); 
    unsigned long _poll_CT3(); 
    unsigned long _poll_SE1(); 
    unsigned long _poll_SE2(); 
    unsigned long _poll_SE3(); 
    unsigned long _poll_SE4(); 
    unsigned long _poll_PAT(); 
    unsigned long _poll_FBW(); 
    unsigned long _poll_XMT(); 
    unsigned long _poll_XFI(); 
    unsigned long _poll_XRO(); 
    unsigned long _poll_XRL(); 
    unsigned long _poll_ATN(); 
    unsigned long _poll_QMT(); 
    
    /*
     * wait for interrupt XXX
     * returns with 0 if timeout occurred, or 1 if event occurred
     */
    unsigned long _wait_ANY(unsigned long timeout);
    unsigned long _wait_CT1(unsigned long timeout);
    unsigned long _wait_CT2(unsigned long timeout);
    unsigned long _wait_CT3(unsigned long timeout);
    unsigned long _wait_SE1(unsigned long timeout);
    unsigned long _wait_SE2(unsigned long timeout);
    unsigned long _wait_SE3(unsigned long timeout);
    unsigned long _wait_SE4(unsigned long timeout);
    unsigned long _wait_PAT(unsigned long timeout);
    unsigned long _wait_FBW(unsigned long timeout);
    unsigned long _wait_XMT(unsigned long timeout);
    unsigned long _wait_XFI(unsigned long timeout);
    unsigned long _wait_XRO(unsigned long timeout);
    unsigned long _wait_XRL(unsigned long timeout);
    unsigned long _wait_ATN(unsigned long timeout);
    
    /*
     * request the attention of all the cogs specified in 'cogs' bits 0 .. 15
     */
    _cog_ATN(unsigned long cogs);
    
    #endif
    

  • Use the WC and WZ effects to change GETBRK's results.
                    getbrk  val wc     'get skip call depth
                    getbrk  val wz     'get skip pattern
                    getbrk  val wcz    'get interrupt status
    
  • evanhevanh Posts: 15,126
    :0 Brian, you must be the only person to have kept up with all the instruction changes as they occurred.
  • RossHRossH Posts: 5,336
    ozpropdev wrote: »
    Use the WC and WZ effects to change GETBRK's results.
                    getbrk  val wc     'get skip call depth
                    getbrk  val wz     'get skip pattern
                    getbrk  val wcz    'get interrupt status
    

    There are several discussions about GETBRK, but I can't find anything definitive about what the bits mean. Are they the same as GETINT?
  • RossHRossH Posts: 5,336
    ozpropdev wrote: »

    Hmmm. Not sure that helps. And I thought C code was impenetrable! :)
  • evanhevanh Posts: 15,126
    edited 2019-06-10 07:42
    Ross,
    Here's my interpretation of the last one:
    
    			: {	1'b0,					// c	getbrk with wz
    				~|skipb,				// z
    				skipb[31:0]	};			// r
    
    C = 0
    Z = 1 if no skip bits set.
    result(D register) = skip bits

    EDIT: Inverted Z description.
    EDIT2: Corrected C as well. I really don't know my verilog.
  • RossHRossH Posts: 5,336
    I need the bits that tell me whether or not I am currently executing an interrupt service routine - i.e. whether or not I can use SKIPF. The bits are in there, I can see that - but I don't know how to interpret them :(
  • ozpropdevozpropdev Posts: 2,791
    edited 2019-06-10 07:44
    From some of my notes I think this is what your after.
    GETBRK reg wcz
    
    jjjjjjjj_iiii_hhhh_gggg_ffff_ee_dd_cc_b_a
    
    jjjjjjjj brk code
    iiii    call depth in skip
    hhhh    int3 select
    gggg    int2 select
    ffff    int1 select
    ee      int3 state
    dd      int2 state
    cc      int1  state
    b      int  stall
    a       hubs
    
    for %ee,%dd,%cc
    
     %0x = waiting for interrupt event
     %10 = waiting for interrupt branch
     %11 = executing interrupt service routine
    


    Edit: Typo & correction.
  • evanhevanh Posts: 15,126
    Hmm, Brian, it's changed from that from what I'm reading:
    result = jjjjjjjj_b_c_x_m_hhh_ggg_f_e
    
    jjjjjjjj break code
    b	? (only show brk_pass in brk isr)
    c	? (csc_active)
    x	streamer active
    m	mode? (mem)
    hhh	int select
    ggg	int state
    f	stall
    e	mode? (hubs)
    
    as per the verilog
    {	brk_isr ? !brk_pass[1] : int_stall,	// c	getbrk with wcz		(only show brk_pass in brk isr)
    				hubs,					// z
    				brk_code & {8{brk_isr}},		// r	(only show brk_code in brk isr)
    				!brk_pass[1] && brk_isr,		//	(only show brk_pass in brk isr)
    				csc_active,
    				xfr_active,
    				mem_mode,
    				int_select[3:1],
    				int_state[3:1],
    				int_stall,
    				hubs }
    
  • evanh wrote: »
    Hmm, Brian, it's changed from that from what I'm reading:
    result = jjjjjjjj_b_c_x_m_hhh_ggg_f_e
    
    jjjjjjjj break code
    b	? (only show brk_pass in brk isr)
    c	? (csc_active)
    x	streamer active
    m	mode? (mem)
    hhh	int select
    ggg	int state
    f	stall
    e	mode? (hubs)
    
    as per the verilog
    {	brk_isr ? !brk_pass[1] : int_stall,	// c	getbrk with wcz		(only show brk_pass in brk isr)
    				hubs,					// z
    				brk_code & {8{brk_isr}},		// r	(only show brk_code in brk isr)
    				!brk_pass[1] && brk_isr,		//	(only show brk_pass in brk isr)
    				csc_active,
    				xfr_active,
    				mem_mode,
    				int_select[3:1],
    				int_state[3:1],
    				int_stall,
    				hubs }
    

    Yikes!
    My verilog is a bit rusty, but looking at it again I read the 32 bits as
    mmmmmmmm_l_k_j_i_hhhh_gggg_ffff_ee_dd_cc_b_a
    
    mmmmmmmm brk code
    l       brk pass
    k       csc_active
    j       xfr_active
    i       mem_mode
    hhhh    int3 select
    gggg    int2 select
    ffff    int1 select
    ee      int3 state
    dd      int2 state
    cc      int1  state
    b      int  stall
    a       hubs
    




  • RossHRossH Posts: 5,336
    Thanks all, but I am simply removing all instances of SKIPF. It turns out that the overhead of trying to figure out if you are not executing an interrupt service routine (and hence can use SKIPF) makes the use of SKIPF pretty pointless anyway.
  • evanhevanh Posts: 15,126
    Interrupts could be an optional compile time switch. Prop2 is built around the system engineer knowing total utilisation of features, ie: Tell the compiler if interrupts are ever going to be used in the target product.
  • evanhevanh Posts: 15,126
    edited 2019-06-10 10:29
    PS: Such a switch can affect use of other instructions too. WAITxxx and REP and even eliminate much use of STALLI. Being able to use the WAITxxx instructions in particular not only improves jitter timing but also will lower the Prop2 power consumption too.
  • RossHRossH Posts: 5,336
    evanh wrote: »
    Interrupts could be an optional compile time switch. Prop2 is built around the system engineer knowing total utilisation of features, ie: Tell the compiler if interrupts are ever going to be used in the target product.

    Yes, I thought that too - until I realized it meant I had to have two copies of all the library code, and two times as many kernels (and there are 12 of the little buggers already!). One set that could be used in an interrupt-driven system, and one that could not :(
  • evanhevanh Posts: 15,126
    edited 2019-06-10 12:53
    ozpropdev wrote: »
    Yikes!
    My verilog is a bit rusty, but looking at it again I read the 32 bits as
    I count only 20 bits, plus C and Z, in that result (From a GETBRK D WCZ).
  • Note that SKIPF works in hubexec mode by inserting nops, so it doesn't give any speed advantage there over conditional execution. So you're probably wise to skip SKIPF :).

    Have you considered having interrupts use the same stack as regular code? It would simplify things a lot, and be more like other processors.
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    Note that SKIPF works in hubexec mode by inserting nops, so it doesn't give any speed advantage there over conditional execution. So you're probably wise to skip SKIPF :).

    I was actually mostly using SKIPF in the kernel (i.e. in cog execution mode). But even so, the benefits were becoming more and more doubtful in the cases I was using it.
    Have you considered having interrupts use the same stack as regular code? It would simplify things a lot, and be more like other processors.

    I don't really like the idea that all stacks have to allow for the possibility of arbitrary interrupt code, when they have no idea how much stack space might be required (I'm thinking primarily of threads here).

    The P2 is a microcontroller with limited RAM, not a general-purpose CPU with effectively infinite RAM. Everything needs to be constrained to fixed sizes.
  • evanh wrote: »
    ozpropdev wrote: »
    Yikes!
    My verilog is a bit rusty, but looking at it again I read the 32 bits as
    I count only 20 bits, plus C and Z, in that result (From a GETBRK D WCZ).

    The int_select and int_state registers are not single bits though.
    int_select are 4 bits each and int_state are 2 its each.
    These are defined elsewhere and not shown in the Verilog snippet.


  • evanhevanh Posts: 15,126
    edited 2019-06-11 02:47
    So you're saying in verilog, select[3:1] can be the top of a hierarchical structure?
  • evanh wrote: »
    So you're saying in verilog, select[3:1] can be the top of a hierarchical structure?
    In Chip's Verilog source he probably has something like this
    reg [3:0] int_select [3:0]; 
    reg [1:0] int_state [3:0];
    
    So these have a defined width (bits) and a length (elements).
    In the Verilog were referring to we are incorporating elements of these registers.



  • RossHRossH Posts: 5,336
    I'm leaving in the ability to call GETBRK. For now, I've left it called _get_int() since renaming it to _get_brk() seems a bit confusing given it is in the interrupt library and intended to return the interrupt status.

    One day, someone will figure out what the return value actually means and it might prove useful.
  • Here's some test code that verifies the int_state results.
    LEDs blink if state indicates ISR executing.
    
    'GETBRK int_state bits test P2_ES Eval board
    
    dat	org
    
    	getct	pa
    	addct1	pa,##20_000_000
    	addct2	pa,##21_000_000
    	addct3	pa,##22_000_000
    
    	mov	ijmp1,#isr1
    	setint1	#1		'ct1 interrupt
    	mov	ijmp2,#isr2
    	setint2	#2		'ct2 interrupt
    	mov	ijmp3,#isr3
    	setint3	#3		'ct3 interrupt
    
    	jmp	#$
    
    {
     %0x = waiting for interrupt event
     %10 = waiting for interrupt branch
     %11 = executing interrupt service routine
    }
    
    isr1		addct1	pa,##10_000_000
    		getbrk	pb	wcz
    		shr	pb,#2
    		and	pb,#%11
    		cmp	pb,#%11 wz	'isr1 executing?
    	if_e	drvnot	#56		
    		reti1
    
    isr2		addct2	pa,##10_000_000
    		getbrk	pb	wcz
    		shr	pb,#4
    		and	pb,#%11
    		cmp	pb,#%11 wz	'isr2 executing?
    	if_e	drvnot	#57		
    		reti2
    
    isr3		addct3	pa,##10_000_000
    		getbrk	pb	wcz
    		shr	pb,#6
    		and	pb,#%11
    		cmp	pb,#%11 wz	'isr3 executing?
    	if_e	drvnot	#58		
    		reti3
    
    
    
  • RossHRossH Posts: 5,336
    ozpropdev wrote: »
    Here's some test code that verifies the int_state results.
    LEDs blink if state indicates ISR executing.

    Thanks. You can see from this code why the overhead of trying to figure out if you are in an ISR - and you have to check all three interrupts to be sure - makes it not worth the effort in many SKIPF cases.
  • cgraceycgracey Posts: 14,133
    edited 2019-06-11 17:05
    RossH, I haven't finished the debug-related documentation yet, but here is what GETBRK returns:
    wire [33:0] getbrk_czr	=
    							// GETBRK with WCZ
      i[wc] ? i[wz]	? {	brk_isr ? !brk_pass[1]		// c = first-brk if in brk isr
    				: int_stall,		// c = STALLI mode if not in brk isr
    			hubs,				// z = cog launched in hub-exec mode
    			brk_code & {8{brk_isr}},	// d[31:24] = BRK {#}d code (only shown in brk isr)
    			!brk_pass[1] && brk_isr,	// d[23] = first-brk (only shown in brk isr)
    			csc_active,			// d[22] = colorspace converter active
    			xfr_active,			// d[21] = streamer active
    			mem_mode,			// d[20] = WRFAST mode, as opposed to RDFAST mode
    			int_select[3],			// d[19:16] = int3 source select (qmt_event..int_event)
    			int_select[2],			// d[15:12] = int2 source select
    			int_select[1],			// d[11:8] = int1 source select
    			int_state[3],			// d[7:6] = int3 status (%00=idle, %10=pending, %11=isr active)
    			int_state[2],			// d[5:4] = int2 status
    			int_state[1],			// d[3:2] = int1 status
    			int_stall,			// d[1] = STALLI mode, as opposed to ALLOWI mode
    			hubs }				// d[0] = cog launched in hub-exec mode
    
    							// GETBRK with WC
    		: {	skipb[0],			// c = next skip bit in process
    			1'b0,				// z ignored
    			skipk[3:0],			// d[31:28] = skip call depth (skip suspended when not 0)
    			skipm,				// d[27] = SKIP mode (as opposed to SKIPF/EXECF/xbyte mode)
    			lut_share,			// d[26] = LUT sharing enabled
    			stk_xbyte,			// d[25] = top_of_stack[19:0] = $001FF
    			xbytet[8:0],			// d[24:16] = current xbyte mode (set by '_RET_ SETQ{2}' with top of stack = $001FF
    			trap[15:0] }			// d[15:0] = event trap bits (qmt_event..int_event)
    
    							// GETBRK with WZ
    		: {	1'b0,				// c ignored
    			~|skipb,			// z = no skip bits in process
    			skipb[31:0] };			// d[31:0] = skip bits in process (lsb first)
    

    Sorry I didn't post this a few days earlier.
  • RossHRossH Posts: 5,336
    ersmith wrote: »
    Have you considered having interrupts use the same stack as regular code? It would simplify things a lot, and be more like other processors.

    I want to go back to this particular point, because I now have interrupts working in conjunction with multi-threading. If the interrupts had to use the same stack space as the thread, then a program could require up to three times as much stack space.

    For an actual example, consider the attached program, which combines threads and interrupts (as usual, compiled for the P2 EVAL board, serial interface 230400 baud).

    Each thread has a stack of ~100 longs, as does each interrupt - they need a reasonable stack size because they all call various library functions (e.g. to perform I/O).

    If the interrupts used the same stack space as the executing thread that they happen to interrupt, then each thread would have to have a stack of 300 longs - (100 longs for the thread itself, another 100 in case interrupt 2 goes off, and another 100 in case interrupt 1 goes off, because interrupt 1 can interrupt interrupt 2). Since there are 100 threads executing, this program would use another 200*100 longs, or around 80kb of additional Hub RAM dedicated to stack space. Of course, this is a fairly pathological example! :)

    /***************************************************************************\
     *                                                                           *
     *                        Thread and Interrupt Demo                          *
     *                                                                           *
     *    Demonstrates many threads executing concurrently on a single cog,      *
     *    in conjunction with interrupts going off periodically.                 *
     *                                                                           *
     *    NOTE: This program will only work on the P2, since the P1 does not     *
     *    support interrupts                                                     *
     *                                                                           *
     \***************************************************************************/
    
    /*
     * Catalina interrupt support
     */
    #include <catalina_interrupts.h>
    
    /*
     * Catalina multi-threading support
     */
    #include <catalina_threads.h>
    
    /*
     * Catalina HMI functions (unlike stdio functions, these 
     * functions can be used in interrupt functions)
     */
    #include <catalina_hmi.h>
    
    /*
     * include some useful multi-threading utility functions (but note
     * that we cannot call any thread functions while in an interrupt)
     */
    #include <thread_utilities.h>
    
    /*
     * define how many threads we want
     */
    #define THREAD_COUNT 100
    
    /*
     * define the stack size each thread needs (since this number depends on the
     * function executed by the thread, the stack size has to be established by 
     * trial and error):
     */
    #define STACK_SIZE (MIN_THREAD_STACK_SIZE + 100)
    
    /*
     * define the stack size each interrupt needs (since this number depends  
     * on the function executed by the interrupt, the stack size has to be 
     * established by trial and error):
     */
    #define INT_STACK_SIZE (MIN_INT_STACK_SIZE + 100)
    
    /*
     * define the number of thread locks we want (we only need one)
     */
    #define NUM_LOCKS 1
    
    /*
     * define the pins that we will toggle in the interrupt functions
     */
    #if defined (__CATALINA_P2_EVAL)
    #define PIN_SHIFT (57-1-32) // use pin 57,58 (and dirb, outb) on P2_EVAL
    #define _dir _dirb
    #define _out _outb
    #else
    #define PIN_SHIFT 0 // use pin 1,2 (and dira, outa) on other platforms
    #define _dir _dira
    #define _out _outa
    #endif
    
    /*
     * define some global variables to be used by the interrupt functions
     */
    static unsigned long mask_1   = 1<<(PIN_SHIFT + 0);
    static unsigned long on_off_1 = 1<<(PIN_SHIFT + 0);
    
    static unsigned long mask_2   = 1<<(PIN_SHIFT + 1);
    static unsigned long on_off_2 = 1<<(PIN_SHIFT + 1);
    
    /*
     * define some global variables to be used by the thread functions
     */
    static int ping;
    
    /*
     * a pool of thread locks - note that the pool must be 5 bytes larger than
     * the actual number of locks required (MIN_THREAD_POOL_SIZE = 5) 
     */
    static char pool[MIN_THREAD_POOL_SIZE + NUM_LOCKS]; 
    
    /*
     * a lock allocated from the pool - required to protect the thread HMI
     * plugin functions
     */
    static int lock;
    
    
    /*
     * thread : this function can be executed as a thread. Multiple instances
     *          of this function can be started using the _thread_start function.
     */
    int thread(int me, char *not_used[]) {
    
       while (1) {
          if (ping == me) {
             // print our id
             _thread_printf(pool, lock, "%d ", (unsigned)me);
             ping = 0;
          }
          else {
             // nothing to do, so yield
             _thread_yield();
          }
       }
       return 0;
    }
    
    /* 
     * interrupt_x : these functions can serve as interrupt service routines. 
     *               Such functions must have no arguments, and must return - but
     *               cannot return a value. They must be set as an interrupt 
     *               using one of the _set_int_x functions.
     */
    
    void interrupt_1(void) {
        // we are a counter interrupt, so add to the counter for next time
        _add_CT1(100000000);
    
        // now toggle our LED
        _out(mask_1, (on_off_1 ^= mask_1));
    
        // just to demonstrate that we have full C functionality, do some output
        // (note 1: we would not usually do this in an interrupt routine!!!)
        // (note 2: we cannot call ANY thread operations while in an interrupt
        //          routine, and so instead of using _thread_printf we use 
        //          t_printf - but this may sometimes result in garbled output
        //          if an interrupt occurs when a thread is also performing I/O)
        t_printf(" <<Interrupt 1>> ");
    }
    
    void interrupt_2(void) {
        // we are a counter interrupt, so add to the counter for next time
        _add_CT2(300000000);
    
        // now toggle our LED
        _out(mask_2, (on_off_2 ^= mask_2));
    
        // just to demonstrate that we have full C functionality, do some output
        // (note 1: we would not usually do this in an interrupt routine!!!)
        // (note 2: we cannot call ANY thread operations while in an interrupt
        //          routine, and so instead of using _thread_printf we use 
        //          t_printf - but this may sometimes result in garbled output
        //          if an interrupt occurs when a thread is also performing I/O)
        t_printf(" <<Interrupt 2>> ");
    }
    
    
    /*
     * main : Start up to THREAD_COUNT threads, then ping each one in turn.
     *        Also, set up two timer interrupts that use interrupts 1 & 2 
     *        (note that a program that uses threads cannot use interrupt 3).
     */
    void main(void) {
    
       int i = 0;
       void *thread_id;
    
       // declare some space that will be used as stack by the threads
       unsigned long stacks[STACK_SIZE * THREAD_COUNT];
    
       // declare some space that will be used as stack by the interrupts
       unsigned long int_stack[INT_STACK_SIZE * 2];
    
       // assign a lock to avoid context switch contention 
       _thread_set_lock(_locknew());
    
       // initialize a pool of thread locks (we need only 1 lock)
       _thread_init_lock_pool (pool, NUM_LOCKS, _locknew());
    
       // assign a thread lock to avoid HMI plugin contention
       lock = _thread_locknew(pool);
    
       _thread_printf(pool, lock, "Press a key to start\n");
       k_wait();
    
       // start THREAD_COUNT instances of our thread function. Note that we
       // need to point to the TOP of the stack space reserved for each thread
       for (i = 1; i <= THREAD_COUNT; i++) {
          thread_id = _thread_start(&thread, &stacks[STACK_SIZE*i], i, NULL);
          _thread_printf(pool, lock, "thread %d ", i);
          if (thread_id == (void *)0) {
             _thread_printf(pool, lock, " failed to start\n");
             while (1) { };
          }
          else {
             _thread_printf(pool, lock, " started, id = %d\n", (unsigned)thread_id);
          }
       }
    
       // the interrupt service routines toggle their LEDs on
       // interrupt - set up the direction and initial value  
       _dir(mask_1 | mask_2, mask_1 | mask_2);
       _out(mask_1 | mask_2, on_off_1 | on_off_2);
    
       // these are counter interrupts so set up the initial counters
       _set_CT1(100000000);
       _set_CT2(300000000);
    
       // set up our interrupt service routines - note that we need to point
       // to the TOP of the stack space we have reserved for each interrupt
    
       // note: we cannot use interupt 3 in a multi-threaded program!!!
    
       _set_int_1(CT1, &interrupt_1, &int_stack[INT_STACK_SIZE * 1]);
       _set_int_2(CT2, &interrupt_2, &int_stack[INT_STACK_SIZE * 2]);
    
       // now loop forever, pinging each thread in turn. Periodically, an 
       // interrupt will occur ...
       while (1) {
          _thread_printf(pool, lock, "\n\nPinging all threads\n");
          for (i = 1; i <= THREAD_COUNT; i++) {
             _thread_printf(pool, lock, "%d:", i);
             // ping the thread
             ping = i;
             // wait till thread responds
             while (ping) {
                // nothing to do, so yield
                _thread_yield();
             };
          }
          // slow things down enough to read the messages
          for (i = 0; i < 100; i++) {
             _thread_yield();
          }
       }
    }
    

    The attached program is compiled in Native mode, but interrupts now also work in Compact and LMM execution modes.
Sign In or Register to comment.