[DS] Multithread en la DS

Buenas.

Desde hace unas semanas, he estado trabajando en algo que echaba en falta en la DS: Programacion Multi-hilo y la verdad que me ha costado lo suyo, debido a que he tenido que aprender ensamblador de ARM y algunas cosillas sobre el sistema de la DS, pero ya puedo decir que tengo un programa estable, que trabaja con tres hilos (el del main, uno activado por un timer y otro por vblank)

Lo malo es que ultimamente, tengo relativamente poco tiempo para dedicar al avance de esta librería Multithread, que al igual que eSound, se apoya en LIBNDS reemplazando justo lo necesario.

CARACTERISTICAS

El sistema que estoy trabajando, utiliza las interrupciones para llevar a cabo la conmutacion entre hilos, se reserva el TIMER3 para poder hacer esta tarea mas agil (lo tengo programado a 1000Hz).

Cada hilo, evidentemente, necesita su propia pila y se pueden crear hasta 31 hilos nuevos, tal y como yo lo tengo definido (el hilo 0, corresponde al main y el 32, es asignado a una tarea 'dummy')

Se dispone de un sistema de prioridades que es efectivo a la hora de reactivar un hilo (lo cual permite que un hilo no pueda ser interrumpido, si su prioridad es mas alta)

FUNCIONES

De forma provisional, contamos con:

void irqInit_Multithread(); :-> equivalente a IrqInit de LIBNDS, pero para multitarea

int Create_Thread(int priority,void (*func)(void *arg),void *arg,void *stack,int size); :-> Crea un hilo asignandole la prioridad, funcion de entrada, argumento de entrada (puede ser NULL), direccion de la pila y tamaño. Devuelve el ID de la tarea o -1 si no se pudo crear

void multithread_disabled(); :-> Importante funcion que prohibe el cambio de hilo (util por ejemplo, para acceder a una funcion que es llamada desde varios hilos pero que no soporta reentradas)

multithread_enabled(); :-> Habilita el cambio de tareas. Si detecta un cambio de hilo pendiente, espera a que se produzca


void Sleep_Thread(); :-> Pone a dormir el hilo.
void Wait_Thread(); :-> Pone el hilo en espera

Sleep necesita de iWakeup para volver a despertar, mientras que Wait puede ser activado por un simple cambio de hilo (su utilidad sería aprovechar los tiempos muertos entre hilos para hacer un determinado proceso y luego, devolver el control a otro hilo)


int iWakeup_Thread(int n_thread); :-> Activa un hilo dormido o en espera. La 'i' inicial indica que puede ser utilizada en interrupciones, pero no causa por si misma la activación del hilo (eso se produce después en el gestor de hilos, que es llamado desde el vector de interrupciones, si procede)

Y esto es todo lo que tengo funcionando, por el momento :). Cuando tenga todo preparado para subir, tendreis noticias mias ;)
Madre mia, pues para tener poco tiempo, como te lo estas currando. Felicidades por esta pasada de curro [oki]
perdonar mi suprema ignorancia... [angelito]
q es esto??

sea lo q sea, se ve q te lo has currao muxo.... ;) [idea] [ok]

asias Salu2 [fumando]

PD: te e mandao un MP [alien]
Se podria usar, por ejemplo, para cargar un hilo en el arm7 y comunicar el hilo principal y el hilo del arm7 de una forma sencilla?(por ejemplo, en un emu para usar el arm9 para el main y la emulacion de un nucleo gordo, y en el arm7 un nucleo pequeño??)
Tiene muy buena pinta(estoy empezando ha ver cosas con varios hilos), estaria bien que se enterasen los desarolladores de DSLinux, supongo que le podrian sacar mucha chicha.
Saludos y gracias por tu esfuerzo!!
Revocool escribió:perdonar mi suprema ignorancia... [angelito]
q es esto??

sea lo q sea, se ve q te lo has currao muxo.... ;) [idea] [ok]

asias Salu2 [fumando]

PD: te e mandao un MP [alien]


Esto es la posibilidad de ejecutar varios hilos de programa en nuestra DS, lo que tu llamarias multitarea

¿ventajas? Bastantes.

Para empezar, algunas cosas que hacemos en nuestras aplicaciones, se activan desde el vector de interrupciones. Sin esta libreria, eso supone que durante el tiempo que la funcion handler esta 'rulando',. no es posible atender otra interrupcion...

Ahora sin embargo, el tiempo que se perdería en dicho handler, sería minimo (el necesario para llamar al iWakeup) y se podran tratar todas las interrupciones rapidamente, mientras los hilos se activan de forma sincronizada al terminar, e incluso se interrumpe momentaneamente algun hilo para tratar otro de mayor prioridad, sin que esto afecte al tratamiento de las iterrupciones.

Otra de las ventajas, es el poder disponer de una pila mayor.

Segun veo en el fichero ds_arm9.ld, las pilas para el modo supervisor e interrupciones, constan de 256 bytes cada una y el resto, hasta un total de 16KB, es la pila de sistema, que es la que empleamos para todo lo demas.

Pues bien, podemos crear un hilo y asignarle un espacio de pila mayor para aquellas tareas que hagan un mayor uso de variables locales o que utilicen bucles recursivos, por ejemplo

PD: Con respecto a tu MP, creo entender que te refieres a hacer un RPG Maker que se edite en el PC, pero que luego se ejecute en la DS. No conozco el RPG Maker, pero supongo que utilizará algun tipo de lenguaje intermedio que hace posible ese uso directamente, o casi directamente. Ponte en contacto con la persona que ha hecho el port a DS, que es lo mejor que puedes hacer.

Se podria usar, por ejemplo, para cargar un hilo en el arm7 y comunicar el hilo principal y el hilo del arm7 de una forma sencilla?(por ejemplo, en un emu para usar el arm9 para el main y la emulacion de un nucleo gordo, y en el arm7 un nucleo pequeño??)
Tiene muy buena pinta(estoy empezando ha ver cosas con varios hilos), estaria bien que se enterasen los desarolladores de DSLinux, supongo que le podrian sacar mucha chicha.
Saludos y gracias por tu esfuerzo!!



