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.
Best Answer
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:
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 addresshow 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 useuint16_t
and#include <stdint.h>
)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:
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.