C code meets assembler - And falls down

Go To Last Post
61 posts / 0 new

Pages

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

As you know I'm a ASM person.
All apps. that go to sleep/idle to save power will benefit from a fast program, and if that is important ASM will allways be in the picture!

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

david.prentice wrote:

You devise a test suite.
You devise a set of input data.
You specify the expected output.

You run the test suite.
You discover 'where' and 'how' your code fails.

More importantly, your readers would have some idea what 'order shambolic' might mean.

It means they come out in a shambles. A mess. I have prepared test suites and I have prepared test data and I have flushed it down the USB plumbing. The test data worked (eventually). The live interaction with external hardware did not.

Quote:
Yes. It requires you to reduce your code down to a minimal set that reproduces your problem.

I don't know how to make it any simpler. It has to fetch the data, and get it right.

How can I simplify that?

Scroungre

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

clawson wrote:

Agree entirely. I think the only "regular" topic here where Asm seems the only choice is the already mentioned video generation - where every single cycle counts (the faster you can do it the more rows and columns you can have). But I'd be hard pushed to think of any application where "C performance" is otherwise not acceptable.

In this case, the other AVRs have interrupt routines running that really do care about every cycle. I completely rewrote an effective 'switch' statement in assembler so that the fall-through would be the last desired result and would lead into the subroutine without a branch or jump because that saved three cycles in a worst-case scenario.

Sometimes it is important. This is why I'm using six AVRs in parallel on the input data side...

Scroungre

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

You claim that your ASM function 'works'.

I suggested a C equivalent. It will compile to give you very similar ASM sequences to your original ASM.

I cannot believe that your AVR is going to behave any differently.

I am sceptical however of a 'strobe' with an unspecified polarity. And if your Slave AVR has any particular timing considerations.

I would also be suspicious of a C newbie that uses pointers inappropriately. Likewise using global variables that may share name spaces. In other words, there is a whole lot of other things that can corrupt data.

Design an ASM routine that uses local variables and checks array bounds.
Design a C routine that uses local variables and checks array bounds.

The C will probably use more cycles and instructions. I doubt that the functionality will be affected at all.

Anyway, as suggested days ago, you can always leave your function coded in ASM. Simply add the appropriate "asmfunction.S" to your project.

David.

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

david.prentice wrote:
You claim that your ASM function 'works'.

I do, and it did. It worked fine talking to the other AVRs - What it didn't do was speak USB.

Quote:
I am sceptical however of a 'strobe' with an unspecified polarity. And if your Slave AVR has any particular timing considerations.

That's the point of toggling. It gets around the whole timing thingy. Yes, both chips could just go away - And still meet the spec - and come back an hour later. More details available upon request.

Quote:
I would also be suspicious of a C newbie that uses pointers inappropriately. Likewise using global variables that may share name spaces. In other words, there is a whole lot of other things that can corrupt data.

I am a C newbie. Be suspicious all you want.

Quote:
Anyway, as suggested days ago, you can always leave your function coded in ASM. Simply add the appropriate "asmfunction.S" to your project.
...

David.

And if my assembler monkeys with the SRAM you think the rest of the program is just going to play nice with it? Heh.

I don't think so. I could be wrong.

Scroungre

PS - Since I wrote both the chips, I can specify the initial state of the "read" and "ready" strobes, and I did. They also both know exactly how many bytes are supposed to be transmitted. That part works fine... In assembler. S.

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

Incidentally, Skeeve got it right some time ago, and I didn't give suitable credit. The other AVR is doing time-sensitive stuff. S.

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

Can you wind back and post the generated Asm both for your original C attempt and also for David's "this should be like the Asm C code". Can you not see how (a) the original code was not written like the Asm and (b) that David's code produces something very close to the Asm you are trying to mimic? In what way doesn't David's code deliver what you are looking for? When we know that suggestions can be made as to how the C might be changed to be even closer.

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

I programmed earlier micros using assembler (such as the 8080,80186 families) because the PC compilers (C, pascal, etc) were hard to use for embedded targets. I found myself structuring my code and data in ways to emulate what the C language itself would do.

The GNU GCC compiler and libraries for the AVR are well suited for embedded use, and there are many functions in AVR LibC that I would not care to re-create in assembler for myself (some of these ARE written in assembler anyway). You can of course call a C library function from Assembler, but it's easier to just write the calling code in C.

Often when I look at the output of the compiler I see a similar pattern, the compiler will copy the source operands to specific registers, perform the operations, and copy the results back to the original locations EVEN IF THE ORIGINAL LOCATIONS WERE REGISTERS! OTOH the compiler will often find code segments that are common to several sections of the C code and generate only one copy of this in assembler inserting the required calls or jumps to access it from the various sections to save code space. That's something I might miss in coding it myself in assembler and certainly in C.

abcminiuser wrote:

You need to set the compiler into GNU99 standards mode, and not the default C89 standard (or pure C99 standard). To do that, pass --std=gnu99 on the command line to avr-gcc when compiling.
- Dean :twisted:

That's the first time I've heard about this GCC switch. What exactly does it do, and why isn't it the default?

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

Quote:

That's the first time I've heard about this GCC switch. What exactly does it do, and why isn't it the default?

http://gcc.gnu.org/onlinedocs/gc...

I forget all the GNU dialect extensions but one that springs to mind is named struct initialisers using : rather than . Another very popular one is anonymous fields in structs.

A list of all the GCC dialect extensions is here:

http://gcc.gnu.org/onlinedocs/gc...

BTW the reason most people switch to 99 rather than the default 89 is for the:

for (int i=0; i<10; i++) {

construct. (there are many other reasons but that's the most common one I guess). If you use that in -std=c89 or -std=gun89 you get:

test.c:6: error: 'for' loop initial declaration used outside C99 mode

Both Mfile and AVR Studio (well the old one at least! ;-)) pass -std=gnu99 by default.

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

Yay! OP here. I believe I have fixed it, although there's a few things I still don't quite understand.

Anyhow, there were two interacting problems, which added to the confusion. Problem #1 was indeed [i] - Making it volatile fixed most of the trouble.

#2 was a bit more fundamental, and involved a bit of arcane USB business, as it seems to like considering eight bytes as two 32-bit integers and shipping them out little-endian, whereas I really want them big-endian.

What I don't understand is why it didn't do that with test arrays. Initializing thus:

static uint8_t Count[24] = { 0x11, 0x22, 0x33, 0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,
0xF1, 0xE2, 0xD3, 0xC4,0xB5,0xA6,0x97,0x88,0x79,0x6A,0x5B,0x4C };

worked fine, providing all the bytes in initialized order, but once I've gotten my 'Count' array from the outside world, as it is, I have to do this:

	temp = Count[i];					// Swap two bytes
	Count[i] = Count[(i - 2)];			//
	Count[(i - 2)] = temp;				//
	temp = Count[(i - 1)];				// And do it again. 
	Count[(i - 1)] = Count[(i - 3)];	// 
	Count[(i - 3)] = temp;				// 

I'm not sure why that should be - I'm rather surprised, actually - but it works now.

Obviously, it could be neater, faster, etc., and it would be nice to be able to turn the optimization back on (at the moment, that still throws a huge wrench into the works) but hey. It works! Yay! :)

Thanks for all your comments. Even those I didn't end up using were still educational.

Thanks again!

Scroungre

Pages