Para eso que dices, lo mejor es usar la interrupcion del IPC. Si te miras el código fuente de eSound, veras que utilizo ese metodo para comunicar el arm9 y el arm7 y programar un sonido,directamente.

Podrias enviar un valor mediante IPC, tratar la interrupcion en el lado correspondiente, creando un servidor RPC que llame a las func iones que necesites y luego esperar una respuesta en el otro arm para sincronizar.

Yo lo hago así con eSound, solo que en el lado de arm7, conecto el handler de la interrupcion IPC y en el lado de arm9, hago un bucle while esperando la respuesta de la 'señal' IPC para que todo esté sincronizado.

PD: En mis fuentes, uso vblank en el arm7 tambien, debido a que en el emulador que uso no funciona la interrupcion del IPC. Evidentemente, tambien se puede hacer lo mismo usando un timer,pero en ese caso, tendrias una respuesta 'demorada' mientras que usando IPC es inmediata la respuesta.

PD2: Evidentemente, el sistema de multitareas funciona tambien en ARM7. Seguramente adapte la librería eSound para ese uso.
Ok, muchas gracias por la aclaracion.
De todas formas es muy muy muy interesante lo que cuentas, espero que saques una version publica igual de bien comentada que tus esound, asi da gusto leerse la documentacion ;)
Saludos
otto_xd escribió:Ok, muchas gracias por la aclaracion.
De todas formas es muy muy muy interesante lo que cuentas, espero que saques una version publica igual de bien comentada que tus esound, asi da gusto leerse la documentacion ;)
Saludos


Por supuesto ;): tratare de documentarla de igual forma, en español (en ingles soy bastante malo :() y tambien trataré de hacer algunos ejemplo basicos para que os sea facil entender como funciona esto
yo de programacion con hilos en C tengo algo de experiencia, aunque he manejado mas hilos con Java. Si puedo ayudarte en algo aqui m tienes [ok] ... eso si de programar en la DS ni idea, pero voy cogiendole ganas a hacer algo
Muchas asias Hermes [tadoramo] ;), pero no te molestes en explicarmelo xq no tengo ni zorra de programacion... [buuuaaaa] (aunke x lo menos e entendido su funcionalidad) s como un multitareas/"ventanas" no?
[buenazo] el creador del "RPG Maker" s ingles y tb toy mu verde en eso... ratataaaa
(Al grano): Sería facil o por lo menos factible:
q alguien hiciera un "creador de juegos caseros" (un programa base)?
para la gran mayoria de usuarios q no tienen ni idea de programacion
Estaria muy pero q muy bien!! [amor]

te deseo q "el proyecto" q te estas currando tanto (q esta a años luz de mi compresion :( ) te vaya muy bien y de sus frutos! ;)

Salu2 [tadoramo] [tadoramo] [tadoramo] [tadoramo]
Wow, primero la libreria de sonido, ahora la de multitherad... creo que los de palib deverian fijarse un poco en ti, e intentar meter tus cosas en su version, y aun mas los de LibNDS.

Bueno, muchas gracias, espero (supongo que "esperamos") la libreria, documentacion y ejemplos... la de Sound venia muy completa toda documnetada y muy bien..

ANIMO
Neopiro escribió:Wow, primero la libreria de sonido, ahora la de multitherad... creo que los de palib deverian fijarse un poco en ti, e intentar meter tus cosas en su version, y aun mas los de LibNDS.

Bueno, muchas gracias, espero (supongo que "esperamos") la libreria, documentacion y ejemplos... la de Sound venia muy completa toda documnetada y muy bien..

ANIMO


Tranquilo, que todo llegará!


De momento, prefiero publicar mis fuentes de forma 'despegada', ya que es mejor así: que ellos decidan que intergan y como lo integran.

Por otro lado, ya tengo rulando el multithread en ARM7, ya que la version en la que estoy trabajando ahora, fallaba por un fallo de optimizacion del compilador muy curioso.

Digamos que ponemos un bucle asi:

while(manolo==0) ;

Pues en el ARM7, se traducia por algo asi:

reg=manolo;
while(reg==0);

Y claro, 'manolo' es actualizado desde una interrupcion, pero de la segunda manera, no sale del bucle en la vida [jaja]

Al final, he pasado la funcion a ensamblador y santas pascuas, jejeje
Hermes escribió:
Tranquilo, que todo llegará!


De momento, prefiero publicar mis fuentes de forma 'despegada', ya que es mejor así: que ellos decidan que intergan y como lo integran.

Por otro lado, ya tengo rulando el multithread en ARM7, ya que la version en la que estoy trabajando ahora, fallaba por un fallo de optimizacion del compilador muy curioso.

Digamos que ponemos un bucle asi:

while(manolo==0) ;

Pues en el ARM7, se traducia por algo asi:

reg=manolo;
while(reg==0);

Y claro, 'manolo' es actualizado desde una interrupcion, pero de la segunda manera, no sale del bucle en la vida [jaja]

Al final, he pasado la funcion a ensamblador y santas pascuas, jejeje


Juer, con eso tuve muchos problemas en una cosa que hice hasta que descubrí que se podía declarar un argumento volatile en c :P
DemonR escribió:
Juer, con eso tuve muchos problemas en una cosa que hice hasta que descubrí que se podía declarar un argumento volatile en c :P


Si. Yo hubiera caido en lo mismo, de no ser, porque al principio, el while constaba de TRES comparaciones y como una de ellas era TRUE justo antes del bucle, el while se sustituia en ensamblador por un bucle infinito: while(1); [reojillo] .

Así que decidí curarme en salud y pasarlo todo a ensamblador y se acabaron los problemas :-P

PD: Cuando un codigo es critico como el mio , es mejor tirar de ensamblador, sobre todo cuando en ese while se produce una conmutación de hilo (como por arte de magia)
Una idea para la librería: Los semáforos y otros mecanismos de concurrencia (cooperativos)

Cuando 'liberes' la librería le echaré un vistazo.

