Shop OBEX P1 Docs P2 Docs Learn Events
IRC is a few cycles short — Parallax Forums

IRC is a few cycles short

BebopALotBebopALot Posts: 79
edited 2006-03-04 22:21 in General Discussion
Hi folks, I am having trouble understanding why one of my interrupt routines is going at every 62 or 63 cycles instead of every 64. When I don't use conditional instructions such as JNB and break it out from the rest of the program in a new one, it hits every 64 cycles like a charm. I thought the mov w, #-64 and then retiw took care of this but I am still coming up a few cycles short in my program below. Just to give you background on what this code does is that it dynamically reconfigures the IRC to switch back and forth betwen 40KHz and 250Hz while alternating the output between two different pins every time 40KHz occurs. It works ok, I just don't understand the small descrepancy in cycles.

FYI the intital state is set to run the 40 KHz first. The routine is contained in the IRC under :RTCC and also the first configuration of the RTCC. Sorry about the uneven indents and stuff. Thats not how it looks in my editor though.


Thanks to anyone that can help.


Device SX28L, Turbo, Stackx, OSCHS3
IRC_CAL IRC_SLOW
FREQ 5_120_000


RESET Start

ORG $000

Interrupt ; Interrupt Routine - always starts at $00
DEC CycleCount ; countdown to 255
JNB CycleCount.7, WRITE ; cyclecount.7 = 0, toggle? go to accessment
;
; underflow occurs, reconfigure IRC
NOT IRCState ; flip state
JNB IRCState.0, :RTCCPSA
:RTCC Mov w, #%1100_0000 ; initialize RTCC = 191
Mov rtcc, w
Mov w, #%1001_1111 ; reconfigure as RTCC only!
Mov !option, w
Mov w, #%0000_0110 ;
Mov CycleCount,w ; set cyclecount to 6 (bit 7 ON on underflow)- 64 cycles = 12.5 us
NOT TX1TX2 ; flip transmitter select
reti ; configuration complete
:RTCCPSA Mov w, #%0000_0001
Mov RTCC, w ; initialize RTCC to 1
Mov w, #%1000_0001 ; reconfigure RTCC with PSA 1:4
Mov !option, w
Mov w, #%0001_0100
Mov Cyclecount, w ; set counter to 20 (bit 7 ON on underflow)- 20 cycles prescaled = 4.0 ms
reti
;
WRITE JNB IRCState.0, RetRTCCPSA ; jump to prescaler return if IRCState=0
JNB TX1TX2.1, :Ra ; toggle a port during RTCC - but which?
NOT RB ; write RB
JMP RetRTCC
:Ra NOT RA ; write RA
RetRTCC Mov w, #-64
Retiw
RetRTCCPSA Retiw

Org $08


CycleCount ds 1
TX1TX2 ds 1
IRCState ds 1
IRCRecon ds 1

Org $100

Start

;load predefined registers

Clr Cyclecount ; trigger var
Clr TX1TX2 ; Tx1/Tx2 (0/1)
Clr IRCState ; 0 = RTCC w/ PSA , 1 = RTCC
Clr IRCRecon

; de-glitch initial state

Mov w, #%0000
Mov Ra, w ; set register A output to digital LOW
Mov w, #%0000_0000
Mov Rb, w ; set register B output to digital LOW
Mov w, #%0000_0000
Mov Rc, w ; set register C output to digital LOW

; configure ports

Mov M, #$0f ; access TRIS mode
Mov W, #%0000 ; bits 0-3 are outputs
Mov !Ra, W ; make Ra0-Ra3 outputs
Mov W, #%0000_0000 ; all bits output
Mov !Rb, W ; set Port B pins to output
Mov W, #%0000_0000 ; all bits output
Mov !Rc, w ; set Port C pins to output

; set intial conditions


Not TX1TX2 ; select TX1 (indirect)
Not IRCState
JNB IRCState.0, :RTCCPSA
:RTCC Mov w, #%1011_1111 ; initialize RTCC = 191
Mov rtcc, w
Mov w, #%1001_1111 ; reconfigure as RTCC only!
Mov !option, w
Mov w, #%0000_0110 ;
Mov CycleCount,w ; set cyclecount to 6 (bit 7 ON on underflow)
JMP Main

:RTCCPSA Mov w, #%0000_0001
Mov RTCC, w ; initialize RTCC to 0
Mov w, #%1000_0001 ; reconfigure RTCC with PSA 1:4
Mov !option, w
Mov w, #%0000_0000
Mov Cyclecount, w ;

MAIN

JMP Main
«1

