I need some assembler advice / help please

Go To Last Post
30 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi
This is for an XMEGA using Atmel Studio and GCC

 

As a C programmer I need some assembler advice.

I have an unsigned char that contains a value between 0 and 8.. lets call it X
I want to do something like this

#define OFFSET 10
Y = OFFSET - X

Now in assembler do a relative jump that is based on the value in Y
nop
nop//so if Y contained 2 it would jump to here for example
nop
nop
nop
nop
nop
nop
nop
nop

Then back to C here

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you just talking about implementing a variable delay then?

 

i would have thought a "computed goto" would be the solution in AVR land that implies the IJMP opcode. It jumps to a target based on what's in the Z register so you load Z with the base of the string of NOPs and add to it the "OFFSET" then IJMP through Z.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually here's a mad idea:

#include <avr/io.h>
#include <avr/pgmspace.h>

#define NOP 0x0000
#define RET 0x9500

typedef void (*fptr_t)(void);

const uint16_t opcodes[] PROGMEM = { NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, RET };

int main(void) {
    fptr_t fp;
    uint8_t offset = PINB & 7; // just as an example input
    fp = (fptr_t)(opcodes + offset);
    fp();
}

Now this comes to you completely untried. Though it did compile OK:

00000054 <opcodes>:
	...
  68:	00 95                                               ..

0000006a <__ctors_end>:
  6a:	11 24       	eor	r1, r1
  6c:	1f be       	out	0x3f, r1	; 63
  6e:	cf e5       	ldi	r28, 0x5F	; 95
  70:	d4 e0       	ldi	r29, 0x04	; 4
  72:	de bf       	out	0x3e, r29	; 62
  74:	cd bf       	out	0x3d, r28	; 61
  76:	0e 94 41 00 	call	0x82	; 0x82 <main>
  7a:	0c 94 4a 00 	jmp	0x94	; 0x94 <_exit>

0000007e <__bad_interrupt>:
  7e:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000082 <main>:

const uint16_t opcodes[] PROGMEM = { NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, RET };

int main(void) {
    fptr_t fp;
    uint8_t offset = PINB & 7; // just as an example input
  82:	e6 b3       	in	r30, 0x16	; 22
  84:	e7 70       	andi	r30, 0x07	; 7
    fp = (fptr_t)(opcodes + offset);
  86:	f0 e0       	ldi	r31, 0x00	; 0
  88:	ee 0f       	add	r30, r30
  8a:	ff 1f       	adc	r31, r31
  8c:	ec 5a       	subi	r30, 0xAC	; 172
  8e:	ff 4f       	sbci	r31, 0xFF	; 255
    fp();
  90:	09 95       	icall
}
  92:	08 95       	ret

FYI -0xFFAC is 0x0054, the address of "opcodes[]". However I can never remember whether ICALL/IJMP want word or byte addresses. Clearly (from the disassembly) 0x0054 is a byte address. If IJMP wants word addressing there may need to be a /2 in there somewhere!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

X contains the value of a count which was started by an Event driven by an external signal.

This code is in an interrupt which the external signal also triggered.  Because the time taken to get into the ISR

is unknown I want to use this to get a known time from when the external trigger occured.

I looked at IJMP but not being into AVR assembly I wondered if there was perhaps some other way of doing it.

If I use IJMP, would I not have to first push the original Z?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I use IJMP, would I not have to first push the original Z?

That depends on the context in which it is being used. But yes ICALL/IJMP are fixed to using Z so if it's already being used for something else that needs to be "put out of the way" so you can use Z for this purpose.

 

AFAIK a computed goto using one of these instructions is the only way to achieve this without a cascade of CPI and BRxx instructions (that take longer than the NOPs).

 

BTW did you see my "C based" idea in #3 ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I dont mind if the instructions are longer than nops. As long as after the assembly has run I am always at the same number of cpu cycles for each interupt.

 

Yes I saw your #3. Am still trying to get my head around if it will work lol. Looks like an interesting idea, perhaps I should do some testing :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you trying to make a short pulse, based on the external signal?  If so, how short is the min pulse width?  What is the longest?

 

If the start of the pulse can be allowed to delay because of interrupt latency, then some kind of timer approach might help.  Input capture also comes to mind.

 

