Accessing the Amiga CD32 NVRAM directly
1 Attachment(s)
I need to access the Amiga CD32 NVRAM directly (nonvolatile.library is not an option), so I searched around for information and I've found basically none. So I looked at a few photos of the Amiga CD32 motherboard somebody kindly posted on the net and found out that the EEPROM is an ATMEL AT24C08N-10SC. I downloaded the official datasheet (attached here for your convenience) and started typing my code...
Getting it to work on UAE was fairly easy. But then it turned out that things work differently on a real machine - and, as a consequence, my code doesn't work. Since I don't have an Amiga CD32, I pestered a friend who patiently ran tens of tests for me. Unfortunately I caught him in the middle of an insanely busy period, so things proceeded very slowly. In the end, after more than one month, I gave up and decided to ask for help here. The (one?) problem seems to be that reading bits doesn't work. In the best case, I managed to read the ACK (0) returned when a START condition is generated (when even that doesn't work, the code hangs in an endless loop), but then getting 1 with all the following reads. Additional details: * the tests have been performed after taking the machine over entirely; * the tests have been performed after letting AmigasOS/nonvolatile.library initialize the EEPROM; * performing also an EEPROM reset upon initialization did not make any difference; * I've tried all the bit-reading code variants I could think of to no avail; * alternative code that has the SDA direction always set to input (except where output is strictly needed) gave even worse results. Attached are also my source code files: * NVRAM-test.s: code with all the bit-reading code variants I tried (see function ReadBit()), plus various writes to $dff180 to follow the code execution; * NVRAM-clean.s: same code, but with only the variant that theoretically makes more sense (and works in WinUAE). The code is fully commented and provides futher details. One important thing to mention is that the datasheet at some point says something impossible. Main points: * there are two wires, SCL (the clock, driven by the CPU) and SDA (the data); * any operation must start with a START condition, which happens when SDA is pulled low (0) while SCL is high (1); * for random reads, after telling the EEPROM where to start reading from and getting the ACK from it, it is necessary to generate a REPEATED START condition; * the ACK consists in a 0 received over the SDA wire, and given that SDA is active high, it means that, after receiving the ACK, SDA is low; * the datasheet shows the REPEATED START happening in the next 1 cycle, which is impossible given that SDA is low! Here's a picture that shows it (the marked START is the REPEATED START). https://thumbs2.imgbox.com/a5/4e/svMT8nVZ_t.png (click to enlarge) According to this picture, in the single REPEATED START cycle SDA has to go from low to high and then from high to low (while SCL is held high, otherwise an additional cycle would be executed). Now, apart from the fact that I can't see how the chip can sense both changes with SCL staying high, pulling SDA high while SCL is high is the specular operation of START and, in fact, it's a STOP, so doing that actually aborts the operation. In this context, that should be fine because the random read is actually the combination of two operations: the beginning of a random write, to provide the address to start reading from, and a sequential read, which reads multiple bytes from the currently latched address. So, in theory, aborting the write and starting a sequential read should work just fine (and in UAE it does). In practice, the tests reported the opposite. Therefore, I tried with an extra cycle: 1. pull SCL low; 2. pull SDA high (so now SDA is high without generating a STOP condition); 3. pull SCL high; 4. pull SDA low (this generates the START condition for the read). This gave the exact same result on both WinUAE and the real machine. For completeness, a couple of words about the memory reset. The procedure indicated in the datasheet is: "After an interruption in protocol, power loss or system reset, any 2-wire part can be reset by following these steps: 1. Clock up to 9 cycles. 2. Look for SDA high in each cycle while SCL is high. 3. Create a start condition." Step 3 looks quite weird. If taken literally, it means that when a read or write operation starts, a second START condition has to be generated - pretty odd. Anyway, I've tried both with and without step 3, and the results were exactly the same. So, what am I missing? Thanks in advance for your help. |
My first guess is that when reading from the chip, data direction must be kept in input mode all the time. Chip probably detects changing data line state when data direction is changed. Probably also data state and direction change must be done in correct order to prevent glitches.
Emulation (EEPROM emulation part is from qemu) probably ignores bad states where real chip most likely means undefined behavior. Emulation has logging that may help (recompile is needed to enable them) but most likely these problems are caused by some undocumented Akiko behavior when data direction changes or something similar... I can quickly run it if you attach executable binary. |
If you think the explanation is bad, just find one for a different I2C part, the protocol is always the same.
Read the I2C wiki page, it has a specific example for eeproms, and also explains the protocol. What kind of interface is there from the Amiga side, by the way? Is the eeprom just connected to a couple of Akiko GPIO's, or does it have a dedicated I2C controller? Edit: a repeated start is the same wire state as START, by way, it just means that bus doesn't go idle between the repeated start and the previous transaction. |
Thanks for the quick reply, Toni!
Quote:
Quote:
Quote:
Quote:
|
Quote:
Quote:
Quote:
|
Quote:
|
Quote:
Well, I had also looked at the datasheet of an ATMEL EEPROM of 64+ kB, and there the diagram did feature the extra cycle ;) |
@Toni
Quote:
Code:
************************************************************************************************************************ On WinUAE it works in the context of both the bigger program I'm doing all this for and the test proggie. You can find the latter attached here. It takes over the system, dumps the NVRAM to RAM:NVRAM.raw, restores the system, and quits. I'm sending it to my tester, too, and when I have a response I'll let you know. |
Why not disassemble the lowlevel library to understand how CBM did it?
|
psygore has written some nvram lib in asm, and this time he provided the source code.
I didn't use it in CD32load because I think CD32 NVRAM is too small and there will be conflicts between games (the NVRAM is made to play one game to the end, then play another, deleting previous game data), but I'm pretty sure it's very good given the quality of Psygore prods. |
Quote:
But thanks for the suggestion. |
Quote:
|
OK, I have the results of a few tests. Before reporting them, a quick note.
Before getting bytes reading to work (which is what the new version of the ReadByte() function tries do to), it is necessary to be able to read single bits, as when an operation is started the ACK from the EEPROM needs to be read. The test code I posted in the OP basically tries all these cases: 1. set SDA to input -> pull SCL low -> pull SCL high -> read SDA -> set SDA to output; 2. pull SCL low -> set SDA to input -> pull SCL high -> read SDA -> set SDA to output; 3. pull SCL low -> pull SCL high -> set SDA to input -> read SDA -> set SDA to output. To me, only the first one makes sense. And yet it's the one that on a real Amiga CD32 returns 1 when reading the ACK, which should be 0. So either bit reading doesn't work, or the EEPROM is actually returning a NACK (I'll deal with the latter case later). Therefore, it dawned on me that there was still a case left: setting SDA to input and pulling SCL low at the same time, with a single longword write to both the registers involved. So, I have modified ReadBit() to do this: set SDA to input and pull SCL low -> pull SCL high -> read SDA -> set SDA to output Code:
ReadBit move.l #NM_SCL<<8,(a3) ;set NVRAMDIR.SDA to input and pull SCL low (NM_SCL<<8 = $00008000) Code:
ReadByte moveq.l #7,d3 ;set counter extra cycle restart + single-write byte reading -> dump no + no -> first 17 bytes: 0; other bytes: $ff no + yes -> all 0s yes + no -> first 57 bytes: 0; other bytes: $ff yes + yes -> all 0s Evidently, nothing really works. Now I'll check for the umpteenth time the START condition generation and then try the single-write strategy with it, repeating all the tests above. If nothing helps, I'll change the whole code again to have SDA permanentely set to input, except for when sending bits - and then perform all the tests above. In the meanwhile, any thoughts? |
http://www.winuae.net/files/b/winuae2.7z has CD32 EEPROM debugging enabled. Perhaps it helps to find the differences between ROM code and your code.
Messages that start with I2C are EEPROM emulator debug messages. Direction/data read/write are Akiko I2C port access messages = logs of messages.. NOTE: direction writes that don't change anything are filtered to reduce log flood. ROM code rewrites it every time, even if old and new value is same. |
Quote:
I do have one suggestion though - don't just try random stuff until you get it working. Learn as much as you can about I2C EEPROM protocols and do the job properly. Then those of us who don't mind looking at other people's code won't be disappointed by the quality or confused by weird hacks. |
@Toni Wilen
Thank you, I'll give it a try as soon as I can. @Bruce Abbott I have the impression you formed the idea that I want to get things done without caring about quality and without making the effort to understand. It's exactly the opposite, instead. Quote:
Note: I'm not implying neither I implied before that the nonvolatile.library code is necessarily bad or that my code is better. I only said that I prefer a clean-room implementation tailored to my (strict) needs and based on the EEPROM documentation. Quote:
I got the code to work on UAE quickly. Unfortunately, the code doesn't work on a real Amiga CD32, unfortunately I don't have a real Amiga CD32 to play with, and even more unfortunately the Akiko chip isn't fully documented, so the only option I had was to write test programs that covered all the possibilities I could think of and disturb a friend to run them to try to figure out what I was doing wrong (and the process was excruciatingly difficult also because he is in another country and in the middle of a very busy period, so I was getting the results with days or even weeks of delay) - that's quite different from trying random stuff. Finally, since I could not get things to work, I came here, I openly acknowledged my failure, and, knowing that there are people more knowledgeable than me (especially about Akiko, which seems to be the critical component here), I asked what I was doing wrong. Toni provided a suggestion, and, although previously I had made tests according to the same idea, I made some more tests, reported their results, and planned to make even more. All along, I've been proceeding in the most rigorous way I could. Now, did I misunderstand anything? I don't know, that's why I asked. Did I do anything wrong? I don't know, that's why I asked. Do I miss some information (about Akiko)? I don't know, that's why I asked. For sure, I'm not trying to come up with a solution by randomly assembling a weird hack. I hope this provides a satisfying clarification. |
@Toni Wilen
I just started looking at the log relative to the boot of the machine. Some things are as expected, others aren't. Before continuing, I'd like to verify whether my reading is correct. First of all, the initial snippet of the log, with notes added by me: Code:
log message SCL SDA operation cycle Code:
log message SCL SDA operation cycle Code:
log message SCL SDA operation cycle Code:
log message SCL SDA operation cycle Looking closely, if I'm interpreting the log correctly, this is what happens: 1. the last bit sent before this block is 0 (so SDA is low); 2. SDA gets set to input; 3. SCL gets pulled low to start the 9th cycle; 4. the state of SCL and SDA at this point is 40, which means that SDA must have been pulled high by the EEPROM - is this correct? Noteworthy: here the direction gets changed before pulling SCL low. Code:
log message SCL SDA operation cycle Looking closely: 1. SCL is pulled high to start the second part of the 9th cycle (at this point, SDA is still high); 2. the code performs a read from the direction register: what for? trigger a refresh according to the actual voltage of the wires? (this might well be one key element I was missing); 3. the data register gets read, an SDA is now 0 (meaning that the EEPROM has sent the ACK); 4. the code pulls SCL low (starting the 10th cycle, which the sending of the address offset starts at), but both the starting and the current value of SDA are now high: is that correct (in the real chip, the current value is not stable yet; still, why was the starting value 1 instead of 0, given that the last bit sent over the wire was 0? emulation state error? logging error?); 5. the code tries to pull SCL low again (without any effect): why? 6. the direction of SDA is restored to output. I wonder: what's with all this apparently superfluous fiddling during the 9th cycle? Also, here, unlike before, the data direction gets changed after starting the cycle: does this mean that for reading the change must be done before pulling SCL low and for writing it must be done after? Code:
log message SCL SDA operation cycle |
Direction read is most likely simply side-effect of code using bset/bclr or or/and to change the state (even if it is already in correct state). I am quite sure read does not do any side-effects.
|
Quote:
|
Quote:
Quote:
Quote:
Quote:
|
All times are GMT +2. The time now is 17:40. |
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.