When actually LDS r16, PORT_IN reads the pins ?

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets say I have an XMEGA and I read PORTC_IN.
LDS instruction is two cycles. For XMEGA it specifies three cycles if reads from internal RAM. Is it the extended I/O space in RAM ?
Going further, in the program memory, LDS has an 16bit opcode followed by 16bit operand address.
It is correct to assume that the actual port read does NOT happen during the first clock cycle but during the second one or the third ?
If someone could elaborate about reading the port pins in fractions of a clock that would be interesting.
George.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a good question. I have just used a delay without thinking further, but now I checked what is happening.

I have an SPI device, which tells it's state by pulling or not pulling MISO low after the CSB goes low. There shouldn't be much delay between these two according to that chips datasheet.

When I try to do that in one clock cycle

PORTD.OUTCLR = PIN4_bm
if(PORTC.IN & PIN6_bm)
   res=0;

I always get that res=0. Thus I added a delay by setting the OUTCLR twice and then it worked OK. This was a xmega32a4 running at 2 MHz. Now I have the same with xmega64d4. I had the same issue at 2 MHz. Now I run at 4 Mhz so I thought maybe I need more delay (because of some ringing etc?), but I didn't.

So I took a look at the scope. There is only 24 ns delay from PIN4 going low to PIN6 going low and virtually no ringing, just a few ns long ~0.5V undershoot.

So why was I always getting PIN4 is high? Is there actually some delay in OUTCLR or is IN giving an "old" state.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

See Figure 13-8 (Synchronization when reading a pin value) on XMEGA AU manual.

Ozhan KD
Knowledge is POWER

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No RSTDISBL, no fun!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you post the assembly code that the compiler generated? Maybe something odd is happening.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, and if you set up virtual ports the compiler can use IN and OUT to access them in two clock cycles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Setting a pin in a port is done with std and reading with lds. Both take two clock cycles. So when does actually setting the PIN voltage happen?

Is it the after the second std cycles or within it?

According to 13-8 (or 11-8 in D manual) there is a 1-2 cycle propagation delay. If I understand it correctly the propagation delay needs to end at the middle of lds thus 1 cycle from the end of std.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mojo-chan wrote:
Can you post the assembly code that the compiler generated? Maybe something odd is happening.

This is from the earlier xmega32a4 case:


0000436a :
#include "adc.h"