Tell more about the specs, like a timing diagram perhaps.  An interesting puzzle.

 

Your OP code looks like "Duff's Device". ;)

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidgrm wrote:

X contains the value of a count which was started by an Event driven by an external signal.

This code is in an interrupt which the external signal also triggered.  Because the time taken to get into the ISR

is unknown I want to use this to get a known time from when the external trigger occured.

I looked at IJMP but not being into AVR assembly I wondered if there was perhaps some other way of doing it.

If I use IJMP, would I not have to first push the original Z?

Ah, so you are looking to de-jitter ?

Yes, you will need to save used registers. (and also safety-mask count, so unexpected large values do not crash into the weeds)

 

IJMP info example is here

https://www.avrfreaks.net/forum/h...

 

Another way to do that in ASM is to have a series of simple counters - you need more than one to get 1 cycle granularity, as the counter loop is usually 3 cycles min.

If you make that counter loop a nicer 4 cycles, you clone 4 loops with a choice of +0,+1,+2,+3 padding, and choose which one of the 4 to use via lower 2 bits of count, and (count >>2 ) is the looping value.

 

A simulator is a good tool here...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Actually here's a mad idea:

 fp = (fptr_t)(opcodes + offset); 
 
86: f0 e0 ldi r31, 0x00 ; 0 
88: ee 0f add r30, r30  << this line doubles for word pointer
8a: ff 1f adc r31, r31 


 If IJMP wants word addressing there may need to be a /2 in there somewhere!

Works, but looks to me like this has a granularity of 2 from that word align add r30,r30 - so will de-jitter down to 2 cycles

(& the table needs to be 2x larger)

 

http://www.atmel.com/webdoc/avra...

http://www.atmel.com/webdoc/avra...

 

Last Edited: Fri. Sep 18, 2015 - 10:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes its to de-jitter for some video overlay.

