8

Device: dsPIC33FJ128GP802

I have some *.s files as follows

.global _D1
.section .speex, code
_D1:
.pword 0x66C821,  0x1B0090,  0xD96C36,  0x9B60B0,  0xDD4E36,  0xBF4E53
.pword 0xD1098B,  0x719BD9,  0x873989,  0x003B69,  0x279035,  0xED4244
.pword 0xE1403C,  0x54D439,  0x826550,  0xC59627,  0xDD0432,  0x88FA29

I have declared the same in a *.h

extern void D1(void);

Now I am passing the D1 to a table read function

nowPlaying.file1 = (unsigned long) D1;
function(nowPlaying.file1);

My problem is that, if the address of D1 is above 0X8000, the routine is not correct. I tried large and small code models, but the result is the same. I think this is due to the 16 bit limitation of the pointers Is there any method to access the absolute address of D1 directly from the code. May be something like built in function or macros.

Saneesh A T
  • 1,152
  • 1
  • 13
  • 17
  • I've never used the dsPIC series, but any reason you can't use a C const array instead of the assembler? I think there's options to place that at specific code locations (if you need to for some other reason). Just thinking maybe some compiler optimization shortens the pointers because it's not expecting to be referencing data at the higher memory locations. – PeterJ Feb 02 '13 at 06:33
  • Yes, the compiler manual says all pointers including function pointers are of 16 bit. I am looking for some other method to access the memory address. If we use const array, we cann't use the upper byte of a 3 byte word in dsPIC. Another reason is that, the assembly file is generated by a computer software provided by microchip to compress speech data. – Saneesh A T Feb 02 '13 at 06:41
  • Related question: http://electronics.stackexchange.com/questions/56058/c-coding-design-function-pointers (although this is for PIC, dsPIC probably works the same) –  Feb 02 '13 at 07:52
  • Is the data at `D1` supposed to represent a function or an array of data? – The Photon Feb 03 '13 at 06:56
  • Why do you think you need another way to access the memory address? What's wrong with the one provided in the language? i.e. D1? – user207421 Feb 05 '13 at 12:06
  • @EJP Pls find the second comment of this question. Actually the compiler supports pointers of length 16 only. – Saneesh A T Feb 05 '13 at 19:26
  • 2
    @Saneesh So answer *my* questions. Is this code or is it data? If it's code, the system doesn't support it beyond the 16-bit address space, so what you're trying to do is impossible no matter how you express it. If it's data, please say so, and please try addressing it as `const short D1[]`. – user207421 Feb 06 '13 at 09:40
  • Question is off topic: belongs on stackOverflow.com. It involves compiler syntax, type declaration semantics, etc., all of which require attention by compiler writers or C language lawyers. I note that all discussion here and at meta asserting the contrary has been deleted. – user207421 Feb 11 '13 at 01:37
  • @EJP Once the embedded design SE takes off, it would be the ideal forum for this sort of question. Furthermore, I don't think "all" of the discussion re: embedded has been deleted. Downvoted, maybe. – Adam Lawrence Feb 11 '13 at 02:03
  • @Madmanguruman It's been deleted from here and from meta. If there's somewhere else it's been discussed, I never found it. I don't see why SO isn't 'ideal' or even merely the most appropriate of all the places that have ever been mentioned, and as the only person who has actually answered the question in any of those places, I do feel considerably more entitled than most to say so. – user207421 Feb 11 '13 at 07:39
  • @EJP I checked http://meta.electronics.stackexchange.com/questions/2622/are-pure-c-questions-on-topic and your answer thread is still there. It has been downvoted, but not deleted. – Adam Lawrence Feb 11 '13 at 17:49
  • Would changing the title of this question to "How do I pass the absolute address of data in flash to a function in Microchip XC16" be appropriate? – davidcary Oct 07 '15 at 14:39

3 Answers3

4

The data you're describing (full 24-bit use of program memory to store data) cannot be defined and initialized in C, and cannot read directly via C; the only way to access it is by encapsulating in a C-callable assembly function or an intrinsic.

There are really two questions here:

  1. how to play nicely with the compiler, assembler, and linker, so that when you define your 24-bit data in an assembly file as relocatable data with a symbolic name D1, rather than unnamed data at a fixed address, the compiler can see this variable to determine its address

  2. how to access the data

The 2nd question (how to access the data) is answered for 33EP parts in DS70613C and should be answered for 33FJ parts in DS70204C (but the examples in the 33FJ manual only use the low 16 bits). Here's an example code snippet from the 33EP reference manual that works for 33EP parts + should for 33FJ (I don't have a 33FJ device easily available):

