help with ASM.

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

I'm trying to understand and use a piece of code that reads a Wave form.

 

The wave form looks like so

 

 

It works fine for what it is but has a bit of an oversight bug. It will not read anything that has more the 31 bytes. My guess is that the counting bit variable is only an 8bit register.  So my first thought was to just make it an int.

 

register int  count=0;

 

but that is not working, it returns very large numbers greater then 2k. I'm simply just not understanding how the ASM works here.  I see the overflow, my guess is that, that is used to give up if the data is more ten 255. Also I'm completely confused why this code returns twice the bit count? It seem to have been designed that way, maybe the author counted the low points as well as the high points, but I'm not sure the advantage, unless it was used to check integrity (even count). Later on in the code the author did this.

 

    if (!(count & 0x01)) {
        // If we don't get an odd number of level lengths from gcn64_receive
        // something is wrong.

...}

and after that, this.

return (count-1) / 2;

 

That tells me the opposite of what I suspected. Maybe the code counts the stop bit?

 

This is all well and good, but an over kill IMO, I'd like to possibly fix two things.

 

1) Allow the ASM to return a count over 255 (32 bytes)

2) Stop counting twice. I already check the return bit count, so I do not need this double counting.

My attempts are not baring fruit...Would anyone be able to lend a had here?

 

 register unsigned char count=0; 

		// The data line has been released. 
		// The receive part below expects it to be still high
		// and will wait for it to become low before beginning
		// the counting.
		asm volatile(
		"	push r30				\n"	// save Z
		"	push r31				\n"	// save Z
		
		"	clr %0					\n"
		"	clr r16					\n"
"initial_wait_low:\n"
		"	inc r16					\n"
		"	breq timeout			\n" // overflow to 0
		"	sbic %2, 5				\n"
		"	rjmp initial_wait_low	\n"

		// the next transition is to a high bit	
		"	rjmp waithigh			\n"

"waitlow:\n"
		"	ldi r16, %4				\n"
"waitlow_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127 (approx 50uS timeout)
		"	sbic %2, 5				\n"
		"	rjmp waitlow_lp			\n"
	
		"	inc %0					\n" // count this timed low level
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

"waithigh:\n"
		"	ldi r16, %4				\n"
"waithigh_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127
		"	sbis %2, 5				\n"
		"	rjmp waithigh_lp		\n"
	
		"	inc %0					\n" // count this timed high level
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

		"	rjmp waitlow			\n"

"overflow:  \n"
"timeout:	\n"
"			pop r31				\n" // restore z
"			pop r30				\n" // restore z

		: 	"=&r" (count)						// %0
		: 	"z" ((unsigned char volatile *)gcn64_workbuf),		// %1
			"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %2
			"I" (_SFR_IO_ADDR(PORTB)),			// %3
			"M" (TIMING_OFFSET)					// %4
		: 	"r16"
	);
	
return count;

 

This topic has a solution.
Last Edited: Mon. Feb 15, 2021 - 03:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It seems like your code has some flavor along the lines of this idea:

 