Un saludo,
ninor escribió:Una idea para la librería: Los semáforos y otros mecanismos de concurrencia (cooperativos)

Cuando 'liberes' la librería le echaré un vistazo.

Un saludo,


Estuve mirando el tema de los semaforos y lo dejé porque tenía problemas de todo tipo y en general, se puede implementar la funcionalidad usando los recursos con los que ya cuenta la libreria.

De momento, prefiero estabilidad y sencillez. Ya habrá tiempo de meterse con los semaforos y otras historias

Le he añadido algunas funciones interesantes. Como la libreria 'gasta' un timer para sus usos (aunque puede responder a otras interrupciones mas rapidas), he decidido sacarle partido de la siguiente manera:

void Sleep_Timer_Thread(unsigned timer); :-> duerme un hilo durante el tiempo especificado (en milisegundos)

int Get_Thread_Percent_Use(); :-> devuelve el porcentaje de uso de tiempo del hilo.

Es decir: ahora hay una cuenta global que cuenta el tiempo que lleva el programa en activo y tambien se cuenta el tiempo que está activo cada hilo (de ahí que pueda sacar el porcentaje) y además, por añadidura, se puede programar un timer para cada hilo que permite dormirlo durante un tiempo determinado, sin necesidad de complicarnos la cabeza .

Tambien se han añadido otras funciones que permiten conocer el estado de un hilo, el hilo actual, o cambiar la prioridad de un hilo, por ejemplo

Ahora estoy en fase de test intensivo, antes de empezar a crear algunos ejemplos, preparar la docu y publicar todo ;)
·Espero que las PALibs tomen pronto las librerias y se pueda hacer uso de ellas si las usas(es mi caso ^_^U).
¿Se las has pasado de alguna forma o has hecho sabres de su existencia?(sé que ésto es internet y -casi-todo se sabe,pero es por saberlo).

[OFFTOPIC]
Al hilo de esta nueva librería, me veo en la obligación de proponer un:
Imagen

[/OFFTOPIC]


Un saludo, Nekete.
Hermes escribió:void multithread_disabled(); :-> Importante funcion que prohibe el cambio de hilo (util por ejemplo, para acceder a una funcion que es llamada desde varios hilos pero que no soporta reentradas)

Corrígeme si me equivoco:
Este método, se ejecuta dentro de cualquier hilo, y no se cambiará de hilo hasta que se haga el 'multithread_enable', no?

Si es así, ya tienes implementado un sistema para declarar una sección crítica, y no harían falta semáforos (aunque no sea exactamente lo mismo).

Por cierto, qué es lo que gestiona la ejecución de los hilos?
Habrá prioridades?

Creo que te estoy mareando... y yo también me estoy mareando [+risas]

Un saludo,
ninor escribió:Corrígeme si me equivoco:
Este método, se ejecuta dentro de cualquier hilo, y no se cambiará de hilo hasta que se haga el 'multithread_enable', no?

Si es así, ya tienes implementado un sistema para declarar una sección crítica, y no harían falta semáforos (aunque no sea exactamente lo mismo).

Por cierto, qué es lo que gestiona la ejecución de los hilos?
Habrá prioridades?

Creo que te estoy mareando... y yo también me estoy mareando [+risas]

Un saludo,


Eso es. Cuando haces un iWakeup_Thread hacia un hilo, esto queda almacenado hasta que la funcion de tratamiento (una funcion que es llamada desde el vector de interrupciones, de forma 'invisible'), puede actuar.

Cuando haces un multithread_disabled(), esta funcion de tratamiento queda inhabilitada de forma parcial y la seccion de codigo que hace el cambio de tareas propiamente dicho, tambien. Solo cuando ejecutes un multithread_enabled u otra funcion que habilite, como por ejemplo, Sleep_Thread(), pueden ser tratados los cambios.


Existe una funcion mejor que multithread_enabled() cuando el tiempo que tarda en ejecutarse la seccion critica es digamos, largo: la funcion multithread_update() que lo que hace es habilitar y esperar a que se produzca un cambio de tareas, si procede.


La funcion que hace todo el manejo, tiene 3 niveles de programación con este orden de prioridad: cambios de tarea producidos por Sleep_Thread(), Wait_Thread(), cambios de tarea producidos por la creacion de hilos y por ultimo, los producidos por Wakeup_Thread()

Los hilos tienen un campo de prioridad, que se utiliza cuando se trata de despertar un hilo con Wakeup_Thread(). (menor valor= mayor prioridad)

Cuando se trata de un 'relevo' de tarea, se ignora este valor y se recurre a la rotacion de tareas, controlando esto con una serie de flags (para evitar circulos viciosos).

Es decir: las prioridades se aplican solo en el caso de que un hilo trate de interrumpir a otro y no cuando se produce un relevo mediante Sleep/Wait
MMMuy bueno!
Esta semana que viene me pongo ya con la programación de la DS!
(Soy programador, pero en Gwindous, y tengo la lite desde hace tan solo 10 dias: todo un descubrimiento de máquina)

Espero (tener tiempo) y poder hacer alguna cosilla buena

Ah, y muchas gracias por tus detalladas explicaciones Hermes!
Me apunto a este hilo.

Un saludo,
Ufff... lo que se pueden complicar las cosas a veces...

Vereis, el otro día, tenía la libreria rulando tres hilos sin fallo alguno (estuve probando en la DS el programa durante mas de tres horas y todo perfecto).

Bueno, pues como estoy utilizando un nuevo sistema, bien diferente a como hice la primera implementacion y viendo que la cosa va de PM, me decido a intentar meter semaforos.

Al principio, todo mas o menos normal, los tipicos fallos de programacíon que se solventan pronto, pero luego, me doy cuenta de que el programa peta, por un error desconocido. Anulo el tema d elos semaforos y sigo con el mismo problema:

¿será que no restauro bien los registros? No, tanto el registro de estado como los registros del r0 al r12, pasando por sp,lr y pc, se restauran correctamente, eso no puede ser.

