Isolated problem with MCF54417 1-Wire Module (documentation)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Isolated problem with MCF54417 1-Wire Module (documentation)

883 Views
ynaught
Contributor III

Implementing communications to a Dallas 1-wire temperature sensor in my OS in the MCF54417 CPU, I discovered a bug.

 

I expected this to be the easiest 1-wire implementation ever, since I'd bit-banged all previous implementations!  Tests run in the boot-loader code proved out the hardware by correctly measuring the temperature from the device.

 

However, when I tried to implement the exact same subroutine in the OS code, it failed in strange ways!

 

Section 36.4.1.3 ("Byte Transfers") suggests the following procedure:

 

1. Write 0xFF to OW_TXRX (connected to the transmit buffer).

2. Wait for the receive-buffer-full DMA request or interrupt (or poll OW_ISR[RBF] directly if the

interrupt is disabled). During this time, the hardware is writing ones on the bus while sampling the

wired-AND of the data from the device. The read data is shifted into the receive shift register.

When a byte is collected in the receive shift register, the data is transferred to the receive buffer,

and the RBF flag sets.

3. Read from OW_TXRX (connected to the receive buffer) upon receiving the RBF interrupt or DMA

request.

In my code I did not implement interrupts, since this would be used very seldom.  Instead I wrote to OW_TXRX and then polled OW_ISR[RBF] in a tight loop.  Lengthy testing eventually led me to realize:

 

When the CPU sets the OW_ISR[RBF] bit, it is NOT READY for the OW_TXRX register to be read!

 

byte OW_TxRx( byte TxByte) {     byte RetVal;     if( (MCF_OW_ISR & MCF_OW_ISR_RBF)) { // should NOT be set already!         (void) MCF_OW_TXRX; // Dummy read to clear         #ifdef DEBUG_OW             iprintf( "OW_TxRx() had to clear RBF on start\n");         #endif     }                  MCF_OW_TXRX = TxByte;          while( !(MCF_OW_ISR & MCF_OW_ISR_RBF))         ; //           asm( "nop"); // A minimum of 3 NOPs are required to make this work     asm( "nop"); // More may be needed at higher temperatures..????     asm( "nop");     asm( "nop");     asm( "nop");     asm( "nop");     asm( "nop");     asm( "nop");     asm( "nop");          RetVal = MCF_OW_TXRX; // Reading this register clears the RBF flag          return RetVal; } 

 

What I saw was that every OTHER call to this function would find the OW_ISR[RBF] bit already set, and the data I'd have read immediately before this bit was erroneously set would have been a duplicate of the time before.

 

Adding the "nop" instructions was the "fix" for the hardware bug.  It seems clear to me that the hardware is FIRST setting the RBF bit and THEN placing the data into the TXRX register.  I suspect that if I'd implemented an interrupt service function, that the latency of servicing the interrupt would have masked this bug (and may be how Freescale managed to convince themselves this was working).

Labels (1)
0 Kudos
3 Replies

672 Views
ynaught
Contributor III

I forgot to mention:

The reason it worked in the boot code and not the OS, I assume, is that the boot loader executes code directly out of external Flash memory while the OS executes code from DDR2 RAM. 

Since the "fix" was to insert delay, it seems clear that the boot loader simply runs the same code slowly enough that the added delay was not necessary.

0 Kudos

672 Views
TomE
Specialist II

> Since the "fix" was to insert delay,

Not necessarily, but you're probably right.

This is a complicated and pipelined CPU and as such it may very well "execute ahead" of where you think it is.

So it is remotely possible that it may have executed the line that reads "MCF_OW_TXRX" while the test is false.

Adding lots of NOPs may just fill the pipeline and stop it from getting to there.

The IO register definitions should have "magic volatiles" sprinkled all over them. That SHOULD be a Really Big Hint to the compiler to "do the right thing" when generating the code. For a lot of CPUs this means putting a "barrier" instruction in the execution stream. For this one it might be controlled by the "Precise" Cache and MMU modes set for the memory.

The CFPRM documents that the "NOP" instruction is anything but. If you really want a NOP then use TPF, as "The NOP instruction does not begin execution until all pending bus cycles have completed, synchronizing the pipeline and preventing instruction overlap.".

OK, so if you need three of them then your original assertion of it being a hardware problem is probably correct.

Could you replace your NOPs with TPFs and see how many of them it takes? That should give a better estimate of the number of clock cycles as NOP takes 6 clocks and TPF takes 1.

Can you write a loop that reads the whole of MCF_OW_ISR and writes it to a ring buffer? You might be able to see all of TBE, TSRE, RBF and RSRF changing at different times.

You should also write code to measure how long it takes to read a peripheral register. It may take 100 CPU clocks or even more. The bus bridges are slow and the peripherals are running from slow system clocks too.

Tom

0 Kudos

672 Views
TomE
Specialist II

I wrote:

> Can you write a loop that reads the whole of MCF_OW_ISR and writes it to a ring buffer?

You could alternately read the ISR and the TXRX Register. That should show when the data is transferred relative to the status bits changing.

I can't see why this should be temperature dependent. It should depend on the clock that the 1-Wire module runs from. One you've measured the delay, then just wait double that before reading.

It is always worth reading a new manual. The i.MX53 has a 1-Wire module, but the 1-Wire chapter in that manual looks to be identical to the one in your manual.

Can you make any sense of the following? I don't know what the author is trying to tell us, and I don't think they knew either:

36.3.6 Interrupt Register (OW_ISR)

NOTE
When a byte is written to OW_TXRX, software then waits for a transmit
shift register empty (TSRE) interrupt to occur. When TSRE sets, the receive
buffer full (RBF) flag is also set. The RBF flag does not trigger an interrupt,
assuming it is disabled. However, software should read the OW_TXRX to
clear the RBF flag to give a proper status of the pending interrupts.

Tom

0 Kudos