About the Wii's SEEPROM
About the Wii's SEEPROM

Chapter 1: Introduction

The Wii contains a Serial Electrically Erase-able Programmable Read Only Memory (Serial EEPROM aka SEEPROM) chip. It is an Atmel AT93C56 Model chip.

The SEEPROM uses 16 bits for a word. There is a total of 128 (0x80) words. The use of 16-bit words causes 'offsets' to be a bit weird when talking about the location of data in the SEEPROM. For example, if one was to conventionally say the location/offset of 0x86 on the chip, the actual location/offset is 0x43. This can be a bit confusing.

Chapter 2: Hollywood GPIO's

You can communicate with the SEEPROM via the Wii's GPIO Pins. For sending info to the SEEPROM, you use the GPIO_OUT register (0xCD8000E0), and for receiving info from the SEEPROM, you use the GPIO_IN register (0xCD8000E8).

Within the GPIO_OUT register, you send data (communicate) to the SEEPROM via the following 3 bits...

CS (Chip Select) = Bit 21
SK (Clock) = Bit 20
DI (Data from Wii to SEEPROM) = Bit 19

Within the GPIO_IN register, there's one bit to read data from the SEEPROM.
DO (Data from SEEPROM to Wii) = Bit 18

Thus, if you are reading data from the SEEPROM, you would NEVER use the GPIO_OUT register.

Chapter 3: SEEPROM Instructions

The quickest way to learn how the Wii's SEEPROM works is reading this PDF file HERE. If you don't want to read the whole PDF, then simply read below, I go over various items that only matter for the Wii/HBC.

There are various Instructions/Commands you can use to communicate with the SEEPROM. They are...

Every instruction has a Start Bit aka SB. All instructions have a SB of 1. After SB is the OpCode, this varies per every instruction. Next is the Address x16 and/or Data x16. All instruction values are in binary. Fyi 'x16' simply means that the chip is using 16 bit words.


EWEN (Erase-Write Enable)

SB = 1
OpCode = 00
Address x16 = 11XXXXXX

X = Don't care values, doesn't matter what they are.

This instruction MUST come first before executing any other instruction (except for READ). The Hex form of this instruction can vary due to the last 6 bits being don't care values. But for convenience we will say the Hex form of EWEN is 0x4C0 (all don't care values set to 0).


READ (Read)