¿será un problema de reentrada en la interrupcion? Pues va a ser que no, puesto que que al entrar en el modo interrupcion, las interrupciones estan deshabilitadas y ademas, el registro IME tambien está deshabilitado

¿será un problema de pila? De mis funciones en ensamblador, no es desde luego y en el modo interrupcion se utiliza otra pila diferente, que mediante test, compruebo que no varía nada

¿será un problema de caché? Pudiera ser... pero nen, raro es que flusheando las cachés, los fallos se repitan

El fallo se manifiesta de dos formas: cuelgue directo o corrupcion de la pantalla superior espectacular

Pues bueno, yo comiendome el tarro durante horas y dandome de tortas por no encontrar el fallo :p y... ¿que era?


Pues lo unico que podia ser: la parte de código que no es mía, procedente de LIBNDS, jejeje

Vereis, el problema es el siguiente: el ARM tiene un registro sp diferente para el modo interrupciones que en la DS, ese stack tiene una longitud de 256 bytes.

Puesto que hasta ahora, no había posibilidad de utilizar programación multihilo, si queriamos hacer que una aplicacion dependiese de una interrupcion (como por ejemplo, el reproductor de MOD), tenemos un problema con el tamaño de dicha pila

Entonces, al parecer, los creadres de LIBNDS tuvieron la idea de conmutar al modo sistema (las funciones se ejecutan en dicho modo, normalmente), para poder aprovechar la pila y ahí la CAGARON

¿por que? Pues porque aunque los programadores solemos pensar que las entradas en la pila, son simetricas (si hay un push, luego habrá un pop, etc), esto no tiene porque ser así siempre.

Por ejemplo, imaginemos un codigo asi:

add sp,sp,#32
ldr r0,[sp]
sub sp,sp,#32

¿que pasa si despues del add sp,sp... ocurre una interrupcion? Pues que cuando entremos en nuestra funcion de handler, nuestras variables locales macharan los datos que hemos dejado desprotegidos al variar el puntero de pila (de ahi que haya un puntero de pila diferente para cada modo en el arm) .

Aunque parezca increible, eso sucede (o algo similar) con las instrucciones en modo thumb , o si no, explicadme porque el pete desaparece, si desplazo la pila hacia abajo un determinado numero de bytes antes de entrar en el handler y luego, por supuesto, hago la correccion oportuna al salir.

En el codigo utilizado en ARM7, no hay problema, pues el main() acaba en un bucle infinito, que no debe variar mucho la pila y las cosas 'complicadas' cuelgan de las interrupciones

En ARM9, se tiene que dar la circustancia de que una interrupcion coincida con una instruccion equivalente a la que pongo arriba y eso es bastante mas dificil cuando la interrupción VBLANK es la unica que puede dar por saco y menos con un swiWaitForVBlank(); que es muy probable que atrape la interrupcion (aun así, yo creo que un problema que tenia con mi juego Asteroids and Gems, al salvar partida, que hacia que el juego se colgara de forma aleatoria al salvar, podría deberse a ese problema)

Con el tema multithread, la cosa se complica porque a la conmutacion de hilos hay que añadir un timer que realiza esos cambios a la velocidad de 1000 ciclos/segundo y claro, no es una cosa que ocurra a cada instante, si no que dependiendo de como se sincronice, puede tardar 1 minuto, 5, 20 o varias horas, hasta que aparece ese problema

En fin, que ya está todo solucionado y todo funciona perfectamente, aunque he preferido optar por usar una pila independiente de 1024 bytes, para los handlers y evitar futuros problemas.

Tambien he añadido un test para comprobar el puntero de pila de todas las funciones y llamar a una funcion en caso de rebase de pila (o sea, una funcion debug que se llamaría en el momento de conmutar una tarea)

Mañana trataré de adelantar todo el trabajo que pueda, ya que este problema me ha retrasado mucho (aunque lo bueno es que eso repercute en una libreria mas robusta)


EDITADO:

Bueno, parece que el nucleo multihread va perfectamente y mi implementacion (BETA) de semaforos, tambien rula como debiera.

No se si leereis esto o no (ya se que escribo tochos, pero si encima no intercalais ningun post, la cosa se agrava XD)

He añadido cosas nuevas... soporte RPC para poder llamar funciones remotas en cada uno de los procesadores, hasta 32 servidores para cada uno de los procesadores.

