8
\$\begingroup\$

I'm using an ATmega8515 microcontroller for a one-off hobby project. This chip has an external memory interface which can directly address 64 kilobytes of external SRAM.

ATmega8515 datasheet.

For my project I need more than 64 KB, so I'm supplementing this address space by manually bank-switching between four banks of 64 KB each. This bank-switching is achieved by using two normal GPIO pins as additional address lines. This gives me a grand total of 256 KB of external SRAM.

So far so good. I've wired up the first 128 KB onto the main board and I'm planning to have the second 128 KB on a plug-in expansion PCB, that I can add later on.

So, to be clear, I have Bank 0 and Bank 1 populated on the board and Bank 2 and Bank 3 do not exist yet. Circuitry is as described in the above datasheet.

I'd like my project to be able to automatically detect how much SRAM is available, so I've written a function that exercises the SRAM by writing a pattern of data to each bank in turn and then reads it back again and displays it over a UART which I can read using Minicom under Linux.

Bank 0 and Bank 1 both work as expected, and I'm reading out the same data that I stored there.

But I am seeing some weird results when attempting to access the nonexistent Bank 2 and Bank 3. Since those chips are not present in the circuit yet, I was expecting to read back random garbage from those addresses since there would not be any SRAM chip to drive the data bus.

Perfect results from Banks 0 and 1...

Bank0[0x260]: 0x00
Bank0[0x261]: 0x01
Bank0[0x262]: 0x02
Bank0[0x263]: 0x03
Bank1[0x260]: 0x40
Bank1[0x261]: 0x41
Bank1[0x262]: 0x42
Bank1[0x263]: 0x43

Peculiar, yet somehow highly stable and consistent results from absent Banks 2 and 3...

Bank2[0x260]: 0x60
Bank2[0x261]: 0x61
Bank2[0x262]: 0x62
Bank2[0x263]: 0x63
Bank3[0x260]: 0x60
Bank3[0x261]: 0x61
Bank3[0x262]: 0x62
Bank3[0x263]: 0x63

The data read back from the non-existent addresses is not glitchy or noisy in any way. It's rock-solid and repeatable.

So I notice that the 8-bit values being read from the phantom RAM are identical to the low byte of the address (A7..A0) which is being latched by a 74HC573 chip.

What is happening here?

I understand that the low byte of the address is shared with the data lines and are controlled by the 74HC573. So if there is no SRAM chip present to drive the data bus then the data lines will be floating, effectively.

How come the data lines appear to "remember" their voltage state from immediately before they were tri-stated by the 74HC573? The ATmega8515 will be reading the data a few nanoseconds after the data bus is tri-stated - could this state be retained through some stray capacitance in the wiring?

All this said, I don't think there is actually anything malfunctioning here—I am attempting to access chips that are not there—which is obviously "undefined behaviour".

It just strikes me as interesting how the results are so very consistent.

Further information:

Here is my XMEM feature initialisation:

  //Set wait states for UPPER sector to the maximum (slowest possible)
  MCUCR  |= (1 << SRW10);
  EMCUCR |= (1 << SRW11);

  //Set wait states for LOWER sector to the maximum (slowest possible)
  EMCUCR |= (1 << SRW01) & (1 << SRW00);

  //Enable the XMEM interface (p. 29, Mega8515 datasheet)
  MCUCR  |= (1 << SRE);

With many thanks to @TomCarpenter, the problem is solved...

Writing each byte in my 256KB XMEM address space to be the bit-wise NOT of the low byte of its own address causes any absent SRAM to reveal itself.

In other words, writing ~0x73 into address 0x0573 forces the data to be different to the low-byte of the address when the SRAM is present in that location.

When there is no SRAM present, the data read back from the SRAM is the same as the low-byte of the address, likely because of stray capacitance in the wiring.

I can now reliably detect present or absent SRAM chips!

Here's my new output from the terminal...

Bank0[0x260]: 0x9f //SRAM present
Bank0[0x261]: 0x9e //SRAM present
Bank0[0x262]: 0x9d //SRAM present
Bank0[0x263]: 0x9c //SRAM present
Bank1[0x260]: 0x9f //SRAM present
Bank1[0x261]: 0x9e //SRAM present
Bank1[0x262]: 0x9d //SRAM present
Bank1[0x263]: 0x9c //SRAM present
Bank2[0x260]: 0x60 //               SRAM MISSING
Bank2[0x261]: 0x61 //               SRAM MISSING
Bank2[0x262]: 0x62 //               SRAM MISSING
Bank2[0x263]: 0x63 //               SRAM MISSING
Bank3[0x260]: 0x60 //               SRAM MISSING
Bank3[0x261]: 0x61 //               SRAM MISSING
Bank3[0x262]: 0x62 //               SRAM MISSING
Bank3[0x263]: 0x63 //               SRAM MISSING
\$\endgroup\$
3
  • \$\begingroup\$ Have you got bit 6 (XMBK) of the SFIOR register set to 1? \$\endgroup\$ Oct 1, 2023 at 14:23
  • \$\begingroup\$ @TomCarpenter, no I haven't. I don't really understand what the Bus Keeper is for (the datasheet is not very forthcoming on the topic), so I left that bit at it's default setting of 0. I've added my initialisation code to the Q. \$\endgroup\$
    – Wossname
    Oct 1, 2023 at 14:34
  • \$\begingroup\$ As an aside, a quasi-reliable method of 'is there anything there' detection is to do two reads - one with the data pins set to pull up, and one with the data pins set to pull down, both with relaxed timings. I don't know if this microcontroller can do this. \$\endgroup\$
    – TLW
    Oct 2, 2023 at 15:07

