This is the mail archive of the
ecos-discuss@sources.redhat.com
mailing list for the eCos project.
problem with synchronization
- To: ecos <ecos-discuss at sourceware dot cygnus dot com>
- Subject: [ECOS] problem with synchronization
- From: Rafael Rodríguez Velilla <rrv at tid dot es>
- Date: Thu, 28 Jun 2001 15:20:43 +0200
- Organization: Telefónica I+D
I'm working with eCos 1.3.1 in an ARM7TDMI processor
I have written a little assembler routine to attend my serial IRQs (in
order to make a bug with the USART less harmful)
The idea is to have a thread where I use these functions:
manda_buffer_enviar(buffer_pointer, length_of_the_buffer,
time_for_timeout); // For sending
lee_buffer_recibido(buffer_pointer, length_of_the_buffer,
time_for_timeout); // For receiving
To send and receive (respectively) a stream through the serial line.
I have made the USART produce a FIQ, so I use cyg_interrupt_set_vsr to
substitute the FIQ to my custom routine (to speed up serial transactions)
I want to sleep the calling thread of these routines till the buffer is
full (when receiving) or it is empty (when sending), then the FIQ routine
should check if the buffer is full (or empty) and awake the thread.
The FIQ simply sends or receives a character while it has space in the
buffer, when the buffer is full (or empty if it is transmitting) it
disables the generation of the FIQand should awake the thread that made the
call.
I also want to set a timeout, in order to awake the thread if too much
time is spent without receiving (or sending) chars.
I have implemented this with flags (timed flags) but it only works
correctly for the sending side, the receiving side doesn't set the timer
when I use cyg_flg_timed_wait, because it returns inmediatly returning
false.
The courious thing is that it works coerrectly in the sending side, but
not in the receiving and both use the same method of sleeping and
awaking....
I have tried to use, instead of flags, the pair cyg_thread_delay (in the
calling side) - cyg_thread_release (in the FIQ side), but this doesn't
work, I have tried to track what happens with this combination and I've
discover (don't know exactly how) that after calling cyg_thread_release my
main thread is exited !!!! (why does it suicide instead of awaking?)
Please, take a look at the code and tell me if I'm doing something wrong.
I attach two files, vectors.S contains the code corresponding to the FIQ
routine, and seriel_FIQ.c contains the initialization routine, the calling
routines and the FIQ disabling routines.
Do you suggest any other synchronization method?
The FIQ calls termina_rutina_fiq_rx (termina_rutina_fiq_tx) when the
buffer is full (empty) if it is receiving (sending) data. These routines
should awake the thread that has to process the serial data.
To test I have used the following inside a lonely thread:
inicializa_puerto_serie
for (aux=1;aux<=20;aux++) {
manda_buffer_enviar(prueba,aux,100); // 1 second of timeout (ata
19200 bauds it is loooooong enough)
} // this works correctly
for(aux=1;aux<=20;aux++) {
lee_buffer_recibido(prueba,aux,300); // This doesn't wait till data
arraives at the serial port (and it should)
manda_buffer_enviar(prueba,aux,100); //This prints the buffer as it
was before calling lee_buffer_recibido.
}
I'm pretty sure that the FIQ routine is correctly programmed, because it
I don't make it awake the thread at the end of the FIQ (I let it awake by
timeout) it awakes correctly (so, the context is correctly saved). Besides,
the TRANSMITING side WORKS!!!
--
Rafael Rodríguez Velilla rrv@tid.es
Telefónica I+D http://www.tid.es
Telf: +34 - 91 337 4270
#include <pkgconf/hal_arm_mpp_tema.h>
#include <cyg/kernel/kapi.h> // Para sustituir la rutina de atención a la FIQ
#include <cyg/hal/mpp_tema_regs.h>
#include <cyg/hal/hal_io.h> //Para lo del HAL_WRITE... y amigos
#include <cyg/hal/serial_misc.h>
#include <cyg/hal/hal_arch.h>
volatile cyg_uint8 *mpp_serial_buffer_recibido_punt_esc;
cyg_uint8 *mpp_serial_buffer_recibido_fin;
static cyg_handle_t hilo_ligado_rx;
static cyg_uint8 nada;
volatile cyg_uint8 *mpp_serial_buffer_enviar_punt_lec;
cyg_uint8 *mpp_serial_buffer_enviar_fin;
static cyg_handle_t hilo_ligado_tx;
static cyg_uint8 aviso[22]="No debo emitir nada\n";
volatile cyg_uint8 mpp_serial_bandera_overrun;
void cierra_puerto_serie_fiq(void)
{
HAL_WRITE_UINT32 (MPP_US_IDR2,0xFFFFFFFF); //Disable all serial IRQs
HAL_WRITE_UINT32 (MPP_FIQEnableClear,0x1); //Disable serial generating FIQ
}
extern cyg_VSR_t _asm_rutina_serie; //implementado en fiq.S
static cyg_flag_t tx_rx_completa;
static cyg_flag_t rx_completa;
#define TX_COMPLETA 0x1
#define LIMPIA_TX_COMPLETA 0xFFFFFFFE
#define RX_COMPLETA 0x1
#define LIMPIA_RX_COMPLETA 0xFFFFFFFE
void inicializa_puerto_serie_fiq(void)
{
cyg_uint32 irfpri;
cyg_flag_init(&tx_rx_completa);
cyg_flag_init(&rx_completa);
hal_mpp_serial_init();
HAL_WRITE_UINT32 (MPP_FIQEnableClear,0x1); //Disable FIQ
HAL_READ_UINT32 (MPP_IRFPRI,irfpri);
irfpri=irfpri | 0x2000; //Enable FIQ when USART2 raises an IRQ (IRQline 13)
HAL_WRITE_UINT32 (MPP_IRFPRI,irfpri);
HAL_WRITE_UINT32 (MPP_US_IDR2,0xFFFFFFFF); //Disable all USAR IRQs.
HAL_WRITE_UINT32 (MPP_US_CR2,MPP_RXDIS_ESC|MPP_TXDIS_ESC); //Disable both
// serial RX and TX
cyg_interrupt_set_vsr( CYGNUM_HAL_VECTOR_FIQ, &_asm_rutina_serie);
HAL_WRITE_UINT32 (MPP_FIQEnableSet,0x1); //Enable FIQ
}
cyg_uint8 USART8(cyg_uint8 convierteme)
//Routine to mend a bug with the USARTS
{
cyg_uint8 resultado=0,aux=1,contador;
for (aux=1,contador=1;contador<=8;aux=aux<<1,contador++)
{
if((convierteme&aux)!=0)
resultado=resultado|(1<<(8-contador));
}
return(resultado);
}
cyg_uint8 lee_buffer_recibido(cyg_uint8 *buf,cyg_uint8 nlect,cyg_uint32 timeout)
{
cyg_uint8 reason,aux;
mpp_serial_buffer_recibido_punt_esc=buf;
mpp_serial_buffer_recibido_fin=(buf+nlect);
mpp_serial_bandera_overrun=0;
hilo_ligado_rx=cyg_thread_self();
cyg_flag_maskbits(&rx_completa,LIMPIA_RX_COMPLETA);
HAL_WRITE_UINT32 (MPP_US_CR2, MPP_RSTSTA_ESC|MPP_RSTRX_ESC);
HAL_WRITE_UINT32 (MPP_US_IER2, MPP_RXRDY_ESC); //Enable FIQ for RX.
HAL_WRITE_UINT32 (MPP_US_CR2,MPP_RXEN_ESC);
HAL_REORDER_BARRIER();
cyg_flag_timed_wait(&rx_completa,RX_COMPLETA,CYG_FLAG_WAITMODE_OR,(cyg_tick_count_t) timeout);
HAL_REORDER_BARRIER();
cyg_flag_maskbits(&rx_completa,LIMPIA_RX_COMPLETA);
HAL_REORDER_BARRIER();
//cyg_thread_delay(timeout); // Se hecha a dormir con un timeout
return nlect;
}
cyg_uint8 manda_buffer_enviar(cyg_uint8 *buf,cyg_uint8 nlect,cyg_uint32 timeout)
{
cyg_uint8 reason,aux;
mpp_serial_buffer_enviar_punt_lec=buf;
mpp_serial_buffer_enviar_fin=(buf+nlect);
hilo_ligado_tx=cyg_thread_self();
for (aux=0;aux<nlect;aux++)
*(buf+aux)=USART8(*(buf+aux)); // Prepare the buffer to counteract
//USARTS' bug
cyg_flag_maskbits(&tx_rx_completa,LIMPIA_TX_COMPLETA);
HAL_WRITE_UINT32 (MPP_US_CR2, MPP_RSTTX_ESC);
HAL_WRITE_UINT32 (MPP_US_IER2, MPP_TXRDY_ESC); //Enable FIQ for TX.
HAL_WRITE_UINT32 (MPP_US_CR2,MPP_TXEN_ESC); // Enable TX
cyg_flag_timed_wait(&tx_rx_completa,TX_COMPLETA,CYG_FLAG_WAITMODE_OR,timeout);
for (aux=0;aux<nlect;aux++)
*(buf+aux)=USART8(*(buf+aux)); // Correct buffer to counteract
//USARTS bug
return nlect;
}
void termina_rutina_fiq_rx(void)
// esto se ejecuta con las interrupciones inhibidas.
// This is executed with both FIQ and IRQ disabled (in FIQ mode)
{
HAL_WRITE_UINT32 (MPP_US_IDR2,MPP_RXRDY_ESC); //Disable FIQ for RX.
HAL_WRITE_UINT32 (MPP_US_CR2,MPP_RXDIS_ESC);
cyg_flag_setbits(&rx_completa,RX_COMPLETA);
//cyg_thread_release(hilo_ligado_rx);
}
void termina_rutina_fiq_tx(void)
// esto se ejecuta con las interrupciones inhibidas.
// This is executed with both FIQ and IRQ disabled (in FIQ mode)
{
HAL_WRITE_UINT32 (MPP_US_IDR2,MPP_TXRDY_ESC); //Disable FIQ for RX.
HAL_WRITE_UINT32 (MPP_US_CR2,MPP_TXDIS_ESC);
//cyg_thread_release(hilo_ligado_tx); //Lo duerme con sleep_reason=DELAY
cyg_flag_setbits(&tx_rx_completa,TX_COMPLETA);
}
.extern mpp_serial_buffer_recibido_punt_esc
.extern mpp_serial_buffer_recibido_fin
.extern termina_rutina_fiq_rx
.extern mpp_serial_buffer_enviar_punt_lec
.extern mpp_serial_buffer_enviar_fin
.extern termina_rutina_fiq_tx
.extern mpp_serial_bandera_overrun
.global _asm_rutina_serie
_asm_rutina_serie:
sub lr , lr , #4
mov r8 , #MPP_BaseUSART2
add r9 , r8 , #(MPP_US_CSR2-MPP_BaseUSART2)
ldr r10 , [r9]
ands r11 , r10 , #MPP_RXRDY_LEC
beq transmision // Jump if the RX IRQ isn't active
recepcion:
ldr r12 , .mpp_serial_buffer_recibido_punt_esc
ldr r10 , [r12] // R10=place where I have to store what I receive
add r11 , r8 , #(MPP_US_RHR2-MPP_BaseUSART2)
ldr r11 , [r11] // Read data from serial port
mov r11 , r11, LSR #24 // Half-compensate USART's BUG
strb r11 , [r10] // Store the resulting character
add r10 , r10 , #1
str r10 , [r12] // Increase the writting pointer
ldr r11 , .mpp_serial_buffer_recibido_fin
ldr r11 , [r11] // Read the end of buffer pointer
ldr r12 , .mpp_serial_bandera_overrun
ldrb r12 , [r12]
add r10 , r10 , r12 // Add the overrun count to the number of
//received chars
cmp r10 , r11 // Have we received all the expected chars?
bhs finrecepcion // Yes=>then jump
finrx:
ldr r10 , [r9] // Read again the USART's alarm
overrun:
ands r10 , r10 , #MPP_OVRE_LEC
beq finoverrun //Jump if the overrun flag is not set
ldr r10 , .mpp_serial_bandera_overrun
ldrb r11 , [r10]
add r11 , r11 , #1
strb r11 , [r10]
mov r12 , #MPP_RSTSTA_ESC
str r12 , [r8] // Lower the overrun flag
finoverrun:
movs pc , lr // END OF FIQ
transmision:
ands r11 , r10 , #MPP_TXRDY_LEC
beq fintx
ldr r10 , .mpp_serial_buffer_enviar_punt_lec
ldr r11 , [r10] // Load address of the byte to be sent
ldrb r12 , [r11] // Load the byte to send
mov r12 , r12 , LSL #11 //Shift to counteract part of USART's bug
add r10 , r8 , #(MPP_US_THR2-MPP_BaseUSART2)
str r12 , [r10] // Send the character through the serial line
add r11 , r11, #1 // Increase the pointer of the read char
ldr r10 , .mpp_serial_buffer_enviar_punt_lec
str r11 , [r10] // Store it for the next FIQ
ldr r12 , .mpp_serial_buffer_enviar_fin
ldr r12 , [r12]
cmp r12 , r11 // Have I sent all the chars of the buffer?
beq fintransmision // YES=> jump
fintx:
movs pc , lr // END OF FIQ
PTR(vuelve_de_termina_rutina_fiq_rx)
PTR(mpp_serial_buffer_recibido_punt_esc)
PTR(mpp_serial_buffer_recibido_fin)
PTR(termina_rutina_fiq_rx)
PTR(vuelve_de_termina_rutina_fiq_tx)
PTR(mpp_serial_buffer_enviar_punt_lec)
PTR(mpp_serial_buffer_enviar_fin)
PTR(termina_rutina_fiq_tx)
PTR(mpp_serial_bandera_overrun)
PTR(salvaguarda_de_R0)
PTR(__rutina_serie_rx_stack)
PTR(__rutina_serie_tx_stack)
finrecepcion:
ldr sp,.__rutina_serie_rx_stack
mrs r8 , spsr
stmfd sp!,{r0-r3,r8,lr} //save part of the context
ldr lr , .vuelve_de_termina_rutina_fiq_rx
ldr pc , .termina_rutina_fiq_rx // Maybe a thread switch will happen
vuelve_de_termina_rutina_fiq_rx:
ldmfd sp!,{r0-r3,r8,lr} //Restore the saved registers
msr spsr , r8
movs pc,lr // END OF FIQ
fintransmision:
ldr sp,.__rutina_serie_tx_stack
mrs r8 , spsr
stmfd sp!,{r0-r3,r8,lr} //save part of the context
ldr lr , .vuelve_de_termina_rutina_fiq_tx
ldr pc , .termina_rutina_fiq_tx //A thread-switch may happen
vuelve_de_termina_rutina_fiq_tx:
ldmfd sp!,{r0-r3,r8,lr} //Restore the saved registers
msr spsr , r8
movs pc,lr // END OF FIQ
// RAFA, RUTINA SERIE
...
...
// RAFA, RUTINA SERIE
.section bss
.balign 32
salvaguarda_de_R0:
.long 0
.long 0
.rept 1000
.long 0
.endr
__rutina_serie_rx_stack:
.rept 1000
.long 0
.endr
__rutina_serie_tx_stack:
// RAFA, RUTINA SERIE