Bueno, uno menos porque el server 0, se utiliza internamente para detalles como poder activar un hilo en cualquiera de los procesadores, o asignar memoria del monticulo con arm9_malloc() (

o una funcion, print_log, que nos permite escribir un mensaje de texto desde el ARM7 en un buffer que luego podemos recuperar desde el ARM9.

En fin, que solo queda preparar la documentacion y algunos ejemplos
Si que te leemos, pero llega un punto en el que no damos mas de si xDD tienes mucho nivel, y por eso cuesta xD
Esperamos la documentaccion.
Saludos
si, te leemos, al menos yo... auqnue hay cosas que no entiendo mucho, pero bueno, espero que mirando tus ejemplos y la documentacion, pueda apañarmelas un poco para entender algo mas...

Bueno, muchas gracias por currartelo tanto, estoy impaciente por ver los resultados...
si, te leemos, al menos yo... auqnue hay cosas que no entiendo mucho, pero bueno, espero que mirando tus ejemplos y la documentacion, pueda apañarmelas un poco para entender algo mas...

Bueno, muchas gracias por currartelo tanto, estoy impaciente por ver los resultados...
jejeje, me alegro que me leais, pero lo decia porque si no recibo respuestas, solo puedo editar mis post y es posible que se pierdan cosas como la que petición que hago ahora.

Vamos a ver: tengo un server RPC rulando en el arm9, al que le doy buen uso, pero en el arm7, la cosa está muy desaprovechada y necesito ideas.

Para el que no entienda que es un server RPC, pues imaginad una funcion en el arm7, que recibe el control desde un hilo especial, cuando en el arm9 lo solicitamos y que recibe un ID que se utiliza para poder seleccionar otras funciones desde dentro del server, un buffer que contiene datos de entrada y otro buffer que contendrá datos de salida, que serán el resultado que obtendremos en la funcion invocante del arm9.

Ahora mismo, en arm7, el server RPC contiene tres funciones solo: una que permite despertar un hilo en arm7, otra que 'enmudece' la consola (ajusta el volumen a 0) y otra que nos permite poner el nivel de volumen que queramos

Para poder intercambiar datos entre ambos procesadores, se necesita un area de memoria que sea accesible a AMBOS procesadores y tengo observado que al compilar codigo para arm7, este codigo se almacena en un area de memoria exclusiva para el arm7, por lo que si necesitamos un area de ram relativamente grande o que podamos compartir con el arm9, he dotado el server RPC de dicho procesador, con funciones de asignacion/liberacion de memoria (malloc, calloc, realloc,memalign, free) que pueden ser invocadas desde el arm7, aparte de otras funciones.

Pues bien, mi peticion es: ¿Que tipo de funciones pensais que resultaria util incluir en el arm7? Tenemos funciones para controlar el volumen de sonido global o 'despertar' un hilo y lo que pido son funciones que creais necesarias y que no estén implementadas ya.

A mi no se me ocurre ninguna :Ð (que hagan uso de la libreria general, porque para cosas particulares, podeis crear un server RPC propio)
Hermes escribió:El fallo se manifiesta de dos formas: cuelgue directo o corrupcion de la pantalla superior espectacular.

No puedes aprovecharlo para hacer unos 'visuals' espectaculares para el Moonshell o el DSAmp? [qmparto]

Hermes escribió:En fin, que ya está todo solucionado y todo funciona perfectamente, aunque he preferido optar por usar una pila independiente de 1024 bytes, para los handlers y evitar futuros problemas.

Y has creado la pila en un espacio de memoria aparte?

Ya estoy deseando ver la docu.

Un saludo,
ninor escribió:
Y has creado la pila en un espacio de memoria aparte?

Ya estoy deseando ver la docu.

Un saludo,


No es que tenga una pila, si no que tengo varias: en arm9 estoy usando 4 pilas en mis ejemplos , aparte de la normal (la del main) y en arm7 3 pilas, siendo asignada una de ellas desde el monticulo de memoria de arm9 ;) (cosa que tendreis que hacer cuando querais asignar un bloque de memoria que puedan compartir ambos procesadores)

La documentacion de funciones de thread.c, ya la tengo hecha (unas 22 funciones, sin contar las de uso interno)

Ahora me queda la parte RPC y las funciones que ofrecen los server (la funcion de log la vais a agradecer), añadir notas con información que debeis conocer y algunas recomendaciones y hacer algunos ejemplos separados que os permitan comprender mejor como funciona todo esto.

Si cogeis la idea de como funciona, es bastante sencillo, pero si no, podeis encontraros con que en un hilo haces un malloc y corrompes el monticulo de memoria, pues no tuviste la precaucion de deshabilitar el cambio de tareas y justo se hizo la interrupcion en dicha funcion y la volveis a llamar en otro hilo.

Esto es una herramienta poderosa incluso si se utiliza en mono hilo y ayuda a desbloquear las interrupciones en casos como el MOD Player, pero debe ser entendida la forma en que trabaja para evitar problemas que luego cuestan mucho de descubrir donde se producen (como la corrupcion de pila que puede darse en casos puntuales con la actual LIBNDS)
Wow, yo me leo los posts enteritos y flipo. Hay algunas cosas que no las termino de entender, porque aunque casi he terminado mi ingenieria tecnica de informatica, sabeis que no es que se aprenda gran cosa en la universidad, solo una pequeña base. Pero aun asi me lo leo y me flipa que te estes currando tanto esas cosas, se nota que te gusta un monton.
Quiero ser como tu de mayor jejeje.

Nada, que sigue asi que te lo curras un monton. y sobre funciones para el arm7.. pues no se me ocurre ninguna... pero seguire pensando.

Saludos
He colgado una version preliminar de la documentacion de la libreria, por si quereis echarle un vistazo:

http://mods.elotrolado.net/~hermes/multithread_library_preliminar.pdf

Falta algun texto que considero importante, pero lo principal está ahi
Bueno, aun sin entender del todo el tema de los semaforos(de hilos he leido algo), me parece muy interesante lo del servidor rpc.
Nose, pero me parece mas sencillo hacer un uso del segundo micro por este metodo que usando interrupciones, no?(por ejemplo para emus)
Saludos y gracias.
Me encanta tu documentacion, ademas en nuestro idioma, es lo mejor, y de lo mas currado que he visto por ahi para DS...

En cuanto a funciones qu ehagan falta, no se me ocurre ninguna...

PD: una pregunta... seria posible mediante esta libraria hacer como si tuviesemos solo un procesador en lugar de dos? es decir, para el programador no muy habil como puedo ser yo, es posible hacer que la potencia de los dos procesadroes se sume (aunque se pierda algo por el camino) de forma transparente...? Supongo que no, pero solo era una duda que tenia.

Muchas gracias por la demo de la documentacion.
Neopiro escribió:PD: una pregunta... seria posible mediante esta libraria hacer como si tuviesemos solo un procesador en lugar de dos? es decir, para el programador no muy habil como puedo ser yo, es posible hacer que la potencia de los dos procesadroes se sume (aunque se pierda algo por el camino) de forma transparente...? Supongo que no, pero solo era una duda que tenia.



Pues, si... es tan posible, que lo he hecho ;). Lo unico, es que hay que tener en cuenta que es una funcion especial, una funcion de arm9 que se ejecuta en arm7.

Lo tengo implementado de dos formas:

1) int arm7_Send_Exec_Func(void (*func)(void *p),void *p);

Esta funcion, se ejecuta en el arm9 y lo que hace, es almacenar en las variables de arm7, void (*main_func)(void *p) y void * main_data, la funcion y el buffer de datos de entrada/salida que usaremos.

En mi caso, en template.c, en el main() que acaba con un while(1) swiWaitForVBlank();, lo reemplazo por:

while(1) {if(main_func) main_func(main_data);} y ya tenemos nuestra funcion rulando infinitamente