(note: code uses int, whereas it would be better to use uint16_t and #include <stdint.h>)

int prog_data[10] __attribute__((space(prog))) =
  {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999};

unsigned int lowWord[10], highWord[10];
unsigned int tableOffset, loopCount;

int main(void){
    TBLPAG = __builtin_tblpage (prog_data);
    tableOffset = __builtin_tbloffset (prog_data);
    /* Read all 10 constants into the lowWord and highWord arrays */
    for (loopCount = 0; loopCount < 10; loopCount ++)
    {
        lowWord[loopCount] = __builtin_tblrdl (tableOffset);
        highWord[loopCount] = __builtin_tblrdh (tableOffset);
        tableOffset +=2;
    }
    while(1)
        ;
}

You'll note that the builtin functions __builtin_tblrdl() and __builtin_tblrdh() are used to read the low and high 16-bit words of data from a program memory location, and __builtin_tblpage() and __builtin_tbloffset() can be used to extract the page and offset of the address. In this particular example, the highWord array is always 0, and the lowWord array matches the prog_data defined and initialized in C.

Please note no pointers are used here! Although it is possible to use normal variables that are tagged with const, so that they are located by the linker in read-only program space, and so that you can read the memory using standard C pointer techniques, with the compiler automatically managing the paging registers for you, you can only store 16-bit data. You need to access the TBLRDL and TBLRDH builtin functions to get all 24 bits of data.

As for how to play nicely with the compiler/linker/etc, you have to fool the compiler and tell it it's only seeing 16-bit data. Here's an example that worked to get at the variable D1 declared elsewhere:

#define D1_SIZE 18
extern uint16_t __attribute__((space(prog))) D1[D1_SIZE];

#define READ_DATA(dst, v, len) readData(dst, __builtin_tblpage(v), __builtin_tbloffset(v), len)
void readData(uint32_t *pdst, uint16_t page, uint16_t offset, uint16_t len)
{
    TBLPAG = page;
    while (len-- > 0)
    {
        uint16_t lo = __builtin_tblrdl (offset);
        uint16_t hi = __builtin_tblrdh (offset);
        *pdst++ = (((uint32_t)(hi)) << 16) | ((uint32_t)(lo));
        offset += 2;
    }
}

...

uint32_t d1copy[D1_SIZE];
READ_DATA(d1copy, D1, D1_SIZE);

This does correctly read 24-bit values and stores them in the bottom 24 bits of a uint32_t. The extern D1 variable declared in C is a dummy variable that is only used to get at the starting address by taking advantage of the way the compiler/assembler/linker work together. The builtin functions handle the rest of the work.

What I don't know is how to automatically obtain the size of the data, since it's defined + initialized in assembly.

Jason S
  • 13,950
  • 3
  • 41
  • 68
1

Don't convert it to unsigned long and back. Asking for trouble. You're basically lying to the compiler. The correct declaration for nowPlaying.file1 is

struct
{
    // other stuff ...
    void (*D1)(void);
    // other stuff ...
} nowPlaying;

And similarly for function():

extern void function(void (*file)(void));

and remove all the typecasts.

Or, if @PeterJ suggests, it is data, it should be declared as extern short D1[] in both places: and you don't really need the assembler; you could have declared it all in C as const short D1[] = {...}; The compiler should put it into the code segment as it is const.

Kortuk
  • 13,362
  • 8
  • 60
  • 85
user207421
  • 881
  • 1
  • 8
  • 18
  • Unless I'm miss-reading something D1 isn't a pointer to a function, it's data being stored in code space. – PeterJ Feb 02 '13 at 12:47
  • 1
    @PeterJ Then it shouldn't be declared as external void D1(void), it should be defined as extern short D1[]. None of this convinces me that the question doesn't belong on SO. – user207421 Feb 02 '13 at 23:47
  • What the OP is talking about cannot be expressed in C at all, it needs to be encapsulated by a C-callable assembly function or an intrinsic. – Jason S Mar 04 '13 at 22:17
  • @JasonS There's no evidence of that in the question, and the OP has failed to clarify. – user207421 Mar 05 '13 at 21:19
  • Yes there is, if you're familiar with the PIC33F / PIC33E architectures. – Jason S Mar 06 '13 at 02:58
  • @JasonS Exactly as I said: there is no evidence in the question itself. – user207421 Mar 06 '13 at 04:28
  • ....? OP mentioned his device was a dsPIC33FJ128GP802, that provides context clues here. It's not a general C question, otherwise it would definitely be appropriate for SO. – Jason S Mar 06 '13 at 22:33
1

It seems like the simple answer is to write the subroutine in assembler. If I remember right, C30 doesn't access program memory as data using 24 bit pointers. At best it can access program memory thru the PSV window, but then you can only see the low 16 bits of each 24 bit program memory word.

If would be very simple to write a assembler routine that is callable from C30 that returns the 24 bits of data given a 24 bit program memory address. However, is your data a collection of 24 bit values or really a list of bytes that happen to be packed 3 per word? If the latter, then it's even easier. Write a assembler routine that gives you a byte-addresses view of program memory. The address would still need to be 24 bits, but the data values now only 8 bits.

Or just write the whole routine in assembler. If you're doing this kind of low level byte banging and memory packing, it is probably easier. In assembler you can just do what you want in the way the machine wants to do it. In C you have to figure out what incantations to mutter to the compiler to have it write the machine code for you. Sometimes it just easier to do it yourself directly. The dsPIC architecture is particularly easy to write assembly code for, definitely easier than a PIC 16.

Olin Lathrop
  • 310,974
  • 36
  • 428
  • 915
  • 1
    TBLRDL and TBLRDH are the key here, whether you use assembly or the `__builtin_tblrdX()` functions. I agree with you + got a kick out of your phrasing "In C you have to figure out what incantations to mutter to the compiler". Ironically, though, if you're really trying to squeeze maximum performance, sometimes the `__builtin()` functions are better, because the compiler can optimize the way it generates code, whereas it has to treat hand-coded assembly functions as black boxes it can't alter. – Jason S Mar 06 '13 at 03:04