2 Answers 2

11
\$\begingroup\$

There are two possibilities I can think of as to why the lines will be repeatably holding their value when accessing the non-existent RAM.

  1. You have the bus keeper functionality enabled on the data lines.
  2. The data lines when the RAM modules are High-Z have a long enough time constant that you read the same value back.

For (1), the data lines for the XMEM interface have optional bus keepers. The bus keeper is a circuit built into the ATTiny which essentially drives the data line through a resistor which pulls to the current value of the line.

  • If the data line was previously a 0, the resistor will pull to GND and hold the line low.
  • If the line was previously a 1, the resistor will pull to VCC and hold the line high.

Because the data bus is shared with the address bus, with the bus keeper enabled, the High-Z data lines will read back whatever the lower 8 bit of the address line was set. This matches the behaviour you are seeing.

The bus keepers are controlled using the XMBK bit (6) in the SFIOR register. If this bit is set the keepers are enabled. If clear the keepers are disabled.

If the bus keepers are disabled currently, you could actually use them to your advantage in RAM discovery. The process would be:

  1. Enable the bus keepers (SFIOR |= (1 << XMBK);)
  2. Loop through each address
    1. Write the value of ~address to the memory (sets data to bitwise not of address)
    2. Read back the value.
      • If you read back ~address then the RAM is present because you are reading the value you wrote to the RAM.
      • If you read back address then there is no RAM present because you are reading the value held in place by the bus-keeper for when the lower address byte was set by the XMEM interface.

For (2), this would depend on the capacitance and leakage current of the various pins connected to the data lines. When the data lines are High-Z, the capacitance of the various inputs and buffers will hold the lines at their previous value for a short period of time before the leakage current discharges them.

If the time constant (\$\tau \approx C_I \times \frac{V_{CC}}{I_L}\$) is long enough, the lines will retain their previous value for long enough that you will be able to consistently read back this value on the bus, again matching your behaviour.

For the 74HC573 latch the input capacitance is about \$3.5[\mathrm{pF}]\$ and leakage current \$0.1[\mathrm{\mu A}]\$, which gives a time constant of about \$175[\mathrm{\mu s}]\$, plenty long enough for the read phase of the memory transaction to read back the address line. Of course it will also depend on the capacitance and leakage of the RAM modules and ATTiny pins too, but this gives you an idea of orders of magnitude.

To combat this, you could add additional pull-up or pull-down resistors onto the data lines such that they go to a fixed known value very quickly when tri-stated.

According to the datasheet, when the XMEM interface is enabled, the internal pull-up resistors are still controlled by the PORTA register, so you should be able to enable all of these pull-ups by setting PORTA = 0xFF.

Override Signals for Pull-Ups

During read, if there is RAM present, you should read back what you wrote to the address. If there is no RAM present, you should read back 0xFF because of the pull-ups.

\$\endgroup\$
2
  • \$\begingroup\$ What a great explanation, thank you. I will try using the PORTA pull-ups as you suggest, in order to get a clear indication of SRAM availability at a given address. This is fascinating stuff. I might post another question later about what Bus Keepers are for. \$\endgroup\$
    – Wossname
    Oct 1, 2023 at 15:02
  • \$\begingroup\$ I have tried the internal PORTA pull-up resistors, but they have not changed the results. The datasheet (page 197 Electrical Characteristics) gives a value between 20k Ohms and 50k Ohms, so I suspect that they are too weak to affect the outcome. Not to worry though, I think I just need to do as you suggested and write ~address to each location and work it out that way. \$\endgroup\$
    – Wossname
    Oct 1, 2023 at 15:35
5
\$\begingroup\$

It's not a least bit odd. As the data IO bus is muxed with LSB address output, stray capacitances on bus wiring and ICs keep the state stable enough that the MCU reads back the bus state stably - and the bus has the address.

If you don't want that, you could add pull-up or pull-down resistors (or turn on AVR internal pull-ups if possible in the XRAM scenario).

So at different temperature the leakage currents could change the state faster, so it could really be random under different circumstances.

It really pays off to write a specific pattern and expect to read back the same pattern before assuming there is valid memory there.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.