cancel
Showing results for 
Search instead for 
Did you mean: 

USART help - basic IRQ handler

john
Associate II
Posted on August 23, 2008 at 07:34

USART help - basic IRQ handler

34 REPLIES 34
disirio
Associate II
Posted on May 17, 2011 at 12:38

Quote:

How did you test your code ?

Included in the RTOS there is a test suite, the tests output is printed through one of the serial ports (usually I read it using hyper terminal...).

The formatted output itself is a test that demonstrate that the circular buffers and the low level driver are working.

Running the tests I never experienced missing characters on the STM32.

Of course something like this is never tested enough 🙂 I really should include a loopback test at high speed too.

Quote:

The situation where I get dropped input chars (into the STM32) is

when it is sending AT command strings to a GSM module. That module

echo's back the characters it receives, probably a lot faster, so

there is 2-way traffic on the USART.

It is possible that is the GSM module dropping the received characters.

What line speed are you using ? are you getting framing/overrun/parity errors from the USART ?

regards,

Giovanni

---

ChibiOS/RT

http://chibios.sourceforge.net

[ This message was edited by: disirio on 30-06-2008 20:11 ]

lanchon
Associate III
Posted on May 17, 2011 at 12:38

> removed all IRQ enable/disable

that's not going to eliminate races. I don't really *want* to see your code, I'm just offering help. I'm lazy as hell when it comes to reading other people's code, but if I can understand you code there's a fair chance that I can pinpoint the problem. but please clean up the workarounds or I may get lost in the mess.

lanchon
Associate III
Posted on May 17, 2011 at 12:38

hi ST1,

thanks! this bit of info is new to me. I didn't know or had forgotten that the AHB/APB bridges could delay writes, and that these delays were observable. I recommend that you clarify this in the docs and at least on a post of its own. it could cause all sorts of subtle errors to those who are not aware of this.

anyway, I'm guessing that only one transaction may be pending on each bridge, that it must be a write, and that transactions are never reordered. is this correct? so the workaround you mention:

-follow a write with a read of same register to assure completion of the first write

could be extended to:

-follow a write with a read or write access to any register accessible via the same AHB/APB bridge to assure completion of the first write

is this correct? choosing the second access to be a write should be faster due to this write delaying behavior of the bridges.

also, what is the maximum delay a write can be subjected to? I guess bridges operating at core frequency are still ''affected'', right?

programmers should be aware that updates to peripherals might complete out of sequence when using peripherals on different bridges (specially when working with different APB frequencies) and that this out-of-sequence completion might be observable from the outside world.

st3
Associate II
Posted on May 17, 2011 at 12:38

Keil have an interrupt-driven, ring-buffered USART example here:

http://www.keil.com/download/docs/359.asp

[ This message was edited by: st7 on 30-06-2008 21:05 ]

john
Associate II
Posted on May 17, 2011 at 12:38

Thanks for all the updates.

re: AHB/APB bridges - I have not read enough about this yet to fully

understand whats going on, so give me time.

I had seen some strange (impossible) behavior before - the Rx

interrupt firing, but USART_GetITStatus() was returning

FALSE, because it was not enabled, so I switch to using

USART_GetFlagStatus().

re: Keil USART example - thanks, I'll take a look, but I'm probably on

something else today.

John.

john
Associate II
Posted on May 17, 2011 at 12:38

I took a look at the Keil example - its just the same as the ST example

code really.

AHB bridge stuff - any news on this ?

My CPU setup :-

/* Flash 0 wait state */

FLASH_SetLatency(FLASH_Latency_0);

/* Enable Prefetch Buffer */

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

/* HCLK = SYSCLK */

RCC_HCLKConfig(RCC_SYSCLK_Div1);

/* PCLK2 = HCLK */

RCC_PCLK2Config(RCC_HCLK_Div1);

/* PCLK1 = HCLK/2 */

RCC_PCLK1Config(RCC_HCLK_Div2);

So just running off the HSI clock at 8MHz.

Thanks,

John.

reply2senthil
Associate
Posted on May 17, 2011 at 12:38

I too have the same issue. UART drops bytes (approx. 1 in 100,000 bytes)

Has some one really (i mean tested) got a work around for this issue ?

What needs to be done exactly , to solve this bus issue ?

An example could be useful.

- senthilkumar sivagnanam

16-32micros
Associate III
Posted on May 17, 2011 at 12:38

