Shop OBEX P1 Docs P2 Docs Learn Events
SX/B Interrupt Latency — Parallax Forums

SX/B Interrupt Latency

WiaWia Posts: 5
edited 2010-04-28 17:45 in General Discussion
Hello,
I have a question about the time it takes for the SX to respond to an interrupt.

In my code, I want to trigger an interrupt on the rising edge of pin RB.0. The interrupt service routine simply toggles pin RC.0 high and then low.
According to the SX datasheet, the interrupt latency should be 3 clock cycles. (I am using a 50MHz external crystal.) When I look at pins RB.0 and RC.0 on an oscilloscope, the rising edge of RC.0 occurs over 1us after the rising edge of RB.0. That is over 50 clock cycles.

Does SX/B automatically add some latency in the interrupt response? Or am I just doing something very wrong? I have just started learning SX/B (yesterday!), so hopefully I am just missing something simple. Any help would be greatly appreciated!

Thank you!

DEVICE          SX48, OSCHS3
FREQ            50_000_000
ID              "Test"


' -------------------------------------------------------------------------
' I/O Pins
' -------------------------------------------------------------------------

ADC_DATA    PIN    RD     'ADC Data input on Port D
PIX_DATA    PIN    RE    'Pixel Data output on Port E
SX_CLK        PIN    RC.0    'SX generated clock
PX_CLK        PIN    RB.0    'Pixel Clock (interrupt input)
GPIO_0        PIN    RB.1    'GPIO_0 from PMT Controller
GPIO_1        PIN    RB.2    'GPIO_1 from PMT Controller
SEL                PIN    RB.3    'Select from PMT Controller, analog or photon count


' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------

ADC_In        VAR    Byte
Int_Check    VAR    Byte

' =========================================================================
 INTERRUPT
' =========================================================================

ISR_Start:
    'Int_Check = WKPND_B
    'If Int_Check <>%00000001 Then ISR_Exit 'Check to make sure that interrupt due to pin RB.0

    SX_CLK = 1
    'NOP
    'NOP
    SX_CLK = 0

ISR_Exit:
    WKPND_B = 0    'Reset Port B interrupt flags
  

 RETURNINT


' =========================================================================
  PROGRAM Start
' =========================================================================



' -------------------------------------------------------------------------
' Program Code
' -------------------------------------------------------------------------

Start:
    TRIS_B = %11111111
    TRIS_C = %11111110
    TRIS_D = %11111111
    TRIS_E = %00000000

    WKPND_B = %00000000    'Clear pending register
    WKED_B =     %00000000    'Interrupt on rising edge
    WKEN_B =  %11111110    'Enable interrupts on bit 0 only

    ADC_In = 0
    SX_CLK = 0            'clock resets to 0



Main:
    
    ADC_In = ADC_DATA    'read the ADC
    PIX_DATA = ADC_In    'output pixel data
  

  GOTO Main