Unfortunatly the Xmega event system does not allow one to stop / start a uart :(

So the sync pulse from an LM1881 triggers an event which starts the timer. It also triggers the interrupt.

I now take the timer count to figure out what the interrupt latency is and then need to adjust for it.

and also safety-mask count, so unexpected large values do not crash into the weeds

Which is why I want to do it using some NOPS as in my first pos. I can always make sure that the jump never goes more than one past the last NOP

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidgrm wrote:

Yes its to de-jitter for some video overlay...

I now take the timer count to figure out what the interrupt latency is and then need to adjust for it.

Another approach is to use that captured 'zero' time and add that to HW based pulse generation, but I think here you want to time char-send ?

Note that you will still have some jitter, unless you phase lock the CPU clock to the video.

 

Another means for manage of jitter, is to drop the MCU into idle just before you know the interrupt will arrive.

That removes the current-opcode-size jitter effect, but of course still cannot fix the 1 cycle sampling jitter.

Last Edited: Fri. Sep 18, 2015 - 10:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know if it's made for xmega, but there are some code somewhere in this forum that does what you want, by checking the timer value in the ISR.

If you can live a tad slower speed, I would go for a ijmp version, (perhaps reserve r3:r2 for temp storage of Z : movw r2,r30 ...........movw r30,r2).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay so I tried the code below. But as I know nothing about AVR assembler I am getting an error on compile.

 

For this line: "ADD r24,xdelay" "\n\t"

It gives a message "constant value required"

 

    #define OFFSET 10    
    uint8_t X = LINE_TIMER.CNT;
    uint8_t xdelay = (OFFSET - X) + 12;//12 makes sure it skips over instructions before of NOPS

     asm volatile("\n\t"
    "RCALL GETPC"  "\n\t"
    "GETPC:" "\n\t"
    "POP r25" "\n\t"
    "POP r24" "\n\t"
    "CLR r23" "\n\t"
    "ADD r24,xdelay" "\n\t"
    "ADC r25,r23" "\n\t"
    "PUSH r24" "\n\t"
    "PUSH r25" "\n\t"
    "RET" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
    "NOP" "\n\t"
        :
        : "r" (xdelay)
    );

Last Edited: Sat. Sep 19, 2015 - 05:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For de-jittering, one can use three SBRCs and RJMPs (one word, two cycles) for the low two bits.

The others can be handled with a loop.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ADD requires two registers for the operands. You have passed xdelay, which is not a register.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. Sep 19, 2015 - 08:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@skeeve

For de-jittering, one can use three SBRCs and RJMPs (one word, two cycles) for the low two bits.

The others can be handled with a loop.

Do you perhaps have a link to an example?

 

ADD requires two registers for the operands. You have passed xdelay, which is not a register.

Okay so how do I get the value in the xdelay variable into my Assembler? I thought that what I did was to tell the assembler to automatically allocate a register??

Last Edited: Sat. Sep 19, 2015 - 08:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidgrm wrote:
@skeeve

For de-jittering, one can use three SBRCs and RJMPs (one word, two cycles) for the low two bits.

The others can be handled with a loop.

Do you perhaps have a link to an example?

https://www.avrfreaks.net/comment/1357131#comment-1357131

That particular discussion on jitter starts earlier in the same thread:

https://www.avrfreaks.net/comment/1352176#comment-1352176

Although the entire thread is about jitter:

https://www.avrfreaks.net/forum/help-interrupt-jitter

 

Quote:
Okay so how do I get the value in the xdelay variable into my Assembler? I thought that what I did was to tell the assembler to automatically allocate a register??
Assemblers don't automatically allocate registers.  Compilers do that.

 

You declared xdelay to be a global variable.  The assembler will place that in SRAM.

 

You can define xdelay:

#define xdelay r16

... or:

#define xdelay 16

Or you can use .set or .equ:

.equ    xdelay, 16

Or just:

xdelay = 16

Now wherever you use xdelay, 16 will be used instead.

 

You'll need to load the register with a value to initialise it:

    ldi xdelay, (OFFSET - X) + 12;//12 makes sure it skips over instructions before of NOPS

Then when you use the add instruction, you are actually passing the register number 16:

    add r24, xdelay

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. Sep 19, 2015 - 10:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
; Requires 9+Rcount cycles
; Rcount must be an upper register
; Rcount will be destroyed
sbrc Rcount, 0
    rjmp .
sbrc Rcount, 1
    rjmp .
sbrc Rcount, 1
    rjmp .
1:
subi Rcount, 4
nop
brcc 1b

 

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which registers can I use in the assembler inside a GCC interrupt, without trashing my C program if the register is not saved before use?

 

 

Thanks for the suggestions and links. I will try them out and let you know how it works

 

 

Last Edited: Sun. Sep 20, 2015 - 08:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@skeeve

 

I dont understand what you are doing there...

why do you check bit 1 twice?

sbrc Rcount, 1
    rjmp .
sbrc Rcount, 1
    rjmp .

what is this for?

 

1:
subi Rcount, 4
nop
brcc 1b

 

I tried code that Brad posted on one of the links but get an error that I must use a constant:

For some reason it does not accept this line:  "in r16,CPU_SREG ;1\n\t"

After some more research I see that the problem is that inline assembly has some special ways of doing things :(

Can anybody assist with the correct format that this should be in?

 

 

 

 

 

    #define Fix 11
    volatile uint8_t TCC0_CNTX = LINE_TIMER.CNTL;
    asm volatile("\n\t"
    
    // SAVE STATUS REGISTER (3)
    "push r16 ;1\n\t"
    "in r16,CPU_SREG ;1\n\t"
    "push r16 ;1\n\t"

    // INTERRUPT JITTER FIX (11/15)
    
    "lds r16,TCC0_CNTX ;3\n\t"
    "cpi r16,Fix ;1\n\t"
    "brlo FIX0 ;1/2\n\t"
    "FIX0:\n\t"
    "cpi r16,Fix+1 ;1\n\t"
    "brlo FIX1 ;1/2\n\t"
    "FIX1:\n\t"
    "cpi r16,Fix+2 ;1\n\t"
    "brlo FIX2 ;1/2\n\t"
    "FIX2:\n\t"
    "cpi r16,Fix+3 ;1\n\t"
    "brlo FIX3 ;1/2\n\t"
    "FIX3:\n\t"
    

    );

 

 

Last Edited: Sun. Sep 20, 2015 - 03:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidgrm wrote:
Which registers can I use in the assembler inside a GCC interrupt, without trashing my C program if the register is not saved before use?
None of them.  An interrupt can occur at any point in time, and the compiler can use any register for any purpose at any time.  An ISR >>must<< preserve any state it changes.

 

This does of course depend upon the toolchain, but is certainly true of GCC.

 

An exception for GCC is if you reserve a register by declaring a register variable:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind

 

However, there are limitations to this approach.  Any precompiled library code which uses that register will continue to do so.  While it would preserve that register and restore it before returning to the caller, if it is interrupted by an ISR which uses that register variable, then it's 'game over'.  The solution is to wrap the call to the the library code in ATOMIC_BLOCK(), but that will have the effect of adding a great deal of latency to the ISR, which is probably bad since you likely declared a register variable for the purpose of improving interrupt response in the first place.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sun. Sep 20, 2015 - 05:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidgrm wrote:
why do you check bit 1 twice?
Grab a pencil and paper and work it out.

 

Remember that the point of a jitter nulling routine is to 'flatten out' the total number of cycles taken by taking a variable number of cycles to complete based on the cycles already taken up by the jitter.

sbrc Rcount, 1
    rjmp .
sbrc Rcount, 1
    rjmp .

The sbrc/sbrs instructions take 1 cycle if the condition is false, 2 cycles if the condition is true.

 

So, if bit 1 of Rcount is '0', then those four lines will take 2+2=4 cycles.

If bit 1 is '1', then those four lines will take 1+2(for the rjmp)=3 cycles. [thanks to @skeeve]

If bit 1 is '1', then those four lines will take 1+2(1st rjmp)+1+2(2nd rjmp)=6 cycles.

 

 

davidgrm wrote:
what is this for?
Again, grab a pencil and work it out.  Trace the number of cycles it takes to get through the routine, based on an initial value of Rcount.

 

davidgrm wrote:
I tried code that Brad posted on one of the links but get an error that I must use a constant:

For some reason it does not accept this line:  "in r16,CPU_SREG ;1\n\t"

Use __SREG__ instead of CPU_REG.

 

Inline assembler is a bit different.  You must use operands to get at I/O registers.

 

You've also made some changes that I don't understand.  Brad's XMEGA code uses the hardware timer register TCD0_CNT, but you've replaced it with a variable.  Why?  The jitter nulling code depends upon the value of a timer at the point of entry.  You cannot replace it with a variable.

 

  __asm__ __volatile__(
                        // SAVE STATUS REGISTER (3)
                       "push    r16                               \n\t" // 1
                       "in      r16,  __SREG__                    \n\t" // 1
                       "push    r16                               \n\t" // 1

                       // INTERRUPT JITTER FIX (11/15)
                       "lds     r16,  %[tcd0_cnt]                 \n\t" // 3
                       "cpi     r16,  %[fix]                      \n\t" // 1
                       "brlo    FIX0%=                            \n"   // 1/2
                     "FIX0%=:                                     \n\t"
                       "cpi     r16,  %[fix]+1                    \n\t" // 1
                       "brlo    FIX1%=                            \n"   // 1/2
                     "FIX1%=:                                     \n\t"
                       "cpi     r16,  %[fix]+2                    \n\t" // 1
                       "brlo    FIX2%=                            \n"   // 1/2
                     "FIX2%=:                                     \n\t"
                       "cpi     r16,  %[fix]+3                    \n\t" // 1
                       "brlo    FIX3%=                            \n"   // 1/2
                     "FIX3%=:                                     \n\t"
                    :
                    :
                      [tcd0_cnt] "p" (_SFR_MEM_ADDR(TCD0_CNT)),
                      [fix]      "M" (FIX)
                      );

Untested.

 

Sounds like you need to do some studying:

http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html

https://gcc.gnu.org/onlinedocs/gcc/Constraints.html

https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html

 

 

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Sep 21, 2015 - 01:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
Remember that the point of a jitter nulling routine is to 'flatten out' the total number of cycles taken by taking a variable number of cycles to complete based on the cycles already taken up by the jitter.

sbrc Rcount, 1
    rjmp .
sbrc Rcount, 1
    rjmp .

The sbrc/sbrs instructions take 1 cycle if the condition is false, 2 cycles if the condition is true.

 

So, if bit 1 of Rcount is '0', then those four lines will take 2+2=4 cycles.

If bit 1 is '1', then those four lines will take 1+2(for the rjmp)=3 cycles.

Emphasis added.

1+2+1+2=6=4+2

 

BTW The loop is not really necessary.

The other bits could be handled in similar fashion to the low two.

Could be important if one wishes to avoid saving SREG.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

Last Edited: Mon. Sep 21, 2015 - 05:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Perhaps this are noise but an instruction on a small xmega can take 1, 2, 3 or 4 clk , and for a big a ret take 5 clk. so I don't think this will cover the needed interval.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

You've also made some changes that I don't understand.  Brad's XMEGA code uses the hardware timer register TCD0_CNT, but you've replaced it with a variable.  Why? 

 

I changed it to a variable because I was having problems with the inline stuff so I thought it might help :(

 

 

joeymorin wrote:

Sounds like you need to do some studying:

Clearly yes, although this was just meant to be a simple couple of lines of assembler to get me in sync :(

 

sparrow2 wrote:

 instruction on a small xmega can take 1, 2, 3 or 4 clk

 

According to the datasheet it can take up to 7 and perhaps 1 or 2 more if interrupting a multicycle instruction

 

The interrupt response time for all the enabled interrupts is three CPU clock cycles, minimum; one cycle to finish the
ongoing instruction and two cycles to store the program counter to the stack. After the program counter is pushed on the
stack, the program vector for the interrupt is executed. The jump to the interrupt handler takes three clock cycles.

If an interrupt occurs during execution of a multicycle instruction, this instruction is completed before the interrupt is
served.

 

 

Thanks you all for the help

 

 

Last Edited: Sun. Sep 20, 2015 - 08:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
1+2+1+2=6=4+2
For the whole sequence.  I was counting only the code quoted by the OP.

 

EDIT:  @skeeve I just now see your edit in #24.  Quite correct.  My poor addled brain misread the rjmp . for an rjmp 1 /EDIT

 

sparrow2 wrote:
Perhaps this are noise but an instruction on a small xmega can take 1, 2, 3 or 4 clk , and for a big a ret take 5 clk. so I don't think this will cover the needed interval.
In post #18 I linked to an enlightening thread to which you contributed.  In it I also posted an SBRS/SBRC-based approach.  It doesn't handle 5-cycle instructions either.  As I recall, you came up with the tightest approach.

 

davidgrm wrote:
According to the datasheet it can take up to 7 and perhaps 1 or 2 more if interrupting a multicycle instruction
The datasheet text you quote is in reference to the interrupt response time, not the length of any single instruction.  @sparrow2 is correct.  The longest instruction in XMEGA is 5 cycles.

 

It is the variability of the interrupt response time which leads to jitter in the first place:

 

Quote:

 

If an interrupt occurs during execution of a multicycle instruction, this instruction is completed before the interrupt is

served.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Sep 21, 2015 - 01:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes sparrow is correct, I however was relating to the cycles in total.

Sorry if you were a bit misled by my original post. I had not counted any cycles there. It was just the concept that I was after. Once I knew how to do it I would adjust for actual cycles.

Is it not going to be 10?

 

   5 to finish current instruction - maybe only 4 if we assume at least 1 cycle has happened??

+2 push PC

+3 jump to vect

Which would explain why Brad checks for 11???

But we know there will alway be at leat 1 for the instruction,2 for the PC push and 3 for the vec jump. 6 will always be there so the jitter will be caused by the extra 1 - 4??

 

Once I have a test working with Brad's code I will test the others as they do look tighter.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It surprises me that you would need dejittering and still be able to use C.

Here is the syntax for it:

uint8_t dummy;
asm ( "..." : "=d"(dummy) : "0"((uint8_t)(delay)) : cc );

It assumes that you will do arithmetic on precisely one 8-bit variable, which can be destroyed.

 

Note that using C implies that your dejittering code will run after the prologue.

That will complicate getting upper and lower limits on the time since interrupt.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

It surprises me that you would need dejittering and still be able to use C.

 

Using C on the Xmega is more than fast enough. It is just the unknown interrupt latency that causes problems hence the jitter removal :)

 

Thanks for the help :)