Hi tibo,

You are absolutely right regarding the usage of ''synchronisation barriers'' added by ARM in the Thumb-2 instructions set like in this case and also in critical sections and data references.

Cortex-M3 implements a Write buffer on the System-Bus (RAM, AHB and APBs, spaces) and this to prevent bus wait cycles from stalling the processor during data stores, buffered stores to the DCode and System buses go through a one-entry write buffer.

If the write buffer is full, subsequent accesses to the bus stall until the write buffer has drained. The write buffer is only used if the bus waits the data phase of the buffered store, otherwise the transaction completes on the bus.

DMB and DSB instructions wait for the write buffer to drain before completing. If an interrupt comes in while DMB/DSB is waiting for the write buffer to drain, the opcode after the DMB/DSB is returned to on the completion of the interrupt. This is because interrupt processing is a memory barrier operation.

The same mechanism is also implemented in STM32 AHBtoAPB buses and entry depth depends on the AHB/APB ratio in a deterministic way.

In a summary, Barrier instructions are : ISB, DSB, and DMB these ensure

certain actions have taken place before the next instruction is executed.

However, The usage of this instructions is not a standard ANSI C code and compiler is not able to add in an clever way, So we should use these special keywords in manual where the Keyword call/name may differ from the compiler in question.

I haven't yet used these instructions on APBs to see the effects, I will do it in the coming days to confirm the workaround I has already provided will simply do the same Job by stalling the buses and waiting till the data is written effectively to the APBs.

Cheers,

STOne-32.

[ This message was edited by: STOne-32 on 17-08-2008 00:28 ]

16-32micros
Associate III
Posted on May 17, 2011 at 12:38

Hi,

Please note that in the new update of the firmware library v2.0.2, some USART flags and functions were updated :

+ stm32f10x_usart.c /.h

- USART_ClearFlag function: some flags are removed from the possible flags parameters list.

This function can clear only CTS, LBD, TC and RXNE flags.

- USART_GetITStatus function: IS_USART_IT macro is changed to IS_USART_GET_IT

- USART_ClearITPendingBit function: some pending bits are removed from the possible pending bits parameters list.

This function can clear only CTS, LBD, TC and RXNE pending bits.

Reference Manual RM0008 USART_SR ( Status Register ) was also updated in 28/July/2008.

For what concerns the Keil USART IRq example, and looking at the following code in Usart.c :

/*--------

USART1_IRQHandler

Handles USART1 global interrupt request.

*--------*/

void USART1_IRQHandler (void) {

volatile unsigned int IIR;

struct buf_st *p;

IIR = USART1->SR;

if (IIR & USART_FLAG_RXNE) { // read interrupt

USART1->SR &= ~USART_FLAG_RXNE; // clear interrupt

p = &rbuf;

if (((p->in - p->out) & ~(RBUF_SIZE-1)) == 0) {

p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);

p->in++;

}

}

if (IIR & USART_FLAG_TXE) {

USART1->SR &= ~USART_FLAG_TXE; // clear interrupt

p = &tbuf;

if (p->in != p->out) {

USART1->DR = (p->buf [p->out & (TBUF_SIZE-1)] & 0x1FF);

p->out++;

tx_restart = 0;

}

else {

tx_restart = 1;

USART1->CR1 &= ~USART_FLAG_TXE; // disable TX interrupt if nothing to send

}

}

}

I believe there is a mistake in the RXNE and TXE flags and here are cleared twice by first :

o Software sequence in code : USART1->SR &= ~USART_FLAG_RXNE;

o and then by reading USART_DR in code : p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);

Looking at RM0008 rev5 Page 631 it says :

TXE is Read Only !!

Bit 7 TXE: Transmit Data Register Empty

This bit is set by hardware when the content of the TDR register has been transferred into the shift

register. An interrupt is generated if the TXEIE bit =1 in the USART_CR1 register. It is cleared by a

write to the USART_DR register.

0: Data is not transferred to the shift register

1: Data is transferred to the shift register)

Note: This bit is used during single buffer transmission.

Bit 5 RXNE: Read Data Register Not Empty.

This bit is set by hardware when the content of the RDR shift register has been transferred to the USART_DR register. An interrupt is generated if RXNEIE=1 in the USART_CR1 register. It is cleared by a read to the USART_DR register. The RXNE flag can also be cleared by writing a zero to it. This clearing sequence is recommended only for multibuffer communication.