uint8_t adc_ready() // Test if EOC is low = conversion completed
{
  uint8_t res=1;
  ADC_CS_LOW();
    436a:	e0 e6       	ldi	r30, 0x60	; 96
    436c:	f6 e0       	ldi	r31, 0x06	; 6
    436e:	80 e1       	ldi	r24, 0x10	; 16
    4370:	86 83       	std	Z+6, r24	; 0x06
  ADC_CS_LOW(); // This is for delay, which was needed
    4372:	86 83       	std	Z+6, r24	; 0x06
  if(PORTD.IN & PIN6_bm)
    4374:	90 91 68 06 	lds	r25, 0x0668
    res=0;
  ADC_CS_HIGH();
    4378:	85 83       	std	Z+5, r24	; 0x05
#include 
#include "adc.h"

uint8_t adc_ready() // Test if EOC is low = conversion completed
{
  uint8_t res=1;
    437a:	81 e0       	ldi	r24, 0x01	; 1
    437c:	96 fd       	sbrc	r25, 6
    437e:	80 e0       	ldi	r24, 0x00	; 0
  ADC_CS_LOW(); // This is for delay, which was needed
  if(PORTD.IN & PIN6_bm)
    res=0;
  ADC_CS_HIGH();
  return res;
}
    4380:	08 95       	ret

00004382 :
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm guessing there must be more C code than the snippet you posted because the compiler is setting the CS line high before doing the test to decide if res should be cleared (address 4378).

Can you post assembly code for the version that doesn't work? This is a very interesting topic and I'd like to get to the bottom of it. We just need to see exactly what is happening when it fails.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a lot of more C code (full 36 kB), but not in this function. Here is the non-working version.


0000436a :
#include "adc.h"

uint8_t adc_ready() // Test if EOC is low = conversion completed
{
  uint8_t res=1;
  ADC_CS_LOW();
    436a:	e0 e6       	ldi	r30, 0x60	; 96
    436c:	f6 e0       	ldi	r31, 0x06	; 6
    436e:	80 e1       	ldi	r24, 0x10	; 16
    4370:	86 83       	std	Z+6, r24	; 0x06
  //  ADC_CS_LOW(); // This is for delay, which was needed
  if(PORTD.IN & PIN6_bm)
    4372:	90 91 68 06 	lds	r25, 0x0668
    res=0;
  ADC_CS_HIGH();
    4376:	85 83       	std	Z+5, r24	; 0x05
#include 
#include "adc.h"

uint8_t adc_ready() // Test if EOC is low = conversion completed
{
  uint8_t res=1;
    4378:	81 e0       	ldi	r24, 0x01	; 1
    437a:	96 fd       	sbrc	r25, 6
    437c:	80 e0       	ldi	r24, 0x00	; 0
  //  ADC_CS_LOW(); // This is for delay, which was needed
  if(PORTD.IN & PIN6_bm)
    res=0;
  ADC_CS_HIGH();
  return res;
}
    437e:	08 95       	ret

00004380 :

And just the C-code to be clear:

uint8_t adc_ready() // Test if EOC is low = conversion completed
{
  uint8_t res=1;
  ADC_CS_LOW();
  //  ADC_CS_LOW(); // This is for delay, which was needed
  if(PORTD.IN & PIN6_bm)
    res=0;
  ADC_CS_HIGH();
  return res;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That was from the old code, which I can't test now, but I clearly remember how it went. I just tested with the newer code (and hardware), which is otherwise identical, but ADC_CS_LOW/HIGH clears/sets two pins (one for a level converter direction), but the actual step of pulling CS LOW is the last one down and first one up. Thus it works just the same.

I tried without double ADC_CS_LOW and it failed. Looked at the scope (at xmega pins) CS pin went low with ~10 ns fall time. Measuring from the very start of the falling of CS to the MISO reaching 0 V the delay was 29 ns. There was no measurable fall time in MISO (level converter is cabable of 400 MHz).

CPU was running at 4 MHz and CS stayed low for excactly 1 us, thus 4 clock cycles.

Putting asm("nop") instead of repeated CS_LOW made the code work again. Thus I have a lot of allowance in my double CS_LOW which adds 4 cycles in current code. With nop the CS stayed low for 1,25 us, thus 5 clock cycles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks. The only difference appears to be that one instruction that duplicates the write to OUTSET. I would have suspected that the CS line pulse was too short for the device to react to, but you say it pulls the line low within a few ns. Even with propagation delays by the time the read happens it should be fine.

Here is the timing diagram for reads:

This implies that an immediate read should always work, because the read can't happen at the start of the lds instruction. The earliest it can happen is half way through, because on the first cycle only the lds instruction is known and not the address from which it is to read. You can see in your disassembly that the address is only read on the second cycle because it comes after the op-code.

There does not seem to be a timing diagram for writing to the OUTCLR register. What you could try is to read in OUT and make a copy with bit 4 cleared. Then write the copy to the OUT register and see if the delay is no longer required. It may be that the OUTCLR register does a read-modify-write internally which is delayed by one cycle, in which case it would happen at the same time as the lds instruction reads the IN register.

Interestingly this implies that using virtual ports could be problematic, because an IN instruction is only one cycle.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mojo-chan wrote:

This implies that an immediate read should always work, because the read can't happen at the start of the lds instruction. The earliest it can happen is half way through, because on the first cycle only the lds instruction is known and not the address from which it is to read.

How do you make that conclusion? It says that propagation delay is 1-2 cycles.

If we assume, that OUTCLR changes the voltage of the CS-pin at the end of that instruction (=start of lds), the flip-flop has no chance of noticing it and IN will change at the end of lds, not in the middle.

Adding 29 ns to the propagation delay makes it much less likely to work and with these assumption even a single nop may not be enough, since propagation delay is over 2 cycles (almost 3 cycles at 32 MHz).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are correct, the timing diagram got me. I found this: http://asf.atmel.com/docs/3.7.2/...

Lines 173 and 174. Two NOPs for propagation delay.