2) int arm7_Create_Remote_Thread(int priority,void (*func)(void *arg),void *arg,void *stack,int stack_size);

Esta funcion, es algo mas compleja, porque lo que hace es crear un hilo nuevo en el arm7 y lo pone a ejecutar.

El problema es que este hilo, no puede utilizar las funciones de thread.c, como por ejemplo, Sleep_Thread debido a que es una funcion de arm9 y en caso de incluirlas, incluiria las de arm9 y no las de arm7, aunque podria estudiarse añadir funciones tipo Sleep_Remote_Thread() añadiendo las funciones a una tabla en la memoria de intercambio que uso para las RPC o algo similar, pero no se si merece la pena complicarse tanto XD


La funcion debe ser C/C++ y no hacer operaciones o accesos que no esten disponibles en el arm7 o que lo esten, pero de otra forma.

Ah! y un detalle importante: para obtener los resultados desde la memoria pasada como intercambio (el arg), habria que hacer uso de la funcion DC_FlushRange() en caso de usar memoria cacheable, claro


EDITADO:

Mi codigo de ejemplo:

int valores[2]={0,1};

unsigned stack_suma[1024];

void suma(void * p)
{
int * val=(int *) p;
while(1)
val[0]+=val[1];
}


main()
{
....

arm7_Create_Remote_Thread(62,suma,valores,stack_suma,4096);

while(1)
{
DC_FlushRange(valores,8);
printfBG(2+(flip^0),0,3,255,"suma %i ",valores[0]);
}
}
DIOS!!! YO quiero ser un crack como tu. Donde estan las instrucciones para convertirse en tu tio experto?
Bueno, como no podia ser de otra forma... justo cuando ya lo tengo todo preparado para hacer la release, he tenido un problema bastante gordo:

En la release, incluyo, la librería eSound adaptada para funcionar con multitarea, junto con los ejemplos que preparé en su día, tambien adaptados.

Tambien incluyo un par de ejemplos que utilizan la programacion multihilo y uso del procesador ARM7 para 'ayudar' al ARM9 en sus tiempos muertos.

Ademas, he compilado las dos librerias como .a y he automatizado un par de cosas en los Makefiles.

Pero como digo, me ha surgido un problema, concretamente, con la captura de microfono.

La captura de microfono, se hace a una frecuencia bastante alta y aunque yo tengo la libreria preparada para ignorar los flags de interrupciones que queramos a la hora de hacer el cambio de tarea, digamos que no lo hacia del todo bien y ademas, me surge el siguiente problema:

Para cambiar de tarea, llamo a una funcion que se encarga de estudiar todas las solicitudes y establecer las prioridades. Dicha funcion, necesita tiempo y como sabemos, el ARM7 es un procesador bastante lento... asi que al solicitar entrada de microfono a 32KHZ, el sonido se ralentizaba, pues dentro de esa funcion, las interrupciones estaban desactivadas.

Asi que me ha tocado hacer un codigo que me permita reactivar las interrupciones y así poder tratar las interrupciones rapidas sin mayor problema.

Todavia me queda algo de curro por hacer (aunque ya todo funciona como tiene que ser) pero como me duele la cabeza, lo dejo para mañana que hoy estoy un poco hasta los cojones :-P


PD: Que ganas tengo ya de publicar esto...

Bueno, al final me he decidido a subir los dos ejemplos de multihilo que tengo hechos:

El primero, mthread_example1, consiste en 3 hilos (mas el de la RPC) y uso de una funcion del ARM9 desde ARM7, que es mas simple que el mecanismo de un chupete y solo hace un incremento de valor. Tambien podeis ver un ejemplo de uso de las RPC y del log (que aparece en la pantalla superior). La funcion printf que utilizo, hace uso de semaforos.

Lineas rojas: Estas lineas se dibujan desde el segundo hilo y muestran un contador y el porcentaje de uso de CPU

Lineas verdes: Lo mismo pero para el tercer hilo, el cual pasa a deshabilitarse (saliendo de la funcion) al alcanzar el contador la cifra de 1000

El resto de textos se visualizan desde el primer hilo (main)

Pulsando A y B, podeis apagar o encender el sonido mediante RPC


El segundo ejemplo, mthread_example2, muestra en la pantalla superior unas llamas que son generadas desde el ARM7, mediante la conexion de una funcion desde la seccion de codigo de ARM9, al mismo tiempo que podeis oir un mod (o sea, que se aprovechan los tiempos muertos del ARM7 para generar el fuego ;) )

El ARM9 se limita a visualizar las llamas, pasando el buffer a la pantalla y poco mas, aunque de nuevo, podemos controla el sonido mediante los botones A/B mediante RPC

En fin, tened un poco de paciencia, que todo llega :)

http://mods.elotrolado.net/~hermes/multithread.rar
Gracias por crearnos la libreria.
Bueno, la parte que he tenido que reconstruir, parece que funciona bastante bien.

He tenido un problema con los semaforos tambien, por usar la optimizacion -O3 al crear el .a (solo funcionaba bien con -O2), que he resuelto pasando la pare critica a ensamblador (esta vez, ni la declaracion como volatile me libraba del problema)

Se me ha ocurrido un experimento y parece que rula muy bien: he pasado el reproductor de formato MOD, a la libreria libesound.a (codigo ARM9) y la he conectado con el ARM7 mediante una tabla de funciones.

Vamos, que para que se entienda clarito: El codigo de ARM7 se compila en una zona de memoria exclusiva de 64KB y tenemos poco espacio para programas y lo que he hecho es pasar el reproductor de formato MOD a la seccion de código de ARM9, aunque se sigue ejecutando desde el ARM7.

Viendo el exito que ha tenido esto, he añadido una nueva funcion:
arm7_RPC_Create_Remote_Server() que sirve para crear un servidor RPC en el ARM7, conectandole una funcion desde ARM9

Vamos, que hablando en plata, podemos probar codigo o extender codigo que no nos quepa en la memoria exclusiva ded ARM7, creando un server que hará de puente entre estas secciones.