0: Data is not received

1: Received data is ready to be read.

So here seems a race condition that may happen because Content of DR may be lost between both sequence clearing. I think that the first clear is useful and should be removed. Could you try it and let us know if the issue persists again.

Cheers, Have a sunny weekend 🙂

STOne-32.

[ This message was edited by: STOne-32 on 15-08-2008 20:56 ]

tibo
Associate II
Posted on May 17, 2011 at 12:38

Hi all,

this mail addresses not the special UART problem, but the general access to devices, which was

mentioned by STOne/lanchon:

STOne-32 wrote's:

>

> It seems like the core (Cortex-M3) generates bufferable write transfer. This means that the CPU

> consider that the data is written from an AHB point of view while the APB write transfer is managed

> by the AHB2APB bridge and could be written later. In this case the CPU left the interrupt routine

> while the interrupt is not yet cleared the cpu will re-enter again on the interrupt handler. To avoid

> this race condition, could you try the following :

>

> 1) ISR routine has to clear the interrupt peripheral flag when just entering in the routine to avoid

> interrupt missing.

>

> 2) ISR routine has to Implement a write to the APB peripheral register ( to clear the peripheral flag)

> then followed by a read access to the same register/flag. This operation will force the write buffer to

> complete the effective write the bit in the register.

lanchon wrote's:

>

> thanks! this bit of info is new to me. I didn't know or had forgotten that the AHB/APB bridges could

> delay writes, and that these delays were observable. I recommend that you clarify this in the docs and

> at least on a post of its own. it could cause all sorts of subtle errors to those who are not aware of this.

>

> anyway, I'm guessing that only one transaction may be pending on each bridge, that it must be a

> write, and that transactions are never reordered. is this correct? so the workaround you mention:

>

> - follow a write with a read of same register to assure completion of the first write

>

> could be extended to:

> - follow a write with a read or write access to any register accessible via the same AHB/APB bridge to

> assure completion of the first write

>

> is this correct? choosing the second access to be a write should be faster due to this write delaying

> behavior of the bridges.

>

> also, what is the maximum delay a write can be subjected to? I guess bridges operating at core

> frequency are still ''affected'', right?

>

> programmers should be aware that updates to peripherals might complete out of sequence when using

> peripherals on different bridges (specially when working with different APB frequencies) and that this

> out-of-sequence completion might be observable from the outside world.

If I have understand everything correct…:

The default memory map (which is valid without the mpu) for device memory (0x40000000 - 0x5FFFFFFF)

is bufferable = on and cache = off. This means that every write access to this region is (or can be)

buffered and every read access is reading the physical memory address.

There is no statement about the buffer length, but [1] on page A3-22 says, that device memory is multi-copy

atomic, which means that:

- all writes to the same location are serialized and

- a read after a write will get the written value

A consequence of the second statement (and the fact that a read reads the physical address) is, that

after a write the buffer has to be (internally) flushed before the next read. As a second consequence a

subsequent write after the first write will not (necessarily) flush the buffer.

For the application programmers view it is only essential to know that writes to device memory are

always bufferable with the possibility of side effects. The correct solution should be to use a

synchronisation barrier in critical situations (to flush the write buffer), e.g. to use the DSB instruction

after the ''Clear peripheral flag'' instruction in the ISR routine, to ensure that the flag is physically cleared

before the return instruction. (a C function called __DSB() can be found in cortexm3_macro.s).

To STOne:

So, I suppose the read-after-write is a ''work-around'' and should work (in general), but I think the

synchronisation barrier is the correct solution (and it is faster).

To lanchon:

- I think ST will not give any statement about the number of entries in the write-buffer, because it can

vary with the implementation of the chip … (but it is not critical).

- read's are never ''buffered'' (no cache)

- write's are never reorderd

- a subsequent write after a first write will not solve the problem (a syncronisation barrier should be the

fastest solution because there is no device access (read or write) necessary).

- I think the maximum delay is not predictable, because it depends on many factors (e.g. the buffer

length, the bus priority of the writer, the bus speed …)

- programmers of peripherals on different bridges should use a synchronisation barrier after every

instruction where the order is essential, then everything should work.

More info's can be found in:

[1] ARMv7-M Architecture Application Level Reference Manual, page A3-22, A3-25

[2] The Definitive Guide to The ARM Cortex-M3, page 72, 86ff,