Comments

  • PJMontyPJMonty Posts: 983
    edited 2006-02-16 04:40
    Why are you using reti and retiw instructions in your interrupt routine? Only the retiw uses the value of W to properly update the RTCC count. Also, unless you are really pressed for cycles, it's a good practice to have only a single exit point in any routine, but especially interrupt routines. Why? Well, using your interrupt as an example, suppose you need to change the interrupt rate. If you have multiple places where you set W and then do an retiw, you'll have to find all of them and change all of them. If you have a single exit point, then you only have a single place to update.
      Thanks, PeterM
  • Andrew G. MillerAndrew G. Miller Posts: 14
    edited 2006-02-16 22:38
    Remember that JNB is actually two instructions hidden in one.

    JNB = SB and JMP.

    Take a look at the assembler output listing file, and the assembled SX instructions/timing should be more clear.

    -AGM-
  • BebopALotBebopALot Posts: 79
    edited 2006-02-17 03:27
    Doesn't retiw take into account the two instructions hidden in one? If not this would explain the discrepancy.
  • pjvpjv Posts: 1,903
    edited 2006-02-17 05:08
    Hi Bebop;

    A peculiarity of the SX is that every time you write to or test (but not read) the RTCC, it stalls the RTCC count for 2 (I think) cycles. Perhaps something to do with the pipeline, I'm not sure.

    So you are best not to mess with the RTCC, just let it roll over and do its thing.....just load w with an appropriate value for each of the interrupt periods, and then RETIW. That will properly take care of things.

    And, yes, compound instructions are counted properly. The assembler turns them into their appropriate number of elements.

    Cheers,

    Peter (pjv)
  • PJMontyPJMonty Posts: 983
    edited 2006-02-17 09:14
    Bebopalot,

    I think you need to get a better conceptual grasp of what the retiw instruction is doing in order to understand how it works. I believe a lot of people are rather mystified by the whole retiw with a negative number thing, so perhaps this will help.

    Imagine that the RTCC is your digital alarm clock. If you don't change the alarm time on it, every 24 hours it will go off at the same time. You don't need to do anything special, it just happens. Now suppose that for some reason you need to take some medicine every three hours starting at midnight. What you could do is set your alram clock to go off at midnight. You would then get out of bed, take your medicne, go back to bed, and before falling asleep you'd set the alarm to go off at 3 AM. When the alarm went off, you'd repeat the process, only this time when you got back into bed you'd set the alarm for 6 AM. When the alarm went off at six, you'd do the same thing only this time you'd set it for 9 AM, etc, etc, etc.

    Now, the nice thing about this is that no matter how long it took you to get up and take your medicine, when you set the alarm it would be for some absolute time (3 AM, 6 AM, etc) and not a relative time such as "3 hours from now." By doing it this way, you allow yourself flexibility (perhaps you went to the bathroom or ate a snack one of the times you got up to take you rmedicine) in the amount of time your "medicine" task took, but the time between getting up was always a consistent and accurate 3 hours. The key is that the clock kept running and keeping track of time while you got your medicine, and you manually calculated what time to set the alarm for to be three hours from when it woke you up.

    This is essentially what the negative number in the retiw instruction does. When the RTCC interrupt goes off, the CPU jumps to the interrupt code and starts executing it. Depending on what it's doing, sometimes it takes more or less time for the interrupt code to execute depending on things like conditional code paths. The whole time you're executing the interrupt, the RTCC is still going, just like your clock. However, when you get to the end of the interrupt code, there is that nice retiw with a negative number. That negative number is added to the current value of RTCC (whatever it is) and, just like you setting your alarm to be at the next 3 hour increment, it automatically causes the RTCC value to be jumped ahead so that it goes off at the right time.

    If you specify -26, the RTCC will be jumped ahead so that it generates an interrupt 26 clocks cycles from the time the current one went off. If you specify -100, the time between interrupts will be 100 cycles. As long as your interrupt code takes less time than the number of cycles in your RTCC time interval, you're golden. Your interrupt could take 9 cycles on one interrupt and 62 on the next, but when your interrupt is done and the "retiw #-100" is executed, the next interrupt will happen 100 clocks from when the current one started. No muss, no fuss. The CPU does all the work needed to make this happen.

    Finally, by adding in the prescaler to the RTCC, you can lengthen the amount of time between interrupts. If the RTCC is feed with no prescaler, then every clock cycle causes the RTCC clock to increment (or decrement, I cna't remember if it counts up or down) to the next value. This means your interrupt period can be no longer than 255 clocks long. At 50 MHz, that's a pretty short amount of time. However, if you need more time, set the prescaler to something other than 1:1 and you'll get that amount of time increase. If the prescaler is 4:1, then the RTCC value will get changed every four clocks, meaning that your maximum of 255 is now 255 * 4 = 1020 cycles.

    Hopefully this will help make the workings of the RTCC and the retiw instruction a little clearer. The key to remember is that as the person writing code, you can trust that the you'll get consistent time between interrupts regardless of any varying amount of time spent in the interrupt code as long as the overall interrupt execution time is less than the interrupt period.
      Thanks, PeterM
  • James NewtonJames Newton Posts: 329
    edited 2006-02-17 19:12
    Peter, that was lovely...

    ...I've put it on a page at sxlist.com if that is ok with you?

    http://www.sxlist.com/techref/ubicom/RETIWinISRs.htm

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ---
    James Newton, Host of SXList.com
    james at sxlist,com 1-619-652-0593 fax:1-208-279-8767
    SX FAQ / Code / Tutorials / Documentation:
    http://www.sxlist.com Pick faster!



  • BebopALotBebopALot Posts: 79
    edited 2006-02-18 14:34
    Here is my modified code. The problem I have identified still mystifies me. In short, depending on the value of TX1TX2 (0000_0000 or 1111_1111) the IRC rountine takes one of two paths. In one path the cycle count in the SXSim is always 64 (what I need) in the other path it is always 63 (when TxT1 is set to 1111_1111). Each path when selected will repeat for a total of six interupts - so I don't see why there is a discrepancy. The retiw command should jump it ahead correctly for 64 cycles but I am getting 63, in under one set of conditions. Does anyone else see what I am saying?

    BBAL

    ;5.120 MHz version

    Device SX28L, Turbo, Stackx, OSCHS3
    IRC_CAL IRC_SLOW
    FREQ 5_120_000


    RESET ResetEntry

    ORG 0

    Interrupt ; Interrupt Routine - always starts at 0
    DEC CycleCount ; countdown to 255
    JNB CycleCount.7, WRITE ; cyclecount.7 = 0, compare
    ;
    ; underflow occurs, reconfigure IRC
    Setb Recon.0 ; Recon to TRUE
    Exit JNB IRCState.0, TermPSA
    TermRTCC Mov w, #-64
    Go Retiw ; single exit point
    TermPSA Mov w, #-%1111111
    JMP Go
    ;

    WRITE JNB IRCState.0, TermPSA ; jump to prescaler return if IRCState=0
    JNB TX1TX2.0, WriteRa ; toggle a port during RTCC - but which?
    NOT output1
    Mov w, output1
    Mov Rb, W ; write RB
    JMP TermRTCC ; terminate write RB
    WriteRa NOT output2
    Mov w, output2
    Mov Ra, w ; write RA
    JMP TermRTCC ; terminate write RA



    Org 08


    CycleCount ds 1
    TX1TX2 ds 1
    IRCState ds 1
    IRCRecon ds 1
    Output1 ds 1
    Output2 ds 1
    Recon ds 1



    Org 100

    ResetEntry

    ;load predefined registers

    Clr Cyclecount ; trigger var
    Clr TX1TX2 ; Tx1/Tx2 (0/1)
    Clr IRCState ; 0 = RTCC w/ PSA , 1 = RTCC
    Clr Recon ; 0 = don't reconfigure, 1 = reconfigure
    Clr Output1 ; toggle state TX1
    Clr Output2 ; toggle state TX2

    ; de-glitch initial state

    Mov w, #%0000
    Mov Ra, w ; set register A output to digital LOW
    Mov w, #%0000_0000
    Mov Rb, w ; set register B output to digital LOW
    Mov w, #%0000_0000
    Mov Rc, w ; set register C output to digital LOW

    ; configure ports

    Mov M, #$0f ; access TRIS mode
    Mov W, #%0000 ; bits 0-3 are outputs
    Mov !Ra, W ; make Ra0-Ra3 outputs
    Mov W, #%0000_0000 ; all bits output
    Mov !Rb, W ; set Port B pins to output
    Mov W, #%0000_0000 ; all bits output
    Mov !Rc, w ; set Port C pins to output



    ; Initialize

    Not TX1TX2 ; select TX 1/2 toggle this on and off and you get a 63-64 cycle difference
    Not Recon ; toggle reconfigure

    Start

    JNB Recon.0, Loop ; if recon = FALSE do not reconfig
    Call Reconfigure

    Loop
    JMP Loop


    ; Subroutine
    Reconfigure
    NOT IRCState ; flip state
    JNB IRCState.0, PSA
    Mov w, #%1100_0000 ; initialize RTCC = 191
    Mov rtcc, w
    Mov w, #%1001_1111 ; reconfigure as RTCC only!
    Mov !option, w
    Mov w, #%0000_0110 ;
    Mov CycleCount,w ; set cyclecount to 6 (bit 7 ON on underflow)- 64 cycles = 12.5 us
    NOT TX1TX2 ; flip transmitter select
    JMP EndRoutine ; configuration complete
    PSA Mov w, #%0000_0001
    Mov RTCC, w ; initialize RTCC to 0
    Mov w, #%1000_0001 ; reconfigure RTCC with PSA 1:4
    Mov !option, w
    Mov w, #%0000_0001 ; Mov w, #%0001_0100
    Mov Cyclecount, w ; set counter to 20 (bit 7 ON on underflow)- 20 cycles prescaled = 4.0 ms
    Mov w, #-%1111111 ; jump ahead to call IRC every 256 cycles
    EndRoutine Clrb Recon.0 ; set reconfigure to false
    Ret
  • pjvpjv Posts: 1,903
    edited 2006-02-20 03:42
    Hi Bebop;

    I'm still in ransit on my way back home from the "Propeller unveiling" at Parallax. Perhaps I will get a chance to peek at and try your code tomorrow. I'm sure the answer is straigt forward.

    Cheers,

    Peter (pjv)
  • BebopALotBebopALot Posts: 79
    edited 2006-02-20 06:15
    Thanks Peter, I'm all ears.

    Stumped

    BBAL
  • PJMontyPJMonty Posts: 983
    edited 2006-02-20 16:15
    James,

    Sorry to take so long to reply, I was travelling - of course you can post it anywhere you like. It took long enough to write (twice - the 1st version got bogged down in technical details) that I'm glad it struck a chord.
      Thanks, PeterM
  • PJMontyPJMonty Posts: 983
    edited 2006-02-20 16:36
    Bebopalot,

    Well, one tricky aspect to your dilemma is that you are assuming that SxSim is correct. No slight on the exceptional work of Guenther, but simulators always run the risk of being incorrect compared to the hardware. The good news is, you've probably discovered a bug in SxSim that Guenther will likely fix. The bad news is that you've spent a lot of time worrying about details that are almost certainly correct on the real hardware.

    Basically, if you have a single exit point in your ISR, you're using the retiw instruction to return form it, and the maximum execution path of your ISR is shorter by 6 (or is it 9?) cycles than the duration of the ISR period, then your code will execute at a consistent number of cycles each time. This is pretty much the guarantee that SX users depend on when coding ISR routines, and a key reason for many people to use the SX in the first place. Personally, at this point, I would either assume a bug in SxSim or find a way to verify the cycle count using external hardware (such as an oscilloscope) before I would spend another moment worrying about the apparent one cycle discrepancy.

    BTW, this doesn't mean that every time you find a discrepancy betwen your expectations and what SxSim says that you automatically assume a bug in SxSim. On the contrary, I think your diligence in figuring out this problem and the effort you've undertaken on the assumption that the problem is in your code, is the absolute correct way to proceed with these sorts of problems. Most people blame the assembler/compiler/IDE/chip long before they ever assume that their code was wrong. However, at some point, when you find that your solar system simulator shows that days are really 25 hours long for half the year and 23 the other half, you have to assume a flaw in the simulator and find an independent means of verifying the error since real world experience shows that days are consistently 24 hours long.
      Thanks, PeterM
  • BebopALotBebopALot Posts: 79
    edited 2006-02-20 20:48
    PeterM

    I checked with my o-scope and the bursts seem to hover around 40.00 KHz for a while and sometimes deviate. But that may be my digital oscope. If you all recall I disembarked on the IRC path because an experienced forum member cited that using "hard coding" with nops and nested loops really underutilized the clocking of a jitterless interupt. Now I have a question before me, which may not have any practical relevance: which code do I use? My hard coded loop or my code above with the interupts? I don't trust my own digital scope to give me the answer. For both the hard-coded and IRC versions the bursts hang around 40.00 KHz and then fluctuate. Maybe I should get a freqeuncy counter to verify. In any event, I am going to go ahead now and I a least have the option of either version in the future to use and see if one or the other makes a difference. I want my signal just as clean as can be.

    I really have learned a lot in this problem! I feel a little more experienced with the SX now!

    BBAL
  • BeanBean Posts: 8,129
    edited 2006-02-20 21:36
    If you post your code I will check it with a frequency counter.
    Of course the frequency will only be as accurate as your master clock source.
    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "SX-Video·Module" Now available from Parallax for only $28.95

    http://www.parallax.com/detail.asp?product_id=30012

    "SX-Video OSD module" Now available from Parallax for only·$49.95
    http://www.parallax.com/detail.asp?product_id=30015

    Product web site: www.sxvm.com

    "Ability may get you to the top, but it takes character to keep you there."
    ·
  • BebopALotBebopALot Posts: 79
    edited 2006-02-21 17:18
    Thanks Bean, here is my "IRC version". I'll post the "hard coded" in the next post. Total = 2 versions. Burst should be 40KHz.


    ;5.120 MHz version

    Device SX28L, Turbo, Stackx, OSCHS3
    IRC_CAL IRC_SLOW
    FREQ 5_120_000


    RESET ResetEntry

    ORG 0

    Interrupt ; Interrupt Routine - always starts at 0
    DEC CycleCount ; countdown to 255
    JNB CycleCount.7, WRITE ; cyclecount.7 = 0, compare
    ;
    ; underflow occurs, reconfigure IRC
    Setb Recon.0 ; Recon to TRUE
    Exit JNB IRCState.0, TermPSA
    TermRTCC Mov w, #-64
    Go Retiw ; single exit point
    TermPSA Mov w, #-%1111111
    JMP Go
    ;

    WRITE JNB IRCState.0, TermPSA ; jump to prescaler return if IRCState=0
    JNB TX1TX2.0, WriteRa ; toggle a port during RTCC - but which?
    NOT output1
    Mov w, output1
    Mov Rb, W ; write RB
    JMP TermRTCC ; terminate write RB
    WriteRa NOT output2
    Mov w, output2
    Mov Ra, w ; write RA
    JMP TermRTCC ; terminate write RA



    Org 08


    CycleCount ds 1
    TX1TX2 ds 1
    IRCState ds 1
    IRCRecon ds 1
    Output1 ds 1
    Output2 ds 1
    Recon ds 1



    Org 100

    ResetEntry

    ;load predefined registers

    Clr Cyclecount ; trigger var
    Clr TX1TX2 ; Tx1/Tx2 (0/1)
    Clr IRCState ; 0 = RTCC w/ PSA , 1 = RTCC
    Clr Recon ; 0 = don't reconfigure, 1 = reconfigure
    Clr Output1 ; toggle state TX1
    Clr Output2 ; toggle state TX2

    ; de-glitch initial state

    Mov w, #%0000
    Mov Ra, w ; set register A output to digital LOW
    Mov w, #%0000_0000
    Mov Rb, w ; set register B output to digital LOW
    Mov w, #%0000_0000
    Mov Rc, w ; set register C output to digital LOW

    ; configure ports

    Mov M, #$0f ; access TRIS mode
    Mov W, #%0000 ; bits 0-3 are outputs
    Mov !Ra, W ; make Ra0-Ra3 outputs
    Mov W, #%0000_0000 ; all bits output
    Mov !Rb, W ; set Port B pins to output
    Mov W, #%0000_0000 ; all bits output
    Mov !Rc, w ; set Port C pins to output



    ; Initialize

    Not TX1TX2 ; select TX 1/2 toggle this on and off and you get a 63-64 cycle difference
    Not Recon ; toggle reconfigure

    Start

    JNB Recon.0, Loop ; if recon = FALSE do not reconfig
    Call Reconfigure

    Loop
    JMP Loop


    ; Subroutine
    Reconfigure
    NOT IRCState ; flip state
    JNB IRCState.0, PSA
    Mov w, #%1100_0000 ; initialize RTCC = 191
    Mov rtcc, w
    Mov w, #%1001_1111 ; reconfigure as RTCC only!
    Mov !option, w
    Mov w, #%0000_0110 ;
    Mov CycleCount,w ; set cyclecount to 6 (bit 7 ON on underflow)- 64 cycles = 12.5 us
    NOT TX1TX2 ; flip transmitter select
    JMP EndRoutine ; configuration complete
    PSA Mov w, #%0000_0001
    Mov RTCC, w ; initialize RTCC to 0
    Mov w, #%1000_0001 ; reconfigure RTCC with PSA 1:4
    Mov !option, w
    Mov w, #%0000_0001 ; Mov w, #%0001_0100
    Mov Cyclecount, w ; set counter to 20 (bit 7 ON on underflow)- 20 cycles prescaled = 4.0 ms
    Mov w, #-%1111111 ; jump ahead to call IRC every 256 cycles
    EndRoutine Clrb Recon.0 ; set reconfigure to false
    Ret
  • BebopALotBebopALot Posts: 79
    edited 2006-02-21 17:21
    "Hard Coded" Version

    ;5.120 MHz version

    Device SX28L, Turbo, Stackx, OSCHS3
    IRC_CAL IRC_SLOW
    FREQ 5_120_000


    RESET MAIN

    ORG $08 ;define word size for addresses
    Hi_Counter ds 1
    Lo_Counter ds 1
    Long_Delay1 ds 1
    Long_Delay2 ds 1
    Burst_Count ds 1



    ORG $100


    MAIN



    ; de-glitch initial state

    Mov w, #%0000
    Mov Ra, w ;set register values port A to digital LOW
    Mov w, #%00000000
    Mov Rb, w ;set register values port B to digital LOW
    Mov w, #%00000000
    Mov Rc, w ;set register values port C to digital LOW

    ; configure ports

    Mov M, #$0f ; access TRIS mode
    Mov W, #%0000 ; bits 0-3 are outputs
    Mov !Ra, W ; make Ra0-Ra3 outputs
    Mov M, #$0f ; access TRIS mode
    Mov W, #%00000000 ; all bits output
    MOv !Rb, W ; set Port B pins to output
    Mov M, #$0f ; access TRIS mode
    Mov w, #%00000000 ; all bits output
    Mov !Rc, w ; set Port C pins to output


    Generate_Bursts

    Mov w, #$03 ; set w=3
    Mov Burst_Count,w ; set burst counter for TX_1

    ;***** TRANSMITTER 1 *****

    TX_1_Burst
    ;HI WAVE
    SetB Ra.0 ; toggle RA.0 HI
    mov w, #$0f ; delay = 15 cycles
    mov Hi_Counter, w ; load delay counter
    Delay_1:
    decsz Hi_Counter
    jmp Delay_1
    NOP
    NOP
    ;LO WAVE
    Clrb Ra.0 ; toggle RA.0 LO
    mov w, #$0f ; delay = 15 cycles
    mov Lo_Counter, w ; load delay counter
    Delay_2:
    decsz Lo_Counter
    jmp Delay_2
    Decsz Burst_Count
    JMP TX_1_Burst
    ; wait 18936 cycles - start 3.7ms delay
    mov w, #$93
    mov Long_Delay1, w
    mov w, #$0B
    mov Long_Delay2, w
    TOF_Delay:
    decsz Long_Delay1
    jmp $+2
    decsz Long_Delay2
    jmp TOF_Delay

    ;4 cycles (total 189340 cycles = 3.7ms)
    NOP
    Mov w, #$03
    Mov Burst_Count, w ;reset burst counter to 3 for TX_2

    ;***** TRANSMITTER 2 *****

    ;HI WAVE
    TX_2_Burst
    SetB Ra.3 ; toggle RA.3 HI
    mov w, #$0f ; delay = 15 cycles
    mov Hi_Counter, w ; load delay counter
    Delay_3:
    decsz Hi_Counter
    jmp Delay_3
    NOP
    NOP
    ;LO WAVE
    Clrb Ra.3 ; toggle RA.3 LO
    mov w, #$0f ; delay = 15 cycles
    mov Lo_Counter, w ; load delay counter
    Delay_4:
    decsz Lo_Counter
    jmp Delay_4
    Decsz Burst_Count
    JMP TX_2_Burst
    ; wait 18936 cycles - start 3.7ms delay
    mov w, #$93
    mov Long_Delay1, w
    mov w, #$0B
    mov Long_Delay2, w
    TOF_Delay2:
    decsz Long_Delay1
    jmp $+2
    decsz Long_Delay2
    jmp TOF_Delay2

    ;4 cycles (total 189340 cycles = 3.7ms)
    NOP

    JMP Generate_Bursts ;start again
  • pjvpjv Posts: 1,903
    edited 2006-02-22 03:42
    Hi Bebop;

    I ran your code code posted today (Tuesday) this evening, and using a 50 MHz resonator (I don't have a 5.12 crystal), the interrupt gets triggered every 1.280 usec exactly. It never varies from that. I have a super scope that can trigger on event durations with better than 10 nsec precision, so I'm totally sure of the reading.

    I found the code somewhat convoluted, and didn't try to decipher what it was trying to accomplish, but I wonder if there are some typing errors, but can't estimate if they would have any impact:

    1. Did you really mean to set the "Org" at reenty to 100......? Isuspect you meant $100

    2. Did you really mean the line "TermPSA Mov w, #-%1111111" to only set 7 bits in W? I suspect not..........It helps to use an underscore separator such as "mov w,#-%1111_1111" then it's easier to spot such typos. Again I have no idea as to the impact of this, because the code never went there.

    There could be other issues, so I ask you to describe in detail what is being attempted and how it is to be achieved, so we can have some better insight into what's expected.

    I also expect there is a MUCH easier way to accomplish your goal; again a good explanation will help verify that.

    Cheers,

    Peter
  • BebopALotBebopALot Posts: 79
    edited 2006-02-22 15:51
    Hi Peter,

    Overview of the program: I need the SX to hit a subcircuit with a burst of three 40KHz waves then pause for about 4.0ms; then hit a second subcircuit with a burst of three 40KHz waves, pause for about 4.0ms, then repeat the cycle. No two subcircuits should be on at the same time.

    So designing the code, if we are going to use the interrupts then either we reconfigure the interrupts dynamically to equal the time intervals we need with 8 bit counters (simplicity) or we can use a single base interrupt but use multibyte counters to keep track of the number of 12.5us cycles during a 4.0ms pause = 320. I really haven't learned multibyte counters yet, so I chose to keep my counting 8 bit and reconfigure the interrupt dynamically to meet my needs. Certainly, if anyone had a better idea, I want to learn.

    - It seems the program is working right, thanks for checking it out with your superscope.

    - I am having trouble understanding what addresses to use and when in my code. I was getting an error earlier about my subroutine now being in the correct half of page of memory or something. I can't keep it straight when to use which addresses. $00 - interupts ; $08 - define word space ; $100 main program; subroutines = ??

    -. As for "TermPSA Mov w, #-%1111111" the assembler kept giving me a warning "Literal truncated to eight bits" until I reduced it by a digit to only 7 bits. What I was trying to acheive here was to "set the clock forward" to interupting every 255 cycles using the mov w, -x then retiw command but kept getting a truncation error. So I changed my value from "-%1111_1111" to "-%1111111" which seemed to work but left me with a value of 127 instead of 255. I guess I could have put "-%0111_1111" which seems to work also. I don't understand why I am getting a truncation error with 255, -%1111_1111

    Thanks Peter, and it is very interesting to hear the many different design angles on the code so far.
  • pjvpjv Posts: 1,903
    edited 2006-02-22 22:15
    Hi Bebop;

    To help you learn, I wrote an interrupt driven state machine for you to achieve what you are trying to accomplish. This is not the fastest or best way to implement it, but it should definitely help you in understanding what needs to be done.

    The file in the attachment is more verbose than the abbreviated one listed here.

    id 'Bebop4'······················································· ; Feb 22, 2006
    ;alternately provide 3 cycles of 40 KHz (12.5 uSec hi and 12.5 uSec lo) on two pins separated by about 4 mSec

    ··device·sx28,oschs3,turbo,optionx······················· ;new assembler
    ··freq·50_000_000··············································;default run speed = 50MHz
    ··reset·Init······················································· ;entry point for reset and power-up

    ModeLevel·equ·$0D········································· ···;mode for port ABC bit CMOS level selection (0)
    ModePullup·equ·$0E··········································· ;mode for port ABC bit pullup selection (0)
    ModeTristate·equ·$0F······································· ·;mode for port ABC bit output direction selection (0)

    RTCCIntEnable·= %1001_1111

    IntConst·········· ·equ·-125······························· ···;2.5 uSec interrupt tick (125 * 20 nSec = 2500 nSec)
    Port················· equ·ra·······································;

    ;flags
    ··················org·$08···········································;start of usable RAM space; independent of program memory
    Flags········ ··ds·1··············································· ;
    IntFlag··= Flags.0··········································· ····;use this bit as an indicator that interrupt has occurred

    State········· ·ds·1···············································;
    StateDurLow·ds·1···············································;low byte of 16 bit multi-use duration counter
    StateDurHigh·ds·1···············································;high byte of 16 bit multi-use duration counter
    CycleCounter·ds·1···············································;number of cycles in one burst
    BurstBits······ ds·1···············································;holds the output value of the port bits

    ··················org·$000·····················;interrupt routine always starts at beginning of program memory
    Interrupt····· setb·IntFlag··············· ·;advise main routine that interrupt has occurred
    ··················mov·w,Port················ ;get current state of port bits
    ··················and·w,#%1111_1100···· ;clear burst output bits
    ··················or·w,BurstBits·············· ;merge in the current state of the burst bits
    ··················mov·Port,w················ ·;transfer the pre-calculated value for the port to the port
    ··················mov·w,#IntConst········· ;load w to interrupt again in 125 clocks..... 2.5 uSec
    ··················retiw························ ·;return from interrupt

    Init·········· ··;Program starts here on reset and power up
    ·············· ··;Initialize the ports
    SetLevels·····mov·m,#ModeLevel······ ·;Set to 0 for CMOS levels
    ··············· ··mov·!ra,#%0000···········;
    ··················mov·!rb,#%0000_0000···;
    ··················mov·!rc,#%0000_0000···;
    SetPullups··· mov·m,#ModePullup····· ·;Set to 0 for pullups
    ··················mov·!ra,#%0000·········· ;
    ··················mov·!rb,#%0000_0000···;
    ··················mov·!rc,#%0000_0000···;
    SetTris········ mov·m,#ModeTristate··· ;Set to 0 for output
    ··················clr·ra·························· ;
    ··················mov·!ra,#%0000··········· ;x,x,Burst1,Burst0
    ··················clr·rb···························;
    ··················mov·!rb,#%1111_1111··· ;
    ··················clr·rc·························· ;
    ··················mov·!rc,#%1111_1111··· ;
    ··················clr·rtcc······················· ;clear interrupt timer
    ··················mov·!option,#RTCCIntEnable··;enable interrupt
    ··················clr·Flags······················ ;

    InitState····· ;Initialize state and state duration timers high and low
    ··················mov·StateDurLow,#1····· ;trivial initial value
    ··················mov·StateDurHigh,#1···· ;trivial initial value
    ··················mov·State,#StartBurst1··;begin with outputting burst 1

    Main············;loop here waiting for an interrupt
    ··················sb·IntFlag··················· ;test for interrupt occurred
    ··················jmp·Main···················· ;not yet

    usec2_5······ clrb·IntFlag·············· ···;come here every 2.5 usec
    ··················decsz·StateDurLow····· ·;test for state duration low to expire
    ··················jmp·Main··················· ·;not yet
    ··················decsz·StateDurHigh····· ·;wait for state duration high to expire
    ··················jmp·Main··················· ·;not yet
    ··················mov·pc,State·············· ;invoke next state after state duration has expired

    StartBurst1 ·mov·CycleCounter,#3···· ;three cycles of (12.5 uSec hi then 12.5 uSec low)
    SetBurst1Hi· mov·BurstBits,#%0000_0001··;set the port bit 0 hi
    ··················mov·w,#SetBurst1Lo···· ;load low address of next state which will set level low
    ··················jmp·SetFreqDuration····· ;set duration of current state 16 bit counter

    SetBurst1Lo·clr·BurstBits···· ············ ;clear the port bit 0 lo
    ··················mov·w,#SetBurst1Hi····· ;load low address of next state which will set level high
    ··················decsz·CycleCounter······ ;derement every time one cycle is completed
    ··················skip························· ··;effectively this makes the previous decsz equivalent to decsnz
    ··················mov·w,#EndBurst1······· ;load low address of next state which will time the 4 mSec interval
    ··················jmp·SetFreqDuration···· ·;set duration of current state 16 bit counter

    EndBurst1··· ;set delay for a duration of 4 milliseconds that is 1600 ticks of 2.5 uSec; = (6*256) + 64
    ··················mov·StateDurLow,#64··· ;stay in this state for duration
    ··················mov·StateDurHigh,#6+1··;needs to be 1 more than required for dec instruction to time properly
    ··················mov·State,#StartBurst2··;set low address of next state which will start burst 2
    ··················jmp·Main····;

    StartBurst2· mov·CycleCounter,#3·· ··;three cycles of (12.5 uSec hi then 12.5 uSec low)
    SetBurst2Hi· mov·BurstBits,#%0000_0010··;set the port bit 1 hi
    ··················mov·w,#SetBurst2Lo····· ;load low address of next state which will set level low
    ··················jmp·SetFreqDuration···· ·;set duration of current state 16 bit counter

    SetBurst2Lo ·clr·BurstBits··············· ·;clear the port bit 1 lo
    ··················mov·w,#SetBurst2Hi··· ·;load low address of next state which will set level high
    ··················decsz·CycleCounter···· ·;decrement every time one cycle is completed
    ··················skip························· ·;effectively this makes the previous decsz equivalent to decsnz
    ··················mov·w,#EndBurst2······ ;load low address of next state which will time the 4 mSec interval
    ··················jmp·SetFreqDuration···· ;set duration of current state 16 bit counter

    EndBurst2···· ;set delay for a duration of 4 milliseconds that is 1600 ticks of 2.5 uSec; = (6*256) + 64
    ··················mov·StateDurLow,#64··· ;stay in this state for duration
    ··················mov·StateDurHigh,#6+1··;needs to be 1 more than required for dec instruction to time properly
    ··················mov·State,#StartBurst1··;set low address of next state which will start burst 1
    ··················jmp·Main····;

    SetFreqDuration·;save address of next state (is in w) and set duration of current state
    ··················mov·State,w··············· ;set continuation address of state after duration is finished
    ··················mov·StateDurLow,#5···· ;stay in this state for duration
    ··················mov·StateDurHigh,#0+1··;needs to be minimum of 1 for dec instruction to time properly
    ··················jmp·Main····················· ;wait for next tick



    Hope·this helps.

    Cheers,

    Peter (pjv)

    ·OOPS:........... in the attachment at line labeled "SetBurst1Lo", the instruction should be "clr BurstBits" instead of "clrb BurstBits"....it still worked, but what I had is incorrect.

    Post Edited (pjv) : 2/22/2006 10:24:51 PM GMT
  • PJMontyPJMonty Posts: 983
    edited 2006-02-23 07:47
    Bebopalot,

    The "Literal truncated" message is because the SX uses internal values that are 12 bits, but when it's loaded into W, SASM has to lop off the upper four bits to fit it into an 8 bit register. It's a warning you can safely ignore. Adding this line...

    LIST Q=37; This quiets SASM "Literal truncated" warning


    ... to your code will suppress it.
      Thanks, PeterM
  • JavalinJavalin Posts: 892
    edited 2006-02-23 12:03
    PJ,

    I notice you're using shadow registers in the ISR to modify the pins. Is this for a particular reason?

    Relevant code below:

    mov w,Port ;get current state of port bits
    and w,#%1111_1100 ;clear burst output bits
    or w,BurstBits ;merge in the current state of the burst bits
    mov Port,w ;transfer the pre-calculated value for the port to the port
    mov w,#IntConst ;load w to interrupt again in 125 clocks..... 2.5 uSec


    The reason I ask is that I am having issues with an SX application that reads two 115k serial inputs, buffers it and sends them out. Essentially a serial relay app. Periodically I have (i.e. half way through a relay of a CMU2CAM picture) a load of line errors.

    I am using currently setb and clrb (based on the dualuart.src senix example) to manipulate the pins.

    Cheers for any thoughts!

    James
  • pjvpjv Posts: 1,903
    edited 2006-02-23 15:35
    Hi James;

    The reason I modify the output port pins in the ISR is because I want to effect the required levels at EXACTLY the same instant, every time. If I did it in the mainline, then the timing of those port changes are subject to variations due to other things the mainline is doing. In other words, they are less deterministic.

    So, if I pre-calculate what the next output levels are to be in the main line, and then output those at exactly the same spot in the next ISR, we get the transitions happening at exactly the same instant every time. Totally deterministic. When you look at it with a good scope, there is less than 2 nano seconds of jitter.

    For precision pulses or frequencies this is required. Although I'm not sure if that level of accuracy is required by Bebop, it's easy to do, so why not?

    In your case, if the bits are not set/cleared at the same instant, there could be problems in high speed UARTs.

    If you post your total code with a description, I'd be happy to comment. If your code is long or secretive, perhaps you can pare it down, but of course please test the shortened version to confirm it still exhibits the identical problem.

    Cheers,

    Peter (pjv)
  • JavalinJavalin Posts: 892
    edited 2006-02-23 18:02
    Peter,

    Thanks for your offer.··· Attached is current version of software, along with example of serial corruption (jpg)

    The code is fairly well commented.· The essence of the app is to use the ISR to read serial from two UARTS into two 32byte buffers, the ISR also has a transmitt function.· The mainline then empties the buffer and keeps the ISR transmit going.· Data·recieved on one uart is transmitted out of the other, and vice versa.

    This is based on the dualuart.src example from Senix and the 16byte buffering from Gunther!

    Cheers for your help,

    James
  • pjvpjv Posts: 1,903
    edited 2006-02-23 21:35
    Hi James;

    I think you are correct in your assessment; although I have not attempted to run your code.

    Does the problem go away when you run at slower baud rates? If so, then that is more evidence of your suspicions.

    What you are doing in your ISR, is sampling the inputs, and setting the outputs at somewhat indeterminate times (instruction cycle wise) due to the unpredictability of which UART (1 or 2) and which element (transmit or receive) is doing what. And that is an external, unpredictable affair.

    You could fix that by sampling the UART inputs into a temporary storage at a fixed point near (at ?) the beginning of your ISR, and also set the UARToutputs from a temporary (shadow) register at a fixed point in your ISR. What is important is the time from entering the ISR and the point where you sample the inputs and set the outputs be always at the same time (hence instruction) reference.

    This is most easily accomplished by having no variable instruction paths prior to these events, and that is done with temporary shadow registers.

    Other than that, I believe your code can also be "tightened up" by 20% to 40%, and I'd be happy to have a go at that if it's of (significant) interest to you. (I live for the challenge, you see, but do not want to waste my time).

    I assume you will always transmit (relay) the received data at the same data rate? In other words 115200 in to 115200 out, and both in 8N1 format.

    Please advise.

    Cheers,

    Peter (pjv)
  • JavalinJavalin Posts: 892
    edited 2006-02-23 21:49
    Hi PJV,

    The issue goes away at 57600, even if the camera is running 57600 and the pc 115200. Its bizzare that the error is always half way down a large picture "download". Always in a repeatable pattern. If I get the camera to slow down (I think it delay's between bytes), while still at 115200, the problem also goes away.

    www.sxlist.com had some fragmented information on writing to pins at high speed, and using shadow registers - didn't really make total sense I must admit!

    Thanks for your offer, but I dont want to waste your time fixing an issue in my code. However, if you are willing, a short ISR example, with using the register-shadow technique would be enough for me to re-work my code to see if that cures the issue.

    I will be relaying at different speeds, i.e. 115 to 19.2, however in short bursts and within the mostly within the buffer sizes.

    Appreciate the help so far!!

    James
  • Andrew G. MillerAndrew G. Miller Posts: 14
    edited 2006-02-23 22:48
    This is a classic symptom of accumulating bit rate errors between your two devices.

    Most likely the ISR rate and Oscillator speed are off just enough from the exact serial bitrate that, without breaks between characters to reset bit timing, the two bit rates get farther and farther out of sync.

    At some point, you miss a bit, or you read a bit twice.

    Try having the sender put a pause in the data stream, at least at the end of each text line. The inter-character gap resets the start of character timing, and clears the accumulating bit-rate timing errors.

    The other alternative is to use oscillators that are higher tollerance, or have a frequency that is an exact binary multiple of the desired bitrate.

    To exactly match bitrates, is one reason you see older crystal oscillators with oddly fractional decimal frequencies. 4.096MHz etc.

    -AGM-
  • pjvpjv Posts: 1,903
    edited 2006-02-23 22:58
    Hi Andrew;

    No, that is not the right way to fix this. Each character (byte) resyncs every time on detecting the start bit, so extra inter-character spaces should not be required.

    It IS a requirement of course that the sender transmit at least the number of stop bits that the receiver is expecting; one in this case. After that all should be OK if one samples the bits in a byte consistently at the same, and correct time.

    I'm sure we'll get James' issue dispatched with shortly.

    Cheers,

    Peter (pjv)
  • Andrew G. MillerAndrew G. Miller Posts: 14
    edited 2006-02-23 23:52
    Peter,

    If the stop-bit length is 1/bitrate, as expected, the VP will still accumulate the error. Basically, the VP can't see the "edge" of the start-bit until after the error-accumulated stop-bit time, pushing the start-bit's "window" further and further out of sync.

    Maybe the key is the "...if one samples at the same, and correct time...." qualifer from your statement above. That is the core problem, incorrect timing.

    Most/All of the existing SX UART VP's around just take one sample in the middle of the bit period. This is simpler/smaller code, and works, until the bit timing accumulates a (bittime/2) error, pushing the sample point past one end (start or finish) of the real bit period.

    If your receiving VP isn't waiting for the whole stop-bit, then it won't be a· problem (you'd have other problems). It also won't be a problem if the tramsmitter is sending an extra stop-bit (AKA an extra inter-character delay.)··

    I once started work on a smarter UART VP, one that looks at several bit samples within a bit's period to determine the sent bit's value. I abandoned the project when the ISR code grew too long for the project, instead making sure the sender paused at least one bit period between large blocks of data, to reset bit timing and allow the VP to re-find the leading edge of the next start-bit.

    -AGM

    Post Edited (Andrew G. Miller) : 2/24/2006 12:09:16 AM GMT
  • pjvpjv Posts: 1,903
    edited 2006-02-24 01:49
    Hi Andrew;

    Still I think you are incorrect....... I like to work on the basis where we do not have control over the sender, and we have to live with whatever we get, provided it's "in-spec".

    There is absolutely no problem in sampling only once (in the middle) of each bit, PROVIDED that we "see" the front edge of the start bit sufficiently rapidly. My experience is (and that is considerable) that one can "get away" with 3X over sampling, but 5X is more "comfortable". If one does the latter, there will never be a problem with bit-drift. If one does the former, one is generally OK.

    But "generally" isn't good enough, as I like to design things so they ALWAYS work as long as things (timing) are in-spec. So to deal with this, I wrote a VP UART that oversmples 3X, and then also adjusts its synchronization on any transition it sees along the way in receiving the byte. It works, absolutely, 100% of the time.

    I'm not suggesting that's what James requires here, as his problem is sampling at inconsistent times. A temporary shadow register will solve that for him, and I guarantee you it will work 100% without messing with the sender.

    If the sender is out of spec of course, that's a different story.

    By the way James, I don't know if I'll get this done for you tonight........more likely some time this weekend.

    Cheers,

    Peter (pjv)
  • JavalinJavalin Posts: 892
    edited 2006-02-24 10:31
    Peter,

    No worries with time, just grateful for the help.

    I see what Andrew means, the behaviour he describes fits exactly with what I see when I test it. When I recieve a half size picture, or the CMU2CAM sends with some delays in the transmission the issue does not occur. Slow or infrequent (i.e. me typing commands) data again does not show any issue.

    I have a number of 75mhz chips & osc's so I can change it to 4x sampling and see if the issue goes away. Ideally i'd like to be able to deal with 50mhz and 3x sampling, but if its not possible......

    Thanks,

    James
  • pjvpjv Posts: 1,903
    edited 2006-02-24 16:29
    Hi James;

    You get more "mileage" out of your processor's instructions by using an odd number of over sampling. Depending on the accuracy of baud time you select (I frequently use a "nice" number rather than the actual 1/Baud the calculation suggests), an over sampling of 3X works well. If I purposely skew the Baud time to get my "nice" number, I frequently use 5X.

    In any case, I'm going to assume you don't have control over the character format (only one stop bit) and the character rate (continuous transmission without interruption) in selecting a good answer for your situation.

    Also, from a processor load perspective, do both relay directions always happen simultaneously?

    Cheers,

    Peter (pjv)
Sign In or Register to comment.