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 estoy pasando 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 grande y pequeño, pero el resultado es el mismo. Creo que esto se debe a la limitación de 16 bits de los punteros. ¿Hay algún método para acceder a la dirección absoluta de D1 directamente desde el código? Puede ser algo así como funciones integradas o macros.
microcontroller
pic
microchip
compiler
Saneesh AT
fuente
fuente
D1
supone que los datos representan una función o una matriz de datos?const short D1[]
.Respuestas:
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 es encapsulando en una función de ensamblaje invocable en C o en una función intrínseca.
Realmente hay dos preguntas aquí:
cómo jugar bien con el compilador, ensamblador y enlazador, 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óncómo acceder a los datos
La segunda pregunta (cómo acceder a los datos) se responde para las partes 33EP en DS70613C y se debe responder para las partes 33FJ en DS70204C (pero los ejemplos en el manual 33FJ solo usan los 16 bits bajos). Aquí hay un fragmento de código de ejemplo del manual de referencia 33EP que funciona para las partes 33EP + debería para 33FJ (no tengo un dispositivo 33FJ fácilmente disponible):
(nota: el código usa
int
, mientras que sería mejor usaruint16_t
y#include <stdint.h>
)Notará que las funciones incorporadas
__builtin_tblrdl()
y__builtin_tblrdh()
se utilizan para leer las palabras de datos de 16 bits altas y bajas de una ubicación de memoria del programa, y__builtin_tblpage() and __builtin_tbloffset()
se pueden usar para extraer la página y el 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 no se utilizan punteros aquí! Aunque es posible utilizar variables normales con las que está etiquetado
const
, de modo que el enlazador las ubique en el espacio del programa de solo lectura, y para que pueda leer la memoria utilizando técnicas de puntero C estándar, 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 incorporadas TBLRDL y TBLRDH para obtener los 24 bits de datos.En cuanto a cómo jugar bien con el compilador / enlazador / etc., debe engañar al compilador y decirle que solo está viendo datos de 16 bits. Aquí hay un ejemplo que funcionó para llegar a la variable D1 declarada en otro lugar:
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 utiliza para llegar a la dirección inicial aprovechando la forma en que el compilador / ensamblador / enlazador 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.
fuente
No lo convierta a unsigned long and back. Pidiendo problemas. Básicamente le estás mintiendo al compilador. La declaración correcta para nowPlaying.file1 es
Y de manera similar para function ():
y eliminar todos los encasillados.
O, si @PeterJ sugiere, son datos, deben declararse como D1 corto externo [] en ambos lugares: y realmente no necesita el ensamblador; podrías haberlo declarado todo en C como const short D1 [] = {...}; El compilador debe ponerlo en el segmento de código, ya que es constante.
fuente
Parece que la respuesta simple es escribir la subrutina en ensamblador. Si no recuerdo mal, 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 puede ver los 16 bits bajos de cada palabra de memoria de programa de 24 bits.
Sería muy simple escribir una rutina de ensamblador que se pueda llamar desde C30 que devuelva los 24 bits de datos dados 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 es lo último, entonces es aún más fácil. Escriba una rutina de ensamblador que le brinde una vista de direcciones de bytes de la memoria del programa. La dirección aún necesitaría ser de 24 bits, pero los valores de los datos ahora solo son de 8 bits.
O simplemente escriba toda la rutina en ensamblador. Si está haciendo este tipo de golpe de bytes de bajo nivel y empaque de memoria, probablemente sea más fácil. En ensamblador, puede hacer lo que quiera de la manera en que la máquina lo quiere hacer. En C tienes que averiguar qué encantamientos murmurar al compilador para que escriba el código de máquina por ti. A veces es más fácil hacerlo tú mismo directamente. La arquitectura dsPIC es particularmente fácil de escribir para el código de ensamblaje, definitivamente más fácil que un PIC 16.
fuente
__builtin_tblrdX()
funciones. Estoy de acuerdo contigo + me gustó tu frase "En C tienes que averiguar qué encantamientos murmurar al compilador". Irónicamente, sin embargo, si realmente está tratando de exprimir el máximo rendimiento, a veces las__builtin()
funciones son mejores, porque el compilador puede optimizar la forma en que genera el código, mientras que tiene que tratar las funciones de ensamblaje codificadas a mano como cajas negras que no puede alterar. .