No hace falta que diga que si se producen coincidencias en el acceso a memoria, uno de los dos procesadores pierde siempre, asi que es mas rapido compilar las funciones de ARM7 para la zona exclusiva de ARM7 (en parte, la idea de quitar de ahí el reproductor de MODs viene de poder dejar espacio de memoria libre para otros usos)

Yo estoy utilizando un pequeño truco para acceder a los registros de ARM7 definidos en LIBNDS, que consiste en añadir un codigo así:

#ifdef ARM9
#undef ARM9
#define ARM7
#define CROSS_CODE
#endif

#include

#ifdef CROSS_CODE
#undef ARM7
#define ARM9
#undef CROSS_CODE
#endif

De esta forma, consigo acceder a los registros de sonido desde el MOD Player compilado en ARM9, por poner un ejemplo.

Ya solo me queda modificar la documentacion, algun detalle menor y preparar el paquete ;)
Hermes, felicidades por el proyecto, acabas de darme la ilusión para volver a programar para DS.

Mañana empiezo a instalarme todo y probar cosas.
Un abrazo.

PD: Creo que deberían abrir un foro exclusivamente de programación, más que nada por encontrar más fácilmente toda la documentación que hay.
con esto se podria hacer funcionar a la vez en la DS por ejemplo el moonshell mientras chateas con el beup?
magurin escribió:Hermes, felicidades por el proyecto, acabas de darme la ilusión para volver a programar para DS.

Mañana empiezo a instalarme todo y probar cosas.
Un abrazo.

PD: Creo que deberían abrir un foro exclusivamente de programación, más que nada por encontrar más fácilmente toda la documentación que hay.


Muchas gracias por las felicitaciones.

Con respecto al foro de programación, quiza se pueda hacer algo si se enfoca de forma general hacia todas las consolas (lo importante es no tener que pelear con 50 mil post de otros temas, para encontrar la info)

flipe22 escribió:con esto se podria hacer funcionar a la vez en la DS por ejemplo el moonshell mientras chateas con el beup?


No. Para poder hacer eso, necesitarias poder relocalizar los programas y tener una API que regulara detalles como la asignacion de memoria.

Lo que si permite es que un programa, pueda estar haciendo varias cosas y 'despejar' el uso de las interrupciones por ejemplo, el problema que comentais alguno de que en algun programa, el stylus no es nada preciso, estoy seguro a que se debe a que una interrupcion está haciendo alguna tarea que requiere mucho tiempo
y cuando trata la de la pantalla tactil, la informacion está desfasada, ademas de arreglar un bug bastante importante con el uso de la pila desde LIBNDS.


Es decir, muchos programas estan utilizando las interrupciones como si fueran hilos de programa, provocando una sobrecarga y un uso indebido de los handlers de interrupcion.

Ahora se puede despejar el camino usando un hilo que es despertado por un iWakeup dependiendo de un sistema de prioridades.


PD: Para que te hagas una idea de lo que ayuda esto a despejar las interrupciones, en uno de mis ejemplos ( mic_record) grabo sonido a 32KHZ y eso da tan poco tiempo para ejecutar codigo, que he tenido que habilitar las interrupciones desde la funcion que utilizo para gestionar la conmutacion de tareas, pues se ralentizaba la grabacion una barbaridad. (lo cual me a costado trabajo, porque la pila de interrupciones es de solo 256 bytes y realmente, está pensada para tratar las interrupciones de una en una y aqui estamos hablando de encadenar interrupciones)

EDITADO:

Libreria releseada aqui:

http://www.elotrolado.net/showthread.php?s=&threadid=617139
gracias por la explicacion, y sobre todo por el curro que t ha llevado acabar estas bibliotecas [beer]
Hola

En primer lugar, felicitarte por tu trabajo. Aunque no soy un experto en programación, reconozco que es un curro brutal y muy util.

Verás, yo vengo a pedirte un poco de ayuda con mi proyecto.

Estoy trabajando en un proyecto llamado DS_Theremin. La idea es crear una especie de instrumento musical a modo de theremin, aunque en el fondo mi idea es que con el tiempo se convierta en un sintetizador.

El proyecto está un poco avanzado. De momento suena y ahora estoy trabajando en la interfaz gráfica. Si te interesa echarle un ojo, hay un hilo donde he ido hablado de él y subiendo las roms, así como el código fuente.

http://www.elotrolado.net/showthread.php?s=&threadid=615149&highlight=Theremin/

Dicho esto, ahora viene mi petición de ayuda. Verás, para el sonido estoy usando ficheros loopeados. Esto es realmente un poco cutre, porque lo verdaderamente interesante sería poder programar la forma de onda a tiempo real. La ventaja de hacerlo así sería enorme, ya que podría añadir efectos como filtrado, etc...
y en definitiva, tocar cualquier parámetro.

En este hilo te he visto comentar algo por el estilo, y me gustaría que me dijeras si esto se puede hacer con tu librería eSound.

Para colmo, también tengo pensado poner varios osciladores a la vez, y no se si esto es posible (tiene alguna relación con lo que comentas de miltithread, o no tiene nada que ver?)

En fin, como ves, no es que sea un experto en esto (todo lo que he hecho está programado en c y con las PA_lib).

Así que na, cualquier ayuda por tu parte será más que bienvenida.

Saludos y ánimo!

^_^V
Hola

La verdad es que ando muy liado para mirar eso, pero hace tiempo, tuve una idea similar para GP2X: hacer un generador/mezcladorde formas de ondas, añadiendole efectos de reverberación, ataque, caida y vibrato.

En NDS, hay dos tipos de problemas que te puedes encontrar para hacer eso: la escasa potencia que no permitiria hacer demasiadas florituras, sobre todo si eso lo quieres usar para generar musica de un juego (que sería mi caso)


Pero sobre todo, hay un problema mayor: la ausencia de una interrupcion que te indique cuando terminó un sonido o mejor aun, en el caso de hacer un loop, cuando se alcanza la mitad y el final del buffer.


