WinAVR with FPSLIC Problem - Reading from Program Space

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

Hi there,

I use WinAVR Version 20070525 to compile a program which must run on a FPSLIC.

Everything works fine, but when reading data that I stored in program space (PROGMEM) I read the wrong data.

Looking at the listing file, the GCC created the following code to read from program space:

ld R24,Z

The ld instruction is used to load data from data space and not program space.

Shouldn't the GCC used the LPM instruction, like so:?

lpm R24,Z

Reading the avr-lic-user-manual, they say under 6.14 (pgmspace.h> that the target must support the LPM instruction. The FPSLIC does support the LPM instruction.

So why does GCC created the wrong code?

Greetings
Jacques

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

You must use the pgm_read_*() or *_P() family of functions to dereference accesses to PROGMEM data.

- Luke

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

Hi Luke, thans for your reply

I forgot to mention that I point to the data in program memory using the PGM_P macro.

Here is the code:

void USB__Get_Descriptor(USB_Setup_Typedef_P temp_Setup)
{                                 
	unsigned int temp_Counter;
	PGM_P temp_Address;

/* INITIALIZE VARIABLES */
	temp_Address=0;

/* GET THE START ADDRESS OF THE DESCRIPTOR */
	switch (*((unsigned char*)&temp_Setup->Value+1))
	{
		case USB__DEVICE_DESCRIPTOR:
			temp_Address=(PGM_P)&Device_Descriptor;
		break;                             
		
		case USB__CONFIGURATION_DESCRIPTOR:        
			temp_Address=(PGM_P)&Configuration_Descriptor;
		break;
		
		case USB__STRING_DESCRIPTOR:
			switch (*((unsigned char*)&temp_Setup->Value+0))
			{
				case 0x00:
					temp_Address=(PGM_P)&String0_Descriptor;
				break;

				case 0x01:
					temp_Address=(PGM_P)&String1_Descriptor;
				break;

				case 0x02:
					temp_Address=(PGM_P)&String2_Descriptor;
				break;
			}
		break;
	}

/* GET THE NUMBER OF BYTES TO SEND */
	switch (*((unsigned char*)&temp_Setup->Value+1))
	{
		case USB__CONFIGURATION_DESCRIPTOR:        
		/* GET THE NUMBER OF BYTES TO SEND */
			USB.Number_of_Bytes_to_Send=32;
		break;
		
		default:
		/* GET THE NUMBER OF BYTES TO SEND */
			USB.Number_of_Bytes_to_Send=temp_Address[0];
		break;
	}		

/* BUILD THE DESCRIPTOR AND START SENDING THE DESCRIPTOR */
	if (temp_Address!=0)
	{	
	/* CORRECT THE SIZE */				
		if (USB.Number_of_Bytes_to_Send > temp_Setup->Length)
			USB.Number_of_Bytes_to_Send=temp_Setup->Length;
		
	/* GET THE DATA */
		for (temp_Counter=0; temp_Counter

This is a subset of the instructions generated:

/* GET THE NUMBER OF BYTES TO SEND */
	switch (*((unsigned char*)&temp_Setup->Value+1))
     a4a:	82 30       	cpi	r24, 0x02	; 2
     a4c:	21 f4       	brne	.+8      	; 0xa56 
     a4e:	e0 e0       	ldi	r30, 0x00	; 0
     a50:	f0 e0       	ldi	r31, 0x00	; 0
	{
		case USB__CONFIGURATION_DESCRIPTOR:        
		/* GET THE NUMBER OF BYTES TO SEND */
			USB.Number_of_Bytes_to_Send=32;
     a52:	80 e2       	ldi	r24, 0x20	; 32
     a54:	03 c0       	rjmp	.+6      	; 0xa5c 
		break;
     a56:	e0 e0       	ldi	r30, 0x00	; 0
     a58:	f0 e0       	ldi	r31, 0x00	; 0
		
		default:
		/* GET THE NUMBER OF BYTES TO SEND */
			USB.Number_of_Bytes_to_Send=temp_Address[0];
     a5a:	80 81       	ld	r24, Z
     a5c:	80 93 9f 04 	sts	0x049F, r24
		break;
	}		

As can be seen at address a5a, the GCC generated the ld instruction, why not the LPM instruction??

The array that is stored in program memory is defined as :

const unsigned char PROGMEM Device_Descriptor[];

Please help me.
Jacques

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

Joeks wrote:
As can be seen at address a5a, the GCC generated the ld instruction, why not the LPM instruction??

Because you didn't do what Luke just told you. To access data that's been placed in PROGMEM you must use pgm_read_*() helper functions

Did you read Dean's PROGMEM tutorial in the Tutorial Forum. If not it would be a VERY good idea.

(what you are attempting to do would only work in IAR as far as I know as it's the only compiler that uses multi-function pointers where the compiler would recognise that a pointer to program space needed additional LPM code to access it)

Cliff

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

Thanks Cliff,

I will go and read Dean's tutorial.

Quote:
Because you didn't do what Luke just told you. To access data that's been placed in PROGMEM you must use pgm_read_*() helper functions

Luke said the following:

Quote:
You must use the pgm_read_*() or *_P() family of functions to dereference accesses to PROGMEM data.

Either the pgm_read_* OR the *_P functions, I used the PGM_P function. Also the avr-gcc user manual tells that you can use the PGM_P "function" to point to data in program space.

Well, I'm off to go and read the tutorial...

Jacques

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

Sorry, I guess I wasn't clear enough - PGM_P is not a function - it's a data type.

Unfortunately, "pointing" to flash is insufficient. The GCC compiler back-end, which is common to every microprocessor architecture supported by GCC, is designed for Von Neumann machines, and fundamentally incapable of automatically generating code that differentiates between address spaces.

You have to be explicit, every step of the way: When you want to initialize data stored in Flash, when you want to point to that data, and when you want to fetch that data out of Flash.

The "_P" functions I was talking about were:
memcpy_P
strcpy_P, strncpy_P
memcmp_P
strcmp_P, strncmp_P
strcat_P, strncat_P
printf_P (and related stdio functions)

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

Luke,

I solved the problem using the pgm_read_byte/pgm_read_word functions, like you told me.

Thanks for your help
Jacques