Dirección absoluta de una función en Microchip XC16

8

Dispositivo: dsPIC33FJ128GP802

Tengo algunos archivos * .s de la siguiente manera

.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

He declarado lo mismo en un * .h

extern void D1(void);

Ahora paso el D1 a una función de lectura de tabla

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

Mi problema es que, si la dirección de D1 está por encima de 0X8000, la rutina no es correcta. Probé modelos de código grandes y pequeños, pero el resultado es el mismo. Creo que esto se debe a la limitación de 16 bits de los punteros. ¿Existe algún método para acceder a la dirección absoluta de D1 directamente desde el código? Puede ser algo así como función incorporada o macros.

    
pregunta Saneesh A T

3 respuestas

4

Los datos que está describiendo (uso total de 24 bits de la memoria del programa para almacenar datos) no se pueden definir e inicializar en C, y no se pueden leer directamente a través de C; La única forma de acceder a él es encapsulando en una función de ensamblaje que se pueda llamar en C o en una intrínseca.

Hay realmente dos preguntas aquí:

  1. cómo jugar bien con el compilador, el ensamblador y el vinculador, de modo que cuando defina sus datos de 24 bits en un archivo de ensamblaje como datos reubicables con un nombre simbólico D1 , en lugar de datos sin nombre en una dirección fija, el compilador puede ver esta variable para determinar su dirección

  2. cómo acceder a los datos

La segunda pregunta (cómo acceder a los datos) se responde para las partes 33EP en DS70613C y debe responder a las partes de 33FJ en DS70204C (pero los ejemplos en el manual de 33FJ solo usan la bajo 16 bits). Aquí hay un fragmento de código de ejemplo del manual de referencia de 33EP que funciona para 33EP parts + debería para 33FJ (no tengo un dispositivo 33FJ fácilmente disponible):

(nota: el código usa int , mientras que sería mejor usar uint16_t y #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)
        ;
}

Observará que las funciones integradas __builtin_tblrdl() y __builtin_tblrdh() se usan para leer las palabras de datos de 16 bits de alta y baja de una ubicación de la memoria del programa, y __builtin_tblpage() and __builtin_tbloffset() se pueden usar para extraer la página y desplazamiento de la dirección. En este ejemplo particular, la matriz highWord siempre es 0, y la matriz lowWord coincide con los prog_data definidos e inicializados en C.

¡Tenga en cuenta que aquí no se utilizan punteros! Aunque es posible usar variables normales que están etiquetadas con const , de modo que el vinculador las ubique en el espacio de programa de solo lectura, y para que pueda leer la memoria utilizando técnicas estándar de puntero C, con el compilador administrando automáticamente los registros de paginación para usted, solo puede almacenar datos de 16 bits. Debe acceder a las funciones integradas TBLRDL y TBLRDH para obtener los 24 bits de datos.

En cuanto a cómo jugar bien con el compilador / enlazador / etc, tienes que engañar al compilador y decirle que solo está viendo datos de 16 bits. Aquí hay un ejemplo que funcionó para obtener la variable D1 declarada en otro lugar:

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

Esto lee correctamente los valores de 24 bits y los almacena en los 24 bits inferiores de un uint32_t. La variable externa D1 declarada en C es una variable ficticia que solo se usa para obtener la dirección de inicio aprovechando la forma en que el compilador / ensamblador / vinculador trabaja en conjunto. Las funciones integradas manejan el resto del trabajo.

Lo que no sé es cómo obtener automáticamente el tamaño de los datos, ya que está definido + inicializado en el ensamblaje.

    
respondido por el Jason S
1

No lo conviertas a unsigned long y back. Pidiendo problemas. Básicamente estás mintiendo al compilador. La declaración correcta para nowPlaying.file1 es

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

Y de manera similar para function ():

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

y elimina todas las tipologías.

O, si @PeterJ sugiere, son datos, se deben declarar como externos cortos D1 [] en ambos lugares: y realmente no necesita el ensamblador; podría haberlo declarado todo en C como const corto D1 [] = {...}; El compilador debe colocarlo en el segmento de código ya que está constante.

    
respondido por el user207421
0

Parece que la respuesta simple es escribir la subrutina en el ensamblador. Si recuerdo bien, el C30 no accede a la memoria del programa como datos utilizando punteros de 24 bits. En el mejor de los casos, puede acceder a la memoria del programa a través de la ventana de PSV, pero solo se pueden ver los 16 bits bajos de cada palabra de memoria de programa de 24 bits.

Si sería muy sencillo escribir una rutina de ensamblador que se pueda llamar desde C30 que devuelva los 24 bits de datos a una dirección de memoria de programa de 24 bits. Sin embargo, ¿son sus datos una colección de valores de 24 bits o realmente una lista de bytes que se empaquetan 3 por palabra? Si este último, entonces es aún más fácil. Escriba una rutina de ensamblador que le dé una vista de direcciones de bytes de la memoria del programa. La dirección aún tendría que ser de 24 bits, pero los valores de los datos ahora solo son de 8 bits.

O simplemente escriba la rutina completa en el ensamblador. Si está realizando este tipo de agrupación de memoria y bytes de bajo nivel, probablemente sea más fácil. En ensamblador, solo puede hacer lo que quiera de la manera que la máquina quiere hacerlo. En C tienes que descubrir qué conjuros para murmurar al compilador para que escriba el código de la máquina por ti. A veces es más fácil hacerlo tú mismo directamente. La arquitectura dsPIC es particularmente fácil de escribir código de ensamblaje, definitivamente más fácil que un PIC 16.

    
respondido por el Olin Lathrop

Lea otras preguntas en las etiquetas