Para trabajar lo que propones, lo ideal es hacerlo a doble buffer. Esto es, se hace un loop que dure el doble de tiempo y se determina en que mitad del buffer podemos escribir (aqui lo ideal, es contar con una interrupcion, que parece, en NDS no existe)

Lo que no se yo es si alguno de los registros que utilizamos para indicar la posicion de sonido al programar, se actualizan a cada sample que se reproduce o si esa cuenta, se lleva de forma interna.

Lo mas probable, es que esos datos no se actualicen y en ese caso, la unica forma de hacer eso, seria haciendo una interrupcion que sea bastante rapida, con el fin de determinar cuando cesa el sonido (sin loop) y proceder a una conmutacion rapida , donde el mayor problema podria ser que se generase algo de ruido debido a la estatica (los tipicos clicks, vamos)

El caso es que no se que es lo que muestran los registros, una vez programados y si nos pueden sevir de ayuda, pero lo que si se, es que como dependamos de tener que estar encendiendo y apagando los canales de sonido, eso no va a ser nada bueno...

Por mi parte, ahora tengo bastante con el juego que tengo a medias y las modificaciones a la libreria Multithread a la que estoy añadiendo la libreria WIFI, a la vez que hago alguna cosilla con ella y no está la cosa para ponerse a mirar como hacer efectos especiales, jeje
Gracias por tu respuesta.

Sí, efectivamente el tema de los clicks es algo que vengo sufriendo desde el principio. Lo del doble buffer es una buena idea, aver sime pongo con ello.

Seguro que no existe una interrupción que indique el fin del sonido? Me parece recordar que con las PALib había una función que lo indicaba... no sé, lo investigaré.

De todas formas, la cosa iba con el tema de usar tu multithread con la reproducción de dos sonidos al mismo tiempo, es decir, lo que en un sinte serían dos osciladores sonando a la vez. Crees que se puede aplicar a esto?

Un saludo y gracias.

^_^V
Bueno, despues de unos dias de trabajo, ya puedo adelantar algunas cosillas de las que dispondra la nueva version de librerias que estoy preparando:

- Por un lado, he solventado un problema grave en el ARM7, debido al uso de operaciones con long long en una funcion decorativa (la que informa del porcentaje de uso de CPU). En ARM7, esa funcion carece de utilidad y el añadido extra eran 19KB, teniendo en cuenta que la memoria total es de 64KB (incluido los stacks)

-He añadido unhilo en ARM7 que se encarga de medir la temperatura y actualizar el reloj de tiempo real, que son cosillas que no tenia soportadas.

- En el MOD Player, he aumentado la capacidad de la memoria interna que se usar para trabajar con ellos, hasta 256KB que creo que es suficiente para poner un MOD de los tochos. Por otro lado, he añadido una identificacion de cabecera nueva, que a mi me resultó util para un archivo MIDI que convertí a MOD usando una utilidad llamada PTMID


- Como he estado trabajando con las librerias WIFI (y despues de liberar espacio), he subido la pila usada en los handlers hasta unos 4KB en el ARM7 y 16KB en el caso de ARM9


- En sonido, he añadido soporte ADPCM, lo que nos permite comprimir una onda de 8 Bits a la mitad, aunque el formato está pensado para comprimir de samples de 16Bits. El codec empleado es el IMA ADPCM del que Microsoft hace uso en el formato WAV. De todas formas, ya os pasare el fuente del compresor por algun lado (y que mejor manera que en forma de utilidad, jeje)


- Lo del ADPCM, no se limita solo a la reproduccion, si no que ahora podeis capturar desde el microfono en dicho formato (es un compresor que chupa pocos recursos), aparte de que he añadido captura a 16Bits (son 12 reales, pero que carajo: no iba a proporcionar una salida de 12Bits por sample!)


- Con respecto al sonido, estoy meditando si añadir soporte para generar ruido y el soporte 'tonal' que tiene la consola. Lo malo sería que de meterlo, entraría en conflicto con el reproductor de musica MOD y eso ya me gusta menos... pero ya veremos.


Sobre el WIFI, no se que hacer: la libreria está mal, ya que vale, la DS es poco potente y se atraganta cuando recibe muchos datos, pero coño, que los escupa!, que no es normal que la consola diñe por eso.

Aparte queda el tema de que necesita una amplia atencion en el ARM9 (para trabajar decentemente, he tenido que apagar el timer que controla la multitarea de mis librerias, pues causaba demasiada interferencia [uzi] .

El problema es que tal y como yo lo veo, las librerias WIFI deberian trabajar en ARM7 para juegos, porque lo que es hacer streaming con ellas, se caga las patas abajo: a 8 bit por sample en stereo, trabaja, aunque tiene el problema de que se cuelga la consola cuando le parece (incluso usando un sistema mucho mas efectivo de sincronizacion que el propuesto por el creador de las librerias).

Haciendo uso del Codec ADPCM desde el servidor (con lo que se reduce a la mitad la transmision), la cosa pinta mucho mejor, aunque, no estoy completamente a salvo de petes... y eso que he probado a meter bloqueos para que no se produjera desincronizaciones, ni bloqueos a nivel hard, pero en este caso, creo que peta la pila TCP/IP de la consola (porque no da abasto)

El problema de usar el Codec ADPCM, es que su calidad musical no es muy buena que digamos..., sobre todo, si conviertes desde MP3, como es mi caso, al usar un Plugging desde Winamp que hace la transmision de sonido)

Eso me hace pensarme si merece la pena que siga tranajando con la libreria WIFI o seria mejor esperar a que su autor corrigiera los problemas que tenga (al fin y al cabo, esta mas familiarizado que yo con ella y conocera mejor que yo los problema) o intentar portar algo hacia el ARM7 haciendo un puente que me permita invocar esas funciones desde ARM7 y asi poder emplear la libreria en juegos (que eso estaria muy, pero que muy curioso)

En fin, que es lo que hay: la NDS esta muy ajustada en potencia y eso hace dificil darle ciertos usos, pero lo que no me mola es que la libreria Wifi pete en vez de escupir los datos y seguir como si tal cosa... [burla3]
42 respuestas