SB = 1
OpCode = 10
Address x16 = XXXXXXXX (The SEEPROM's Offset)

This instruction will output a stream of 17 bits of data via the DO bit of the GPIO_IN register. The first bit is always a dummy bit (value of 0). There is a easy way to get around this dummy bit (more on that later). Excluding the dummy bit, the READ instruction will output the 16-bit word of data from the SEEPROM Offset that was designated.

As mentioned earlier the offset donations are different due to the SEEPROM's 16 bit word size. Offset values range from 0x00 to 0x7F.

Hex form for READ Instruction for SEEPROM offset 0x7F = 0x67F.


WRITE (Write)

SB = 1
OpCode = 01
Address x16 = XXXXXXXX (The SEEPROM's Offset)
Data x16 = ZZZZZZZZZZZZZZZZ (The full 16 bit word to write at designated offset)

This instruction will allow you to write in a new 16-bit word at the designated offset. You can write anywhere on the SEEPROM, but writing at the wrong spot will result in a perma-brick.

The WRITE Instruction is the longest instruction as you need to send your 16 bit word (that gets written to the SEEPROM) after sending the bits of SB, OpCode, & Address x16.

Hex form for WRITE value (0x1234) at SEEPROM offset 0x50 = 0x5501234


ERASE (Erase)

SB = 1
OpCode = 11
Address x16 = XXXXXXXX (The SEEPROM's Offset)

What is Erase? Erase means set every bit of the word to '1'. This changes any SEEPROM word value to 0xFFFF. ERASE is quicker to do than WRITE, thus it may be better to execute an ERASE instruction if you no longer need certain data on the SEEPROM.

Just like with the instruction WRITE, executing ERASE at the wrong spot will result in a perma-brick.

Hex form for ERASE at SEEPROM offset 0x02 = 0x702


EWDS (Erase-Write Disable)

SB = 1
OpCode = 00
Address x16 = 00XXXXXX

X = Don't care values

Whenever you are finished with Erasing/Writing to the SEEPROM, you should always run this instruction to prevent further unwanted interaction with the SEEPROM. It's not really needed as every time power is supplied to the SEEPROM (from the Wii turning on), this instruction is executed (on Starlet's side) for extra safety.

Hex form for EWDS is 0x400.


ERAL (Erase All)

SB = 1
OpCode = 00
Address x16 = 10XXXXXX

X = Don't care values.

This instruction will only work when the power being supplied to the VCC pin is anywhere from 4.5V to 5.5V. This instruction sets EVERY bit of the entire SEEPROM to '1'. Thus every 16 bit word on the SEEPROM becomes 0xFFFF.

Hex form for ERAL is 0x480


WRAL (Write All)

SB = 1
OpCode = 00
Address x16 = 01XXXXXX (X = Don't care values)
Data x16 = ZZZZZZZZZZZZZZZZ (The 16 bit word value that will be written at every SEEPROM offset)

This instruction will only work when the power being supplied to the VCC pin is anywhere from 4.5 V to 5.5 V, and when the Disable Register (no idea what this is...) is cleared. This instruction will write the 16 bit word value (designated by Data x16) at every offset of the SEEPROM.

Hex form for WRAL (with value 0x1234) is 0x4401234.


Chapter 4: Communicating with the SEEPROM (Program/Code Writing Assistance)

Before you can communicate with the SEEPROM, you need HW_AHBPROT disabled. Normally, only Starlet can talk to the SEEPROM. If you are using HBC Versions 1.1.0 or later (with the meta.xml file using the <ahb_access> tag, you will have HW_AHBPROT disabled.

At the start of any program that communicates w/ the SEEPROM, should always unclock the SEEPROM and turn off Chip Select at the very beginning. Doing this is very simple. You simply need to set the SK bit and CS bit in GPIO_OUT both to 0.

With all of that being said here is an example snippet of code..

lis r12, 0xCD80 #Set first half address of the GPIOs
lwz r11, 0x00E0 (r12)
rlwinm r11, r11 0, 21, 19 #Clear bit 20 aka SK
rlwinm r11, r11, 0, 22, 20 #Clear bit 21 aka CS
stw r11, 0x00E0 (r12)

bl wait #Some wait/sleep subroutine

Notice the use of the sync and eieio instructions. Every time you load a value from the GPIOs, you need to use the sync instruction immediately afterwards. Every time you store a value to the GPIOs, you need to use the eieio instruction immediately afterwards. This is because, by default, PowerPC can sometimes do out-of-order execution. You won't be able to see this 'out-of-order' execution effect in a debugging tool (such as Gecko or Dolphin). Load instructions can be executed out of order to how they were listed. While store instructions are always executed in order, but that doesn't mean they are executed in order in reference to other instructions.

If all this is new to you, don't freak out over this 'out of order' stuff in regards to MKWii codes. This 'out of order' stuff is only a concern for communicating with an I/O (input-output) device such as the SEEPROM (due to its various timing requirements which will be explained in the next paragraph). Without going into endless detail, the sync & eieio instructions will make sure everything is in order and will allow us to properly 'time' the communication with the SEEPROM.

The 'bl wait' would be some subroutine you use to allow a certain amount of time to pass by. Every instance you communicate to the SEEPROM, you need to wait a certain amount of time. This time can vary depending on what instructions are being sent, when they are sent, etc. HBC program devs/authors in the past have used a huge time lot of 5 microseconds. This is more than enough to cover any situation. However, I personally use a time allowance of 2 microseconds, and have had no issue with this decreased amount of time. If you want to cut it down smaller than that, I recommend  lots of testing.

You can either use devkit's dedicated usleep function (in C or inline ASM), or make your own. If making your own please note that for Broadway, the shortest time an ASM instruction can take (something such as a nop) is one cycle/tick. Assuming every ASM instruction you had was a nop and factoring in the Wii's bus frequency, it takes 60.75 ticks/nops per 1,000 nanoseconds. Thus, a nop takes 16.46 nanoseconds to complete. Other ASM instructions however can take a few ticks to complete.

The SEEPROM has synchronous timing, hence why we can turn off both bits then wait. Instead of turning one bit off, waiting, turning the next bit off, waiting again.

Before Executing a SEEPROM Instruction ~

Every SEEPROM instruction must be executed on a 'Rising Edge' of Chip Select. This means immediately after you turn on the Chip Select bit, you must (as quickly as possible) start sending your bits (via DI in GPIO_OUT) or else the SEEPROM will not read the stream of bits as an instruction to execute. The first bit of the instruction must be sent (within 200 nanoseconds) after storing your data (with CS bit ON) to the GPIO_OUT register. When sending bits, you always start off with the SB bit and end with the Address x16/Data x16 final bit.

After you send your first bit (via DI in GPIO_OUT) to the SEEPROM, you will need to wait, clock the SEEPROM, wait, unclock the SEEPROM, and wait again before you send your next bit. Once you do this cycle and all bits are sent, the SEEPROM instruction will execute.

If you were doing a READ instruction, you can now utilize the DO bit GPIO_IN register to read the output stream of bits for the data you wanted to view/dump from the SEEPROM.

Busy/Read Signal ~

With the WRITE, ERASE, WRAL, & ERAL instructions, they can take some time to complete, thus you need to check the busy/read signal, before you execute another instruction. This busy/read signal is sent in the DO bit of GPIO_IN. A 0 indicates busy, while a 1 indicates the SEEPROM is ready for another instruction. Once you have sent all your bits for a WRITE/ERASE, you need to write a loop that will check this DO bit. However, before you check this busy/read signal, you must turn off the CS bit (keep it off for at least 1000 nanoseconds aka 1 microsecond), turn it back on and wait another 1000 nanoseconds.

Once the busy signal has gone away (DO bit is 1), you can execute another Instruction. However, you must follow this rule... Every time you want to execute another instruction, you must turn off the CS bit, and wait at least 1000 nanoseconds. Then you can finally turn on the CS bit, and send your instruction bits during the Rising Edge of Chip Select.

More info regarding READ ~

Let's say you executed your READ instruction and now you want to read the output stream of bits from the DO bit of GPIO_IN. Welp for some reason the devs of Atmel made it to where the first bit sent is always a dummy bit (value of 0). There's an easy way around this, simply clock then unclock the seeprom before reading the 1st output DO bit of GPIO_IN. Afterwards, you simply clock-unclock again to read the next output bit, repeat this cycle until you have all 16 bits.

About EWDS ~

Let's say you are at the end of your program. It's not needed but it's recommended to execute the EWDS instruction. This will put the SEEPROM back into a state where an EWEN instruction must be executed again before erasing/writing is allowed. If your program ends with the Wii being shutoff then you DON"T need EWDS. As mentioned earlier, every time the WIi is booted Starlet sends a EWDS instruction to the SEEPROM as a means of extra safety.

Some more things to NOTE:
When executing any WRITE/ERASE instructions, you NEED to disable interrupts. 

Snippet of code for Disabling Interrupts:

mfmsr r11
rlwinm r12, r11, 0, 17, 15
mtmsr r12
rlwinm r10, r11, 17, 31, 31 #Do NOT lose this value in r10 (you may need to store it somewhere).

Once your program is at its end, its time to restore interrupts. Snippet of code for Restoring Interrupts:

cmpwi r10, 0 #Assuming you didn't move/overwrite the value in r10 from above
mfmsr r4
beq- clear_bit

ori r5, r4, 0x8000
b skip_clear_bit

rlwinm r5, r4, 0, 17, 15

mtmsr r5
rlwinm r3, r4, 17, 31, 31 #Not needed unless you want this as a return value

Chapter 5: About ERAL and WRAL

I have written various programs in ASM all using EWEN, READ, WRITE, ERASE, and EWDS. However when I wrote some super simple programs to execute ERAL/WRAL then return to HBC, my program would not return. The TV Screen would remain black indefinitely. I was thus forced to reboot the Wii (holding power button til off).

This caused me to lose 2 Wiis (1 for testing ERAL, and 1 for testing WRAL). It's possible that there is some 'behind the scenes' stuff constantly checking certain values of the SEEPROM, and the very moment these values were checked, the Wii halted. I have checked the source for both ERAL & WRAL HBC programs, and every instruction checks out. I could potentially risk a 3rd Wii by using the ERASE instruction in a big loop to erase the entire SEEPROM (to mimic ERAL), but the only Wii's I have left are boot2 Wiis.

Chapter 6: Example Functions

Some developers have already made C code to interact with the SEEPROM. Team Twiizers wrote some C to be able to read any length of data from any location/offset of the SEEPROM. Tueidj would later write a snippet of C code allowing you to write any type/length of data you want to write to the SEEPROM.

Nobody has written any functions/code to utilize the SEEPROM's ERASE, WRAL, & ERAL instructions. I thus went ahead and made all those functions.

seeprom_erase - http://mkwii.com/downloads/seeprom/seeprom_erase.s
seeprom_format (mimics ERAL) - http://mkwii.com/downloads/seeprom/seeprom_format.s

Here are my ERAL and WRAL functions that halted and bricked my Wiis---
seeprom_eral - http://mkwii.com/downloads/seeprom/seeprom_eral.s
seeprom_wral - http://mkwii.com/downloads/seeprom/seeprom_wral.s

All the above files are licensed under Apache 2.0. Notice of the Apache 2.0 License is present in those files.


Here are optimized ASM versions I worked on for the functions seeprom_read (created by Team Twiizers) and seeprom_write (created by Tuiedj). The functions' license (both are GPLv2) plus original C code is provided.

seeprom_read - http://mkwii.com/downloads/seeprom/seepromREAD.zip
seeprom_write - http://mkwii.com/downloads/seeprom/seepromWRITE.zip

Chapter 7: Seeprom Viewer HBC App + Other Apps

Here's an app I made that displays all your SEEPROM contents on the TV screen - https://github.com/VegaASM/Seeprom-Viewer
Here's an app using my seeprom_erase function to erase 3 Atmel words at Offset 0x78 (you will auto return to HBC) - https://github.com/VegaASM/Seeprom-Erase-Demo
Here's some source (to compile to an app, you will need to add an icon.png and meta.xml w/ the <ahb_access/> tag) using seeprom_format.s to mimic ERAL, you are auto returned to HBC. This MAY brick you (I won't run this, I don't wanna risk losing yet another Wii) - http://mkwii.com/downloads/seeprom/Seepr...c-ERAL.zip

Welp, that's it. Thank you for taking the time to read this all.

Forum Jump:

Users browsing this thread: 1 Guest(s)