Comments

  • ZootZoot Posts: 2,227
    edited 2010-04-20 21:33
    Look at your source output. You'll see all the code at the beginning and end of the ISR generated by SXB for preserving M and PARAM registers. Adds a bit of overhead to both ends of the ISR.

    The SX only preserves the W, FSR and Status registers (and of course the stack and program counter) upon entry/exit of the ISR. SX/B cleverly preserves PARAM1-PARAM5 and M so that they are not clobbered when the user does things like use SX/B statements (which often use the PARAM variables as workspace) or change pin directions or do READs (which would clobber the M register).

    You may want to use the NOCODE option for your ISR, and manually save M only if your ISR ends up using M for any reason (otherwise, don't save it). And of course, DON'T use PARAM variables in the ISR.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php


    Post Edited (Zoot) : 4/20/2010 9:38:15 PM GMT
  • WiaWia Posts: 5
    edited 2010-04-20 22:35
    Hello Zoot,
    Thank you for your quick reply.
    I checked out the List file, and I saw the extra instructions at the beginning and end of the ISR you mentioned. It looks like at the most there will be 10 clock cycles delay added at the beginning of the ISR from these instructions, so I am still not sure if this is where the huge latency is coming from.

    I did try your suggestion of using the NOCODE option, and I had some interesting results.
    Changing nothing in the above code except for adding NOCODE, the latency was drastically reduced, but there are now two interrupts triggered for each rising edge on pin RB.0. The first pulse generated by the ISR occurs 0.4us after the rising edge of RB.0, and the second occurs 1us after the same rising edge.

    I figured that something other than the RB.0 interrupt must be causing the interrupt, so I added in code for checking the WKPND_B register (which was commented out in the code pasted above). So, the code for the ISR is now...
    ' =========================================================================
     INTERRUPT NOCODE
    ' =========================================================================
    
    ISR_Start:
        Int_Check = WKPND_B
        If Int_Check <>%00000001 Then ISR_Exit 'Check to make sure that interrupt due to pin RB.0
    
        SX_CLK = 1
    
        SX_CLK = 0
    
    ISR_Exit:
        WKPND_B = 0    'Reset Port B interrupt flags
      
    
     RETURNINT
    



    I now only have one interrupt per RB.0 rising edge, but the latency is back to 1us and for some reason it misses every third edge. It consistently triggers for two edges and then skips one. My input signal is a clean square wave without ringing, so I don't believe the input is the source of the problem.
    Do you know how this could be caused just by adding "NOCODE"? Or have I just set my interrupt up incorrectly?

    Again, thank you very much for your help!!
  • ZootZoot Posts: 2,227
    edited 2010-04-20 22:42
    You need to make sure RTCC interrupts are disabled, PWM interrupts are disabled, so that only the pin edge change causes an interrupt.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • ZootZoot Posts: 2,227
    edited 2010-04-20 22:44
    OPTION = %0100_0XXX

    15.4.3 Option 
    The OPTION register is a run-time writable register used to configure the RTCC and the Watchdog 
    Timer. The size of this register is affected by the OPTIONX device setting. 
     
    When OPTION Extend = 0, bits 7 and 6 are implemented. 
    When OPTION Extend = 1, bits 7 and 6 read as &#8216;1&#8217;s. 
     
    RTW - If = 0, register $01 is W 
      If = 1, register $01 is RTCC 
    RTI -  If = 0, RTCC roll-over interrupt is enabled 
      If = 1, RTCC roll-over interrupt is disabled 
    RTS - If = 0, RTCC increments on internal instruction cycle 
      If = 1, RTCC increments on transition of RTCC pin 
    RTE - If = 0, RTCC increments on low-to-high transition 
      If = 1, RTCC increments on high-to-low transition 
    PSA - If = 0, prescaler is assigned to RTCC, divide rate determined by PS0-PS2 bits 
      If = 1, prescaler is assigned to WDT, and divide rate on RTCC is 1:1 
    
    7 6 5 4 3 2 1 0 
    RTW RTI RTS RTE PSA PS2 PS1 PS0 
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • WiaWia Posts: 5
    edited 2010-04-21 21:10
    Thank you for the suggestion and for the information on the OPTION register.

    I've added the line
    OPTION = %01000000
    


    to my code, but I still get the same results.

    Do I need to do something else to disable the PWM interrupts?

    If I could get rid of the extra trigger for every pulse, the interrupt latency would still be either 0.4us (~20 clk cycles) or 1us (~50 clk cycles) depending on if I am eliminating the first or the second trigger. Is this latency normal? In my final design, I would need to be able to handle a trigger every 1us, so this timing probably wouldn't work out for me.

    Thanks so much for all your help so far.
  • ZootZoot Posts: 2,227
    edited 2010-04-21 21:42
    If it were me, if I weren't putting the SX to sleep between pin state changes, I would just do it in an interrupt. You are talking about a pretty fast sample rate here, so you might not be able to put much in the main line....


    
    flags VAR Byte
    intPinFlag VAR flags.0
    intCntr VAR Byte
    
    INTERRUPT 2_000_000 ' every .5 us, prob. not enough of a sample rate, but gives you 25 clocks between interrupts for mainline work
    
       ASM
       SNB RB.0 ' wait for high edge
         SETB intPinFlag ' set a single bit
       ENDASM
    
       RETURNINT
    
    Main:
    
       IF intPinFlag = 1 THEN ' must have tripped
           ' do something in less than 25 clocks so you don't possibly miss an interrupt
           intPinFlag = 0
       ENDIF
    
       GOTO Main
    
    
    



    Doesn't give you much time in the mainline to do anything useful (like a read an ADC) as you might miss the next interrupt. You could think about incrementing a counter in the ISR for each edge on the sample, so that if the counter was > 1 you would know you missed a pin input (but could do something useful anyway):

    
    intFlags VAR Byte
    intPinLast VAR intFlags.0
    intPinFlag VAR intFlags.1
    
    INTERRUPT 2_000_000 ' every .5 us, prob. not enough of a sample rate, but gives you 25 clocks between interrupts for mainline work
    
       ASM
    Sample_Pin_Quickly:
       RR intFlags ' moves current pin flag to "last" flag
       CLRB intPinFlag  ' move pin state to flag
       SNB RB.0
         SETB intPinFlag
       JNB intPinFlag, :done  ' pin is currently low, so skip the rest
       SB intPinLast ' pin is high, and it was high last time, so it's not an edge, skip
          INC intPinCntr ' otherwise increment the counter
    :done   
       ENDASM
    
       RETURNINT
    
    Main:
    
       IF intPinCntr <> 0 THEN ' must have tripped
           IF intPinCntr = 1 THEN ' single state since last time
             ' do something
           ELSE
             ' take into account multiple triggers since last checked
           ENDIF
          intPinCntr = 0
       ENDIF
    
      GOTO Main
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • WiaWia Posts: 5
    edited 2010-04-23 15:15
    This is an interesting idea. I'll have to play around with this.

    I realized that there was something wrong with my external crystal circuit, and I wasn't actually running at 50MHz. (After reading through the forum, I tried every combination of OSCHS2, OSCHS3, 1Mohm resistor, 10k resistor, no resistor, C1=15pF C2=33pF, C1=15pF C2=15pF... but with no luck.) I switched to the ceramic oscillator, and now I am actually at 50MHz. This has reduced the latency, but it still seems that the interrupt is taking far longer than it should. (I am still hoping for the 3 clock cycles described in the datasheet - or at least something close to 3.) Also, whenever I add the "NOCODE" option, I still trigger the interrupt twice for every rising edge. (The first at .2us, and the second at .4us). Removing NOCODE, I only trigger once at .4us. Any idea what could be causing the first faster trigger? If I could somehow only trigger on that one, the timing would probably be fast enough. I'll also try implementing your above code and see how that goes.

    Thank you for all the time and thought you've put into the response. This has been really helpful.
  • ZootZoot Posts: 2,227
    edited 2010-04-27 06:04
    Sorry not to get back on this thread sooner...

    Where are you getting 3 cycles? A pin-edge interrupt will take 5 cycles, possibly 6
    said...
    The SX chip will activate the interrupt routine exactly 5 clock cycles in Turbo mode or exactly 10
    clock cycles in compatible mode after a Wake-Up Edge Detection event occurs. This deterministic
    feature allows for nearly jitter-free interrupt response. Latency may vary by as much as +1
    instruction cycle when interrupting on external asynchronous events, thus a high clock speed may
    be necessary to lessen the effects.

    Second, these instructions in SX/B are actually many more assembly instructions than you think:

    Int_Check = WKPND_B
        If Int_Check <>%00000001 Then ISR_Exit 'Check to make sure that interrupt due to pin RB.0
    
        SX_CLK = 1
    
        SX_CLK = 0
    
    



    Look at the list file (view list) and you'll see. If NOCODE isn't used, then you'll see even more.

    Now, if you are ONLY checking for ONE possible pin on interrupt, and you've turned RTCC interrupts off, then you don't need to actually check WKPND_B, only clear it (the interrupt won't happen except on that one pin change):

    W = WKPND_B ' 4 cycles
    SX_CLK = 1    ' 1 cycle
    NOP              ' 1 cycle
    SX_CLK = 0  ' 1 cycle
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • WiaWia Posts: 5
    edited 2010-04-28 17:04
    Ah, this makes much more sense.
    The 3-cycles interrupt info I was getting from the SX48 datasheet...

    Fast and Deterministic Interrupt
    • Jitter-free 3-cycle internal interrupt response
    • Hardware context save/restore of key resources such
    as PC, W, STATUS, and FSR within the 3-cycle
    interrupt response time
    • External wakeup/interrupt capability on Port B (8
    pins)

    The key word I was missing there was INTERNAL.
    Thank you for posting the timing information for a pin-edge interrupt. I was not able to find this anywhere in the datasheet.

    I've gotten rid of the code for checking WKPND_B, and that has helped a bit.

    Thanks again for the help!
  • ZootZoot Posts: 2,227
    edited 2010-04-28 17:45
    The datasheet is "correct" -- it takes 3 cycles for the interrupt to kick in. That doesn't mean everything that happens within the ISR (and that SX/B may add automatically to the start of the ISR) takes 3 cycles. Those instructions take whatever they take.

    The point of the "deterministic" timing is that you can time things precisely if you are bit-banging at high speeds. If you know the INT takes 3 cycles, and you know you've got 10 cycles worth of instructions before a pin's state is toggled, then it will take 13 cycles after the interrupt detection to get your results. So you can plan for things very precisely.

    Note also that while many instructions may only take one code word, that doesn't mean they only take once cycle. For example, JMP Label is one single instruction word, but takes 3 cycles to execute.

    The pin-edge interrupt info is taken from the discussion about pin-edge interrupts in the SXKey / SX manual, not the datasheet.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
Sign In or Register to comment.