Could poll & wait for the signal to drop low  (look for a hi to low transition)...then start counting cycles(in 4us you won't get tooo many, which is good), or you could use an actual timer...but you will get less than 100 cycles in 4us, so timer not exactly needed.  Then keep counting  & looking for a rising edge (easy, since it is currently low).  If the rising edge occurs within 2us worth of counts, declare a 1, otherwise declare the rising edge to be a zero..spit the decision out or shift it out.    Now you are sitting at a high, and ready to repeat the whole process (wait for the sig to drop low)...for as many times as you want.  You can add a counter that counts how many spits you did, say after 473, quit looking and go do other things.

 

Of course if you don't know when all of this will happen, you may want to use an IRQ to detect the first falling edge.  Also, this "scheme" is not tolerant to glitches...so some work  should be added to  ignore runt pulses (if the high isn't at least  2us worth of counts, scrap the result.

 

Your idea of adding/extending a counter to  "quit" after xxx bits  (bytes) seems reasonable. 

 

also, not sure what 

clr %0	

is.    Also, if r16 is used outside of an irq, it MUST be saved/restored, or your life will become a sad circus.

  

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sat. Feb 13, 2021 - 10:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would code it a bit different.

 

 

init counter to 128

while low count down

while high count up

if number over 128 it's a 1 else it's a 0 (perhaps 126..130 is to close, so make an error(timeout)) 

if counter==0 (from up or down) error(timeout).

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

That waveform brings back memories of my attempt to reverse engineer a 433MHz Outdoor Remote Temperature Transmitter from a cheap "Weather Station". It failed because the timing varied greatly with temperature.

 

If I were to try again I would use a hardware timer with TOP value linked to the falling edge and sample the incoming bit at (TOP/2); or something similar.

 

Anyway - If you wish to convert your [count]to int (ie 16 bits) your ASM must also reflect that change:

 

so:

    "	clr %0					\n"

becomes:

    "	clr %A0					\n"
    "	clr %B0					\n"

 

    "	inc %0					\n" // count this timed high level

becomes

    "	adiw %0,1				\n" // count this timed high level

 

Last Edited: Sat. Feb 13, 2021 - 11:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I were to try again I would use a hardware timer with TOP value linked to the falling edge and sample the incoming bit at (TOP/2); or something similar

That sounds like a winner...instead of determining when the rising edge occurs, set a decision timer (started or reset on the falling edge)...at the time of the decision vote 1 or 0. 

Sparrow's is interesting, since it is a form of a debouncer & might be more immune to overall glitches (whereas the single-sampler, could be fooled at the sample moment).  Of course the single sampler could take more than 1 sample around the decision time.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

N.Winterbottom , That did help. I can now see my int value return the expect 64 bits + 1. So it acts just like the 8bit count did now. However when reading 32 bytes from this waveform returns a count of 0 %50 of the time and hangs in the ASM the other %50.

 

I changed both inc's to adiw's.

 

EDIT: I fixed the %50/%50 issue with this
        "    adiw %0,1                \n" // count this timed low level
        "    inc r17                    \n"
        "    breq overflow            \n" // > 255
        "    st z+,r16                \n"

I guess that timeout is needed

 

 

 

Maybe I  should try removing the second inc.   the max bytes return is 32 here.

 

either way i'll need an int to hold everything.   

counting twice : 256 + 256 = 512 .. so  (char)512 would be 0 

or

counting once: (char) 256 wound be 0

 

I'm also all for another model here, but would need some help getting started, my ASM skill are not that attuned yet.

Last Edited: Sun. Feb 14, 2021 - 01:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


I think this could be done almost entirely in an AVR 0-series TCB running in time-out check mode.   You set up to start a timer on the negative edge and latch the counter on positive edge.  If the counter indicates < 2us, shift a "1", ottherwise shift a "0".

 

 

 

 

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

Damn, that's Intriguing . Yeah, going to have to check that one out. Taking this one set at a time here. Only issue I see with it is the timeout problem with timers. If the wave form goes high (disconnected) the AVR will kick in the WTD if enabled or sit in an endless loop. I would need it to give up after x ms and keep running my code to handle the exit clause. 

 

 

 

Can anyone tell me how he loads the work buffer? %1 is never used.

:     "z" ((unsigned char volatile *)gcn64_workbuf),        // %1

 

Is it somehow mapped to r16

Ok , I think I get it.

 

This sets two bits per pit

xx/xxxx

or

xxxx/xx

This is just a fast way to convert the wave form in to bits.

 

Here is the decipher code.

static void gcn64_decodeWorkbuf(unsigned char count)
{
	unsigned char i;
	volatile unsigned char *output = gcn64_workbuf;
	volatile unsigned char *input = gcn64_workbuf;
	unsigned char t;

    //
    //          ________
    // ________/
    //
    //   [i*2]    [i*2+1]
    //
    //          ________________
    // 0 : ____/
    //                      ____
    // 1 : ________________/
    //
    // The timings on a real N64 are
    //
    // 0 : 1 us low, 3 us high
    // 1 : 3 us low, 1 us high
    //
    // However, HORI pads use something similar to
    //
    // 0 : 1.5 us low, 4.5 us high
    // 1 : 4.5 us low, 1.5 us high
    //
    //
    // No64 us = microseconds

	// This operation takes approximately 100uS on 64bit gamecube messages
	for (i=0; i<count; i++) {
		t = *input;
		input++;

		*output = t < *input;
		input++;

		output++;

	}
}

 

 

Last Edited: Sun. Feb 14, 2021 - 01:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did I do something wrong here?

adiw %0,1

 

Ever since I added that if I add any more c code I get this "register r24,r26,r28,r30 required" error. If I remove the line above its ok. if I remove my c code its ok.

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

ADIW (add immediate wide) only works on the upper 4 register pairs used as 16 bit registers.  Is parameter %0 a 16 bit register to which you want to add 1 (or increment)? ir R24 would increment BOTH R24/R25 as a pair of registers.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

%0 is from this  register int  count=0;

 

It prefixed like so
:     "=&r" (count)                        // %0

 

I clear it here
"    clr %A0                    \n"
"    clr %B0                    \n"

 

How can I pick a register to use?

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

oh, like this
 register int count asm("r24");
count =0;

 

 

Complies and runs but still does not count right?

 

The following code returns a count of 65 when reading 32 bytes. I should see 513 (2 per bit + 1).

 register int count asm("r24");
count =0;
		// The data line has been released.
		// The receive part below expects it to be still high
		// and will wait for it to become low before beginning
		// the counting.
		asm volatile(
		"	push r30				\n"	// save Z
		"	push r31				\n"	// save Z

		"	clr %A0					\n"
		"	clr %B0					\n"
		"	clr r16					\n"
		"	clr r17					\n"
"initial_wait_low:\n"
		"	inc r16					\n"
		"	breq timeout			\n" // overflow to 0
		"	sbic %2, 5				\n"
		"	rjmp initial_wait_low	\n"

		// the next transition is to a high bit
		"	rjmp waithigh			\n"

"waitlow:\n"
		"	ldi r16, %3				\n"
"waitlow_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127 (approx 50uS timeout)
		"	sbic %2, 5				\n"
		"	rjmp waitlow_lp			\n"

		"	adiw %0,1				\n" // count this timed low level
		"	inc r17					\n"
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

"waithigh:\n"
		"	ldi r16, %3				\n"
"waithigh_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127
		"	sbis %2, 5				\n"
		"	rjmp waithigh_lp		\n"

		"	adiw %0,1				\n" // count this timed high level
		"	inc r17					\n"
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

		"	rjmp waitlow			\n"

"overflow:  \n"
"timeout:	\n"
"			pop r31				\n" // restore z
"			pop r30				\n" // restore z

		: 	"=&r" (count)						// %0
		: 	"z" ((unsigned char volatile *)gcn64_workbuf),		// %1
			"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %2
			"M" (TIMING_OFFSET)					// %3
		: 	"r16","r17"
	);

 

I'm pretty sure its this

        "    inc r17                    \n"
        "    breq overflow            \n" // > 255

 

but that would cap me at 256. I think I need to make it if  = 512 then branch. To do that I can use my %0  but need to work out the op instruction for = 512 .

I'm not sure how to check just the high byte,

 

cpi, (high byte of %0), 2

breq timeout   

 

 

Last Edited: Sun. Feb 14, 2021 - 06:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I think "=&r" should be "=&w".

 

And as long a count is 16 int (unsigned int?) you probably don't have to specify the register (i.e., asm("r26")).

Last Edited: Sun. Feb 14, 2021 - 06:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you MUST use a register and it need to hold 16 bits then it must be a register pair.

 

ADIW is easy because it does it all for you HOWEVER those top 4 pairs of registers are heavily used by the compiler itself so they need to be save/restored and otherwise treated with care when using C.

 

There is a strange way of incrementing a 16 bit register pair in an AVR that are not the top 4 registers by subtracting a negative number

 

(Edit check how to specify low and high  with the inline asm, I think it's lo8 and hi8)

	subi	r16, low(-1)	;Increment counter
	sbci	r17, high(-1)

r17 is the top nybble here.

 

I'm getting a bit rusty not having done much work in the past few years sad

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Mon. Feb 15, 2021 - 12:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so if I just do this

register int count =0;
:     "=&w" (count)                        // %0
Then I do not have to specify a register and the ASM will clear and free it up for me?

 

I'm still not sure how to do the compare to 512?

cpc r24,(how to compare to 512)

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

Please check my edit above.

 

Have you looked at the instruction set? I don't think there is a Word (or wide) compare so you need to use 2 registers (say R24 and R25 in this case)

 

I have a compare immediate word (16bit) macro but it's for the Atmel assembler, should be able to be translated to use with the inline assembler. (you will need a 3rd scratch register)

 

;Compare immediate a register pair with a value. registers r16-r30
;
.MACRO CPIW 		;Arguments: Register pair,  Data, scratch register (ie zl,zh,1234,temp),
		ldi	@3,high (@2)
		cpi	@0,low (@2)
		cpc	@1,@3
.ENDMACRO

Edit and couple of real life example where the macro is being used

 

; Cap min volume output at 33dB(A)
	CPIW	temp, temp1, 8, temp2	; If noise is < than 33dB(A)
	brmi	Min_200mV		; then set minimum output to 0.2V
	ret
	.
	.
	.
	.
	CPIW	temp,temp1, 1500 ,temp2
	brsh	plus_3db				; Minus 3dB gain
	CPIW	temp,temp1, 500 ,temp2
	brmi	minus_3db				; Plus 3dB gain
	POP_TEMPS					; Normal, do nothing
	ret

temp, temp1 are defined as R24 and R25, temp2 is R23 in this project. You can use the register names directly of course

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Mon. Feb 15, 2021 - 12:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been looking in to that sort of thing

 

like

 

        "    adiw %0,1                \n" // count this timed high level
        "    ldi    r17,hi8 (512)        \n"
        "    cpi    r18,lo8 (512)        \n"
        "    cpc    %0,r17                \n"// = 512

 

but still not much luck.

 

Tried making the compiler generate it and got this

 

        "    cp r24,__zero_reg__        \n"// = 512
        "    ldi r18,2                \n"
        "    cpc r25,r18                \n"

 

but that just causes the chip to freeze up. I may have to breakout some form of debug, I just didn't think this was going to be such a challenge.

 

 

 

 

 

Last Edited: Mon. Feb 15, 2021 - 01:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but still not much luck.

well you are certainly only one inch from from success...you want to do a  cpi on the lows then a cpc on the high bytes (both  highs must be regs, as you know)...don't forget you need to do a test after...breq, brsh, brlo, whatever you need. I don't know if hi8 is the deal, haven't used that one (if I do asm, I use high or low)--I don't do mixed flavors.

 

If the low byte is r12, the high should be r13 (for for any word operations, the hibyt is more & odd)  ZH:ZL  is r31:r30 (typ)

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Mon. Feb 15, 2021 - 01:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My compiler says garbage at end of line of I use high or low.

 

About the registers. As mentioned above here Its not wise to select a register with asm("r24"), as they are high use registers. So I decided to go with the

:     "=&w" (count)                        // %0

but that means I have no idea what register was used but the w forces it to pick the upper 4 register pairs . Am I correct?

 

As for the branch, I use breq, how is my understanding in comments?

 

        "    adiw %0,1                \n" // count up by one
        "    ldi    r17,high (512)        \n"// load the high byte 512 in to r17
        "    cpi    r18,low (512)            \n//"load the low byte of 512 in to r18
        "    cpc    %0,r17                \n" //now since I have no idea where (count) is, reference it my %0
        "    breq overflow            \n" //if the result of cpc was true branch.

 

Should I go back and do
 register int count asm("r24");

So that I know for sure its 24/25 and keep the

:     "=&w" (count)                        // %0

now I can use

"    adiw %0,1                \n" // count up by one
        "    ldi    r17,high (512)        \n"// load the high byte 512 in to r17
        "    cpi    r18,low (512)            \n//"load the low byte of 512 in to r18
        "    cpc    r25,r17                \n" //compere high register.
        "    breq overflow            \n" //if the result of cpc was true branch.

 

Last Edited: Mon. Feb 15, 2021 - 02:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How does a cpi do a load?
Low byte of 512 is 0.

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

My compiler says garbage at end of line of I use high or low.

Correct, see my edit in Red above #14. wink

 

I have never done in line ASM, just plain old Atmel assembler projects. Maybe one or 2 C projects needed some ASM and I just added a pure ASM file to it filename.S it's a bit easier on the brain than line ASM for me.

 

I would use R16, R17 and R18 (I think they are mostly safe), R16 and R17 as the 16bit counter as in #14 (subtracting a negative number).

 

16 bit immediate compare the code in #16 with R16 as temp, R17 as temp1 and R18 as temp2 for the comparison.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Just out of interest, why inline Asm anyway? The only thing it "buys" (by being inline) is the lack of RCALL/RET but in almost every other way it makes it about 100 times more complex to implement than simple .S code.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see you are stuck dealing with OVERFLOW. The cheater's way is to count down using sbiw then you can simply check the Z flag for overflow.

Note there is no need to save R30/R31 so I have removed it.

 

#include <avr/io.h>

#define TIMING_OFFSET 34
#define GCN64_DATA_PIN PORTB

uint8_t gcn64_workbuf[512];


//GCC Allows a function to clobber registers (r18-r27, r30-r31)
//	A function must preserve registers (r2-r17, r28-r29)
//	r29:r28 (Y pointer) is used as a frame pointer (points to local data on stack)
//	Fixed registers (r0, r1):	gcc uses for fixed purposes:
//	r0 - temporary register, can be clobbered by any C code (except IRQs),
//	MAY be used to remember something for a while within one piece of assembler code
//	r1 - assumed to be always zero in any C code.
//	r1 MAY be used within one piece of assembler code, but MUST	then be cleared after use ("clr r1").

unsigned int foo (void)
{
unsigned int countdown;

		// The data line has been released.
		// The receive part below expects it to be still high
		// and will wait for it to become low before beginning
		// the counting.
		asm volatile(

		"	ldi %A0,lo8(512)		\n"
		"	ldi %B0,hi8(512)		\n"
		"	clr r16					\n"
"initial_wait_low:\n"
		"	inc r16					\n"
		"	breq timeout			\n" // overflow to 0
		"	sbic %2, 5				\n"
		"	rjmp initial_wait_low	\n"

		// the next transition is to a high bit
		"	rjmp waithigh			\n"

"waitlow:\n"
		"	ldi r16, %3				\n"
"waitlow_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127 (approx 50uS timeout)
		"	sbic %2, 5				\n"
		"	rjmp waitlow_lp			\n"

		"	sbiw %0,1				\n" // count this timed low level
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

"waithigh:\n"
		"	ldi r16, %3				\n"
"waithigh_lp:\n"
		"	inc r16					\n"
		"	brmi timeout			\n" // > 127
		"	sbis %2, 5				\n"
		"	rjmp waithigh_lp		\n"

		"	sbiw %0,1				\n" // count this timed high level
		"	breq overflow			\n" // > 255
		"	st z+,r16				\n"

		"	rjmp waitlow			\n"

"overflow:  \n"
"timeout:	\n"

		: 	"=&w" (countdown)					// %0
		: 	"z" ((unsigned char volatile *)gcn64_workbuf),		// %1
			"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %2
			"M" (TIMING_OFFSET)					// %3
		: 	"r16"
	);

    return (512 - countdown);
}

 

 

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

clawson wrote:
Just out of interest, why inline Asm anyway? The only thing it "buys" (by being inline) is the lack of RCALL/RET but in almost every other way it makes it about 100 times more complex to implement than simple .S code.

 

Well, it allows direct interaction with C variables, so you can just place it anywhere inside C code. If you call some .S subroutine you need to adhere to some calling convention, that's extra overhead.

That said, yes, it's 100x harder to write cheeky

 

For those who still want to use it, this is the ultimate source of information (it's more complete than the better known https://www.nongnu.org/avr-libc/user-manual/inline_asm.html):

 https://rn-wissen.de/wiki/index.php/Inline-Assembler_in_avr-gcc (in German)

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

clawson wrote:
why inline Asm anyway?

Why, indeed?!

 

https://www.avrfreaks.net/commen...

 

El Tangas wrote:
If you call some .S subroutine you need to adhere to some calling convention, that's extra overhead.

If that's an issue, then you've probably chosen the wrong partitioning between your 'C' and assembler ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
Well, it allows direct interaction with C variables,
.S can access globals of course (and anything local can be passed by reference in the ABI parameter list).

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

awneil wrote:
If that's an issue, then you've probably chosen the wrong partitioning between your 'C' and assembler ...

 

Well, you may want to do something that is very easy in asm but inefficient in C, like bit rotating some variable, or moving some bit using the T flag or whatever. I'd rather use inline assembly than calling some subroutine, but that's just me.

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

N.Winterbottom thx for the help.

 

That did the trick, sadly the entire time is > 1ms, killing the USB frame KA as I have to disable interrupts during this time. a mega328 with firmware usb is not going to cut it :(

 

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

I was wrong, it will work. Seems the issue is that the ASM is cutting out of the code too early?

 

There are only two condition to exit AFAICT, 1) a timeout 2 this overflow. and I'm %100 sure its the over flow, but again just a bit confused as to why.

 

I know the signal holds 32 bytes. but as shown in my debug, it is falling short 1 byte.

 

Simple math tells me 32bytes (edit) counted twice, is 512 bits. So when my timer expires (counting down) it is going to miss a bit. But not a byte.. If I have to live with it, that fine, but I'd like to get that last byte because its part of my CRC

 

The start of my debug is located at the end of the ASM


            "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),    // %2
            "M" (TIMING_OFFSET)                    // %3
        :     "r16"
    );

 PORTB &= ~1;PORTB |=1;PORTB &= ~1;PORTB |=1;

 

Last Edited: Tue. Feb 16, 2021 - 12:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simple math tells me 32bytes is 512 bits.

??? I know my brain is not what it used to be but.... 32 X 8=256 smiley Are you also counting the Stop (and start??) bits either way I don't understand the 512 count.

 

Maybe counting the edges?

 

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

LOL, sorry I forgot about the stupid double counting bits.

 

Simple math tells me 32bytes times 2 is 512 bits. I what I meant.

 

Yes the counting variable is actually counting rise and fall of each bit + stop. So the ending result would be 513 in this case. But that stop bit is never counted because it ejects early.

 

 

Also interestingly the ASM is corrupting one of my global variables. I have a

unsigned char _BRIDGE_MODE;

in my main.h

 

Somehow the ASM is setting it to a 1, I imagine that is not all its corrupting. What would do that, a register that was not free'd up ?

Last Edited: Mon. Feb 15, 2021 - 11:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

C is free to mess up anything it wants! devil Registers in particular especially R24-R31, one needs to be very carefully when mixing C and ASM.

 

I did a mix C/ASM project a while ago where I choose registers that I thought were safe, and they were until some math operation decided that it needed them more than me. sad

 

It may be a bit hard to debug, maybe put a watch on that variable to see when it gets clobbered. It may be very tedious doing single step.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

for the count issue I need to use a constant of 529, that gets me out free and clear but really makes no sense.

 

As for the ASM, isn't r16 the only register its using? Other them registers it allocates its self?  Every use of Bridge mode seems to show r24 in my lst files. I tried to push /pop/move  r24 but that made it worse.

 

pretty sure count is cobbling this. It only clobbers when I receive 32 bytes, 8 bytes is fine. If I change  from:
unsigned int count;
to
register unsigned int count;
its always clobbered.  

 

So I need to some how make the count use a safe register. If I know of what that was free I could specific the pair. I'm not really sure how I would know that. My memory is at %91 as it is.

 

 

 

 231 0148 8091 0000         lds r24,_BRIDGE_MODE  <-- so if I use the variable that get clobbered it shows as 24.
 232 014c 8111              cpse r24,__zero_reg__ <-- also used here.
 233 014e 00C0              rjmp .L22
 234 0150 E898              cbi 0x1d,0
 235                   .L22:
 236 0152 E0E0              ldi r30,lo8(gcn64_workbuf)
 237 0154 F0E0              ldi r31,hi8(gcn64_workbuf)
 238                   /* #APP */
 239                    ;  117 "../BASE/gcn64_protocol.c" 1
 240 0156 81E1                  ldi r24,lo8(529) <-- and here

 

If I try to specify the register like

 register  unsigned int count asm ("r30");

It still remains at r24.

 

 

Last Edited: Tue. Feb 16, 2021 - 12:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I said R24-R31 are heavily used by the compiler, so once your ASM code exits or if you get an interrupt those registers are likely to be changed.

 

Count cannot be a register because it needs to count above 0xff (255) so you need 2x8 bit registers, no idea how that's done with inline ASM.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

 

 

I can see the two register here.

 ldi r24,lo8(529)        
 ldi r25,hi8(529)

 

I know the effected register is 24.

 lds r24,_BRIDGE_MODE

 

I'm guessing its i just clobbering _BRIDGE_MODE.

 

I can fix it this way.

 

 
if (!_BRIDGE_MODE) {..do my none bridge code }

..do asm

if (!_BRIDGE_MODE) {..do my none bridge code }

 

to

 

unsigned char temp = _BRIDGE_MODE;
if (!temp) {..do my none bridge code }

..do asm

if (!temp) {..do my none bridge code }
_BRIDGE_MODE = temp;

 

but Just seem like a band-aid for lack of know how. Plus I think its clobbering other variables.

 

no idea how that's done with inline ASM.

 

Totally understand.  Thx for the help though, I learned a lot today. Hope yet to figure this one out.

 

Last Edited: Tue. Feb 16, 2021 - 03:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:

I know the effected register is 24.

 lds r24,_BRIDGE_MODE

 

I'm guessing its i just clobbering _BRIDGE_MODE.

Well actually no, there's no clobbering going on here.:

LDS is "Load from SRAM". BRIDGE_MODE would only get clobbered by a STS instruction. (or equivalent via a pointer)

 

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

S_K_U_N_X wrote:
Somehow the ASM is setting it to a 1,
This is where "data breakpoints" come into their own. You simply tell the simulator/debugger "break when this thing is written" and you will find the culprit who is doing it.

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

Is  Atmel-ICE still the best option there? I only have an stk-500 but that's no jtag... I see the ICE goes for %100 + these days and dragons are a bit out of sync.  Hmm, or I could get the 

ATmega328PB Xplained Mini and use it exclusively for 328. 

Last Edited: Tue. Feb 16, 2021 - 12:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Atmel-ICE, SNAP or PickKit4 all offer debugging that can do that kind of thing. But even though Snap is cheap a possibly even cheaper option is to get an Xplained-Mini/Xplained-Pro as you say. (later Xplained are now called "Curiosityxxx")

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

But first:

When you do ASM then make a good planning of what to do and when to do it, and which registers they use. 

Like the C compiler make some definitions of registers there are temporary which hold data. So even when you don't make subroutines then think

like that and write input output registers and registers used, in the "header" (a bit like you in forth show stack use).  

 

Add: 

If timing is critical then also max clk for this part.

 

 

Last Edited: Tue. Feb 16, 2021 - 01:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Whoa..  PickKit4  LOL I used the 3 on mplab for pic chips... That works on avr studio? I know microchip bought ateml but I'd like to stay on avr studio for now. Are  Snap/PickKit4  exclusive to mplab ide whereas  Xplained   works with atmel studio. 

Last Edited: Tue. Feb 16, 2021 - 01:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
That works on avr studio? I know microchip bought ateml but I'd like to stay on avr studio for now. Are  Snap/PickKit4  exclusive to mplab ide whereas  Xplained   works with atmel studio. 
That is one bit where you need to do some research. I think their goal is to make all work equally well on either (MCS7 / MPLABX) but at present it seems some devices work "better" on one or the other.

 

From Mchip site it looks like you may need this: https://www.microchipdirect.com/...

 

Given that Snap is https://www.microchipdirect.com/... about $10 less then it would seem like the "better" option.

 

This looks interesting: https://microchipdeveloper.com/s...

Last Edited: Tue. Feb 16, 2021 - 03:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, one more question here for my debugging effort.

 

How is ths used?

:     "z" ((unsigned char volatile *)gcn64_workbuf),        // %1

 

I see no reference to %1 anywhere. I do see this in my lst file.

  16 0002 E050              subi r30,lo8(-(gcn64_workbuf))
  17 0004 F040              sbci r31,hi8(-(gcn64_workbuf))

 

but as N.Winterbottom  pointed out I do not need that.

 

"Note there is no need to save R30/R31 so I have removed it. "

 

My best guess is that This op code sets the state of the bits in gcn64_workbufer

st z+,r16

 

 

 

Last Edited: Wed. Feb 17, 2021 - 03:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
LDS is "Load from SRAM". BRIDGE_MODE would only get clobbered by a STS instruction. (or equivalent via a pointer)

When you increased the number of cycles; did you also increase the corresponding size of {gcn64_workbuf} ?

 

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

Yes I did. I'm going over each bit with my analyzer to figure out why 529 is needed.  So I needed to know where to stick the debug. This seems to work well.

CLR_DBG
        "    sbic %2, 5                \n"

and

SET_DBG
        "    sbis %2, 5                \n"

 

 

EDIT: the document was wrong, it's 32 bytes + 2 for crc, the doc I read said 32 including crc.  That could be taking both ways... I hate English sometimes.

 

The solution marked as correct still stands. I'm good, thx for all the help.

 

Last Edited: Wed. Feb 17, 2021 - 07:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good you solved it :)

 

So this will be a general input:

 

If you have problems because counter overflow then clear in every time you get a char.

If you know the buffers place at "compile time" then you know which value of Z is the end (and here where it's smaller than 256 so you only need to check ZL)

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

S_K_U_N_X wrote:
That could be taking both ways... I hate English sometimes.

That's why such things are so much better illustrated with a diagram!

 

(trouble then is when the text description doesn't match the diagram frown )

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...