Microchip XC16中函数的绝对地址


8

器件:dsPIC33FJ128GP802

我有一些* .s文件,如下所示

.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

我在* .h中声明了相同的内容

extern void D1(void);

现在我将D1传递给表读取功能

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

我的问题是,如果D1的地址大于0X8000,则例程不正确。我尝试了大型和小型代码模型,但结果是相同的。我认为这是由于指针的16位限制引起的。是否有任何方法可以直接从代码中访问D1的绝对地址。可能类似于内置函数或宏。


我从未使用过dsPIC系列,但是由于任何原因您不能使用C const数组而不是汇编程序?我认为可以将其放置在特定的代码位置(如果出于某些其他原因需要)。我只是想也许一些编译器优化会缩短指针,因为它不希望在更高的内存位置引用数据。
PeterJ

是的,编译器手册指出,包括函数指针在内的所有指针均为16位。我正在寻找其他方法来访问内存地址。如果使用const数组,则无法在dsPIC中使用3字节字的高字节。另一个原因是,汇编文件是由微芯片提供的计算机软件生成的,用于压缩语音数据。
Saneesh

相关问题:electronics.stackexchange.com/questions/56058/…(尽管这是针对PIC的,但dsPIC的工作原理可能相同)

处的数据D1应该代表函数还是数据数组?
Photon

2
@Saneesh所以回答我的问题。这是代码还是数据?如果是代码,则系统不支持超出16位地址空间的代码,因此无论您如何表达,您都无法尝试执行。如果是数据,请这样说,然后尝试将其寻址为const short D1[]
user207421

Answers:


4

您正在描述的数据(使用程序存储器全24位存储数据)无法在C中定义和初始化,也无法通过C直接读取;访问它的唯一方法是通过封装在C调用的汇编函数或内部函数中。

这里确实有两个问题:

  1. 如何与编译器,汇编器和链接器配合使用,以便当您在汇编文件中将24位数据定义为具有符号名的可重定位数据D1,而不是固定地址处的未命名数据时,编译器可以看到此变量确定其地址

  2. 如何访问数据

所述第二质询(如何访问数据)被应答了33EP份DS70613C并应在回答为33FJ份DS70204C(但在33FJ示例手册只使用低16位)。这是33EP参考手册中的示例代码片段,适用于33EP零件+应该适用于33FJ(我没有容易获得的33FJ设备):

(注意:代码使用int,而使用uint16_t和会更好#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)
        ;
}

您会注意到,内置函数__builtin_tblrdl()__builtin_tblrdh()用于从程序存储器位置读取数据的低16位字和高16位字,并且 __builtin_tblpage() and __builtin_tbloffset()可以用于提取地址的页和偏移量。在此特定示例中,highWord数组始终为0,lowowWord数组与在C中定义和初始化的prog_data相匹配。

请注意,此处不使用任何指针!尽管可以使用带有标记的普通变量const,以便链接器将其定位在只读程序空间中,以便您可以使用标准C指针技术读取内存,而编译器会自动管理分页寄存器对您来说,您只能存储16位数据。您需要访问TBLRDL和TBLRDH内置函数来获取所有24位数据。

至于如何很好地使用编译器/链接器/等,您必须欺骗编译器并告诉它它仅看到16位数据。这是一个示例,用于获取在其他地方声明的变量D1:

#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);

这样可以正确读取24位值并将其存储在uint32_t的低24位中。在C中声明的extern D1变量是一个虚拟变量,通过利用编译器/汇编器/链接器的协同工作方式来获取起始地址。内置函数处理其余工作。

我不知道如何自动获取数据的大小,因为它是在程序集中定义和初始化的。


1

不要将其转换为unsigned long并返回。自找麻烦。您基本上是在对编译器撒谎。nowPlaying.file1的正确声明是

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

同样对于function():

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

并删除所有类型转换。

或者,如果@PeterJ建议,它是数据,则在两个地方都应将其声明为extern short D1 []:并且您实际上不需要汇编程序;您可以在C中将其全部声明为const short D1 [] = {...}; 编译器应将其作为const放入代码段中。


除非我错读了一些东西,否则D1不是指向函数的指针,而是将数据存储在代码空间中。
PeterJ

1
@PeterJ然后,不应将其声明为外部void D1(void),而应将其定义为extern short D1 []。这些都没有使我相信这个问题不属于SO。
user207421

OP所说的完全不能用C表示,它需要用C调用的汇编函数或内在函数封装。
杰森S

@JasonS在这个问题上没有任何证据,OP尚未澄清。
user207421 2013年

是的,如果您熟悉PIC33F / PIC33E架构,那就可以了。
杰森S

0

似乎简单的答案是在汇编器中编写子例程。如果我没记错的话,C30不会使用24位指针将程序存储器作为数据访问。充其量它可以通过PSV窗口访问程序存储器,但随后您只能看到每个24位程序存储器字的低16位。

如果编写一个汇编程序例程非常简单,则可从C30调用该例程,该例程在给定24位程序存储地址的情况下返回24位数据。但是,您的数据是24位值的集合还是实际上是每个字打包3个字节的列表?如果是后者,那就更容易了。编写一个汇编程序例程,该例程为您提供程序存储器的字节地址视图。该地址仍然需要为24位,但是数据值现在只有8位。

或者只是在汇编器中编写整个例程。如果您正在执行这种低级字节敲打和内存打包,则可能会更容易。在汇编程序中,您可以按照机器要执行的操作来执行所需的操作。在C语言中,您必须弄清楚要编译器发出什么咒语才能让编译器为您编写机器代码。有时候直接自己做会更容易。dsPIC体系结构特别易于编写汇编代码,这绝对比PIC 16更容易。


无论您使用汇编还是__builtin_tblrdX()函数,TBLRDL和TBLRDH都是关键。我同意您的观点,并且措辞不佳:“在C语言中,您必须弄清楚要对编译器喃喃自语的意图”。但是,具有讽刺意味的是,如果您真的想最大限度地__builtin()发挥性能,有时功能会更好,因为编译器可以优化其生成代码的方式,而必须将手工编码的汇编函数视为黑盒,因此无法更改。
詹森·S
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.