Homebrew DSi Reverse Engineering: SD/MMC/SDIO Registers

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Last edited by nocash123,

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
I am troubled with the DATAEND bit. Theoretically it should get set after data transfers. It's working with CMD17 (read single block), but it doesn't ever seem to get set after CMD18 (read multiple blocks). And alongsides, the CMD_BUSY stays set forever after sending CMD18.
To some level that does make sense since CMD18 is meant to read an infinite amount of data. But, the sd/mmc controller should be capable of sending CMD12 (stop transmission) automatically once when the selected number of blocks are transferred.
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Added some details on the IRQ/STATUS bits (simple stuff like SD Card eject/writeprotect flags and notes about seemingly unused/constant bits).
I am still lost on DATAEND.
One other very interesting thing would be understanding port 4004900h. Bit1 seems to be some very important mode bit (the firmware seems to handle things totally differently depending on wheter that bit is 0 or 1) (the bit seems to be R/W, so it looks like being a control bit, not a status bit). The other bits seem to be two IRQ enable bits and two IRQ flags, maybe somehow similar to the normal TXRQ/RXRDY data request bits, but I don't know how those bits are working exactly.

PS. about the SD Card write-protect flag: As far as I know, the 3DS is having a microSD slot (=without write-protect sensor), so the writeprotect bit should be always 1=Unlocked on 3DS, right?
Should be probably as so... unless the 3DS is having something like a write-protect dip-switch instead of SD card write-protect sensor.
 
Last edited by nocash123,

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
Added some details on the IRQ/STATUS bits (simple stuff like SD Card eject/writeprotect flags and notes about seemingly unused/constant bits).
I am still lost on DATAEND.
One other very interesting thing would be understanding port 4004900h. Bit1 seems to be some very important mode bit (the firmware seems to handle things totally differently depending on wheter that bit is 0 or 1) (the bit seems to be R/W, so it looks like being a control bit, not a status bit). The other bits seem to be two IRQ enable bits and two IRQ flags, maybe somehow similar to the normal TXRQ/RXRDY data request bits, but I don't know how those bits are working exactly.

I have some ideas about them, if i'm wrong well i'll be learning more stuff.

1) master , slave bit/endianness setting?

a)
I remember MMC and SD (through SDIO) having a master-slave configuration where two controllers would work in tandem through an AHB interface.
Set to zero = Only Master (MMC)
Set to one = Master and Slave (MMC / SD)

b)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0278b/Cegbbbab.html

32-bit AHB ports

If the global MPMCBIGENDIAN signal is LOW, all 32-bit AHB ports are fixed as little-endian.

If the global MPMCBIGENDIAN signal is HIGH, all 32-bit AHB ports are fixed as big-endian.

or

2)

https://arasan.com/wp-content/media/DS-DSI_MCR7HST.pdf

this is not from DS / DSI (but i'll say it's a coincidence the pdf is called as DS-DSI), but if you look at page 5, you can see the AHB bus used by ARM Cores using a SD/MMC/others controller.

SD/SDIO/MMC/Memory Stick/Memory Stick Pro Host controller The SD/SDIO/MMC/Memory Stick/memory Stick Pro Host Controller comprises of Host_AHB interface, Host controller registers, Bus monitor, Clk_gen, CRC generator and checker (CRC7 and CRC16). The Host_AHB interface acts as the bridge between AHB and Host Controller. The SD/SDIO controller registers are programmed by the ARM Processor through AHB slave interface. Interrupts are generated to the ARM Processor based on the values set in the Interrupt status register and Interrupt enable registers. Bus monitor will check for any violations occurring in the SD bus and time-out conditions. The Clock generation block will generate the SD clock depending on the value programmed by the ARM Processor in the AHB Processor AHB interface control status Register Timing Control Buffer0 Buffer1 Transaction controller CF/+ Interface Controller AHB Bus CF/+ card Operations Registers ECC Sync Buffer xD Interface xD Card CF Bus xD Bus synchronizer power Management SD registers DATA FIFO Bus monitor SD/Mem stick pro Protocol Unit clk control SDIO/ SDMem MMC Memory Stick Pro Card SDIO/ SDMem MMC Memory Stick Pro Card 1x1k SD / SDIO / e•MMC / MSPro / MSPro-Duo / CF+ /xD Controller

The interrupt part makes sense with what you said earlier at the wiki. Basically the (host card controller) AHB interface generates interrupts to the ARM processor through the (slave card controller) AHB co-processor. This programmed IO data transfer happen only if there is not any DMA controller used by the ARM Cores. Any interrupt bits set in the interrupt registers (host AHB interface) will cause the ARM core to raise an IRQ from what I am reading.

or..
Who knows this may be something:

3)

Power control The SD2.0/SDIO2.0/MMC4.5/Memory Stick Pro Host Controller supply SD Bus Power depending on the value programmed in the Power Control Register by the ARM Processor. The ARM Processor has the responsibility to supply SD Bus Voltage according to card OCR and supply voltage capabilities depending on the Host Controller. If the SD Bus power is set to 1 in the Power Control Register, the Host Controller shall supply voltage to the Card. If the Host Driver selects an unsupported voltage in the SD Bus Voltage Select field, the Host Controller may ignore write to SD Bus Power and keep its value at zero.

Which basically allows the host AHB interface to energize the SD controller, but if the power value read at SD Bus Select field is not compatible , the host AHB interface will ignore any writes to this register, by setting it to zero.
 

Normmatt

Former AKAIO Programmer
Member
Joined
Dec 14, 2004
Messages
2,161
Trophies
1
Age
33
Website
normmatt.com
XP
2,187
Country
New Zealand
I am troubled with the DATAEND bit. Theoretically it should get set after data transfers. It's working with CMD17 (read single block), but it doesn't ever seem to get set after CMD18 (read multiple blocks). And alongsides, the CMD_BUSY stays set forever after sending CMD18.
To some level that does make sense since CMD18 is meant to read an infinite amount of data. But, the sd/mmc controller should be capable of sending CMD12 (stop transmission) automatically once when the selected number of blocks are transferred.
You can see the 3DS sdmmc code I wrote https://github.com/mid-kid/CakesForeveryWan/tree/master/source/fatfs/sdmmc which uses the hardware to send that CMD12.
It should be compatible with the DSi code if you just modify the register base address.

I never did get the 32bit fifo code working so if you can spot any bugs in it please let me know.
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
I have some ideas about them, if i'm wrong well i'll be learning more stuff.
Great queer ideas. Port 4004900h looks more like data-transfer-interrupt stuff to me, but some of the control registers (like the CARD_OPTION or TRANSACTION_CTL registers etc) might be actually having some odd configuration options (eg. an endian option, even if it's never used by existing code).

AHB is a bus commonly used by ARM processors? No idea if any such thing is used in nintendo handhelds. If it's there, then it must be hiding internally inside of the main CPU chip (since the ARM CPU and SD/MMC controller are both located in the same chip).

Not sure what you meant about master/slave, or if there's anything similar in DSi. There are apparently two controllers (at 4004800h and 4004A00h) shared for three devices (eMMC/onboard, SD/cardslot, and SDIO/wifi). No idea if any of the two controllers or three devices could be treated as a master or slave... Pretty sure that the MM and S in MMC and SD are standing for Multimedia and Secure, not for Master and Slave.

I don't see anything that would hint on the arasan datasheet being related to DSi. The datasheet doesn't contain too much tech info... and general stuff like having interrupt flags paired with interrupt enable bits is very common, nothing specific to the DSi.

Don't know if there's a special DMA mode, ie. something that switches the chip into a state that works only with DMA. Port 400490Ch is probably mainly intended for use NDMA source/destination. But the firmware is also containing code for accessing the port by software, so it doesn't seem to be restriced to DMA-only.

Been thinking about voltage control, too. The eMMC suppy is wired straight to VDD33. The SD card supply is SD10_VDD, which appears to be just shortcut to VDD33 (maybe with an inductor coil between it). So, it doesn't seem to be possible to change/disable the supply voltage. Changing the data/clk/cmd/reply signal voltages might be possible, the old toshiba datasheets don't mention any such feature though.
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
You can see the 3DS sdmmc code I wrote https://github.com/mid-kid/CakesForeveryWan/tree/master/source/fatfs/sdmmc which uses the hardware to send that CMD12.
It should be compatible with the DSi code if you just modify the register base address.
Thanks, I think I've already seen that (btw. there's also similar source code for DSi in libnds/devkitpro). It's a bit easier to read than disassembling the firmware, and good to get an idea about what is needed to be done in which order for accessing the sd/mmc.
But even with the source code, it does still look as if it would need a lot of reverse-engineer to understand what the source is doing. Half of it seems to be just 'copied' from the old toshiba driver or nintendo firmware, without knowing why one needs to write this or that values to certain registers.

I never did get the 32bit fifo code working so if you can spot any bugs in it please let me know.
The DATA32_SUPPORT stuff doesn't work? Good to know! That would have been a nasty trap... trying to understand how you got the data32 stuff working ;- )

Some notes on the code...
Code:
        //sdmmc_mask16(0x100,0x800,0);
        //sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this
        *(volatile uint16_t*)0x10006100 &= 0xF7FFu; //SDDATACTL32
Uh, that stuff would be easier to read if you would stick to use to symbols like REG_SDRESET instead of using 16bit offsets like 0x100 and 32bit immediate addresses like 0x10006100 in other places.

Code:
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
...
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
That looks wrong to me. If you want to acknowledge the IRQ flags, then the "//sdmmc_write16" lines would be correct, but I guess the "//" means outcommented(?). And, no idea if those flags get set only on start-of-transmission, or if they stay set while and as long as data is to be transferred (or if they get set every some bytes on each 'chunk' of data or whatever). Ie. eventually acknowleding might work only after having transferred all data, so the "//sdmmc_write16" lines might not work in that section of code.

And the "sdmmc_mask16" lines, they are essentially doing the same thing (clearing the RXRDY or TXRQ bit), which is ok, but they are also clearing any bits that were zero at time when starting the read-modify-write operation, which is bad (it would destroy any status bits that are getting newly set during the read-modify-write).

For the DATAEND bit, that's processed here(?):
Code:
                        if(status0 & TMIO_STAT0_DATAEND)
                        {
                                ctx->error |= 0x2;
                        }
                        
                        if((status0 & flags) == flags)
                                break;
The first "IF" is triggering any error upon DATAEND?
And the second "IF" is terminating the while-loop upon DATAEND?
The latter one would be at least confirming that the hardware throws DATAEND after CMD18 (which means that I am doing something wrong when not receiving DATAEND in my code).
 

Normmatt

Former AKAIO Programmer
Member
Joined
Dec 14, 2004
Messages
2,161
Trophies
1
Age
33
Website
normmatt.com
XP
2,187
Country
New Zealand
Some notes on the code...
Code:
        //sdmmc_mask16(0x100,0x800,0);
        //sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this
        *(volatile uint16_t*)0x10006100 &= 0xF7FFu; //SDDATACTL32
Uh, that stuff would be easier to read if you would stick to use to symbols like REG_SDRESET instead of using 16bit offsets like 0x100 and 32bit immediate addresses like 0x10006100 in other places.

Yes that code can be cleaned up a lot...

Code:
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
...
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
That looks wrong to me. If you want to acknowledge the IRQ flags, then the "//sdmmc_write16" lines would be correct, but I guess the "//" means outcommented(?). And, no idea if those flags get set only on start-of-transmission, or if they stay set while and as long as data is to be transferred (or if they get set every some bytes on each 'chunk' of data or whatever). Ie. eventually acknowleding might work only after having transferred all data, so the "//sdmmc_write16" lines might not work in that section of code.

And the "sdmmc_mask16" lines, they are essentially doing the same thing (clearing the RXRDY or TXRQ bit), which is ok, but they are also clearing any bits that were zero at time when starting the read-modify-write operation, which is bad (it would destroy any status bits that are getting newly set during the read-modify-write).

the raw write clears all the other set bits and yes there is a period between the read-modify-write where another flag could potentially be set but there's not really any way around that.
The RX bit get set when the hardware has finished loading one block (0x200) into the internal fifo and TX is set when hardware is ready to accept writes to the fifo.

For the DATAEND bit, that's processed here(?):
Code:
                        if(status0 & TMIO_STAT0_DATAEND)
                        {
                                ctx->error |= 0x2;
                        }
                       
                        if((status0 & flags) == flags)
                                break;
The first "IF" is triggering any error upon DATAEND?
And the second "IF" is terminating the while-loop upon DATAEND?
The latter one would be at least confirming that the hardware throws DATAEND after CMD18 (which means that I am doing something wrong when not receiving DATAEND in my code).

It does throw DATAEND after its finished processing the CMD.
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
the raw write clears all the other set bits and yes there is a period between the read-modify-write where another flag could potentially be set but there's not really any way around that.
What why? Essentially both are writing. The only difference is that the raw write is ensuring that you are writing "1" to bits that should be left unaffected. I can't see how the raw write could have negative effects.

The RX bit get set when the hardware has finished loading one block (0x200) into the internal fifo and TX is set when hardware is ready to accept writes to the fifo.
It does throw DATAEND after its finished processing the CMD.
Okay, one RX/TX irq per 200h-byte block. And one final DATAEND irq after finishing all blocks. Sounds good.

Having a 200h-byte fifo would make sense. Though I am not sure where it would be located. Theoretically it could be located in the memory card, or in the memory card controller.
 

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,979
Trophies
2
XP
2,565
Country
Chile
Great queer ideas.
Queer? Wtf

AHB is a bus commonly used by ARM processors? No idea if any such thing is used in nintendo handhelds. If it's there, then it must be hiding internally inside of the main CPU chip (since the ARM CPU and SD/MMC controller are both located in the same chip).

Not sure what you meant about master/slave, or if there's anything similar in DSi. There are apparently two controllers (at 4004800h and 4004A00h) shared for three devices (eMMC/onboard, SD/cardslot, and SDIO/wifi). No idea if any of the two controllers or three devices could be treated as a master or slave... Pretty sure that the MM and S in MMC and SD are standing for Multimedia and Secure, not for Master and Slave.

Are you sure ? This is for the old ARM7TDMI GBA Arm Cores (under the AMBA spec). the AHB Bus is the official name for the default system bus spec, that ARM uses by protocol. You can see the setup as AHB Master and Slave units where as the AHB Slave is implemented on SD microcontrollers (for example I would see a trigger making sense from the SD controller to Slave AHB unit, so the Master AHB acknowledges as an IRQ from SD)

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0169d/CHDBHHHJ.html

3.1. About the ARM7TDMI AHB wrapper
The ARM7TDMI AHB wrapper interfaces between the ARM7TDMI or ARM7TDMI-S core and the AHB bus. The modules that translate access from the core to AHB accesses when the core is the current master are common to both cores. The wrapper itself sits alongside the core, intercepting the memory bus. An example higher-level module is also included for each core. This shows how the core and wrapper can be connected, and is used by the synthesis scripts provided to allow the wrapper to be synthesized alongside a timing file for the core.

For the ARM7TDMI only, the wrapper also allows testing of the ARM7TDMI core when the Test Interface Controller (TIC) is the current AHB master, allowing the TIF-format production test vectors supplied by ARM to be used with AHB-based designs. Figure 3.1 shows a top-level block diagram of the ARM7TDMI AHB wrapper.


Wasnt this the standard pinout of ARM7 cores... I read a year and half ago on gbatek? If not they're veery similar.

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0169d/i163109.html



edit: wow this explains a lot about signaling at ARM Cores. Definitely!.. Time to learn some more stuff I guess.
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0169d/index.html
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
I meant queer in terms of thinking about uncommon features, like voltage control (that's good). Many other people probably wouldn't think about of such hardware features.

The http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0169d/CHDBHHHJ.html link doesn't work for me, but works if if you replace "index.jsp?topic=/" by "topic", ie. http://infocenter.arm.com/help/topic/com.arm.doc.ddi0169d/CHDBHHHJ.html

But you are on the wrong way with the AHB stuff, at least it's totally offtopic and unrelated to the SD/MMC controller. If you want to go on with it: Best create an own thread for it!
Quite possible that it helps understanding some details about the gba/nds memory systems. I am a bit pessimistic about getting an ultimate answer to the question wheter nintendo consoles are using AHB or not. Most of the hardware (cpu, wram, i/o ports, video, sound, sd/mmc controller, etc) are integrated in a single chip, without external pinouts for most of the bus logic (except of course basic stuff like /RESET and ecternal main ram bus). I think best you could do would be something like observing if timing behaviour resembles the AHB specs, and then you still couldn't tell if it's AHB, or if it's just something else resembling AHB. But go ahead, if you find out something explains mysterious hardware effects would be great!
 
  • Like
Reactions: Coto

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Got my DATAEND bit received :- )
It's working only if STOP_INTERNAL_ACTION.bit8 is set.
So that bit seems to enable the BLK_COUNT counter and/or sending the STOP_TRANSMISSION command when that counter becomes zero.
The DATA16_BLK_COUNT value itself doesn't change though (so there appears to be an internal counter register).
I remember seeing a case where DATA32_BLK_COUNT got decreased (maybe that one decreases directly, without separate internal counter).

Btw. does somebody have a SD/MMC bus logger? I could think of 1-2 cases where it'd be interesting to see which commands/responses are transferred on the SD/MMC bus (eg. testing if SD_CMD bit6-7 are allowing to send GEN_CMD prefixes) (not that it'd be too useful for practical use, it would just eliminate some "unknown" details).

Or watching some SD/MMC signals with oscilloscope would be nice... but I am not too motivated to wire test points to the DSi mainboard...
If somebody could donate two spare "micro-SD to SD" adaptors (or one mini-SD and one micro-SD adaptor), then I could make some cable with neat test points easly accessible between the DSi and SD card.
Would be useful for measuring the variable transfer clock rates, or checking if there's a FIFO in the DSi (then the whole block should be transferred at once, even when the software transfers it with huge delays between the separate halfwords).
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Here is a list of FIXED READ-ONLY register values. The list consist of register base address, number of bytes (usually 2 if it's a single halfword), and the expected/fixed 16bit value (repeated several times if the number of bytes is bigger than 2, eg. for zero-filled regions).
Most of the registers with zero-value are probably just unused, though some of them might get nonzero in whatever situations (for example, 40048F6h.bit0 is tested by firmware, so that register does apparently have some function).
There are also a few register with fixed nonzero-value, those might also change in whatever situations, though I haven't yet encountered any such situation (except of course in cooking coach, which has all registers at 4004800..40049FF disabled/zero). One or two of the nonzero-values might contain stuff like chip ID, which would be really fixed (unless some later DSi or 3DS models have different revisions).
Code:
sdmmc_fixed_values:
 dd 400482Ah // dw 002h,00000h
 dd 4004832h // dw 002h,00000h
 dd 400483Ah // dw 006h,00000h  ;EDIT: added this line 28 Aug 2015
 dd 4004840h // dw 002h,0003Fh
 dd 4004842h // dw 002h,0002Ah
 dd 4004844h // dw 06Eh,00000h
 dd 40048B2h // dw 002h,0FFFFh
 dd 40048B4h // dw 006h,00000h
 dd 40048BAh // dw 002h,00200h
 dd 40048BCh // dw 01ch,00000h
 dd 40048DAh // dw 006h,00000h
 dd 40048E2h // dw 002h,00009h
 dd 40048E4h // dw 00Ch,00000h
 dd 40048F0h // dw 002h,00000h
 dd 40048F6h // dw 002h,00000h  ;for SD, used by firmware (bit0 tested), but always zero?
 dd 40048F8h // dw 002h,00004h  ;fixed/zero for SDIO, but fixed/0004h for SD
;dd 40048FAh // dw 002h,0000xh  ;fixed/zero for SDIO, but variable/0004h..0007h for SD
 dd 4004902h // dw 002h,00000h
 dd 4004906h // dw 002h,00000h
 dd 400490Ah // dw 002h,00000h
 dd 4004910h // dw 0F0h,00000h
 ;---
 dd 4004A2Ah // dw 002h,00000h
 dd 4004A32h // dw 002h,00000h
 dd 4004A3Ah // dw 006h,00000h  ;EDIT: added this line 28 Aug 2015
 dd 4004A40h // dw 002h,0003Fh
 dd 4004A42h // dw 002h,0002Ah
 dd 4004A44h // dw 06Eh,00000h
 dd 4004AB2h // dw 002h,0FFFFh
 dd 4004AB4h // dw 006h,00000h
 dd 4004ABAh // dw 002h,00200h
 dd 4004ABCh // dw 01ch,00000h
 dd 4004ADAh // dw 006h,00000h
 dd 4004AE2h // dw 002h,00009h
 dd 4004AE4h // dw 00Ch,00000h
 dd 4004AF0h // dw 002h,00000h
 dd 4004AF6h // dw 002h,00000h  ;for SD, used by firmware (bit0 tested), but always zero?
 dd 4004AF8h // dw 002h,00000h  ;fixed/zero for SDIO, but fixed/0004h for SD
 dd 4004AFAh // dw 002h,00000h  ;fixed/zero for SDIO, but variable/0004h..0007h for SD
 dd 4004B02h // dw 002h,00000h
 dd 4004B06h // dw 002h,00000h
 dd 4004B0Ah // dw 002h,00000h
 dd 4004B10h // dw 0F0h,00000h
 dd 0   ;end of list
Here's some example code for verifying the values, and throwing a warning message in case they happen to contain a different/unexpected value:
Code:
 ldr  r4,=sdmmc_fixed_values            ;\
@@verify_fixed_lop:                     ;
 ldr  r5,[r4],4  ;address               ;
 ldrh r6,[r4],2  ;len                   ;
 ldrh r7,[r4],2  ;fillvalue             ;
@@verify_fixed_inner_lop:               ;
 ldrh r8,[r5],2  ;address               ;
 cmp  r8,r7      ;fillvalue             ;
 beq  @@verify_fixed_inner_next         ;
 ldr  r1,=txt_fixed_mismatch            ;
 bl   wrstr                             ;
 mov  r0,r5      ;address               ;
 bl   wrhex32bit                        ;
 bl   wrspc                             ;
 mov  r0,r8      ;value                 ;
 bl   wrhex16bit                        ;
 bl   wrcrlf                            ;
@@verify_fixed_inner_next:              ;
 subs r6,2       ;len                   ;
 bne  @@verify_fixed_inner_lop          ;
 ldr  r0,[r4]    ;address               ;
 cmp  r0,0       ;end of list           ;
 bne  @@verify_fixed_lop                ;/
Normmatt, could you add a similar function in your sdmmc driver, and invoke it here and there (best before/during/after doing stuff like initialization, sending commands, reading data, etc)? It would mess-up your code a bit, but it should be no problem to disable or completely remove it at a later time. It would be interesting if you encounter any situations where any of the register values get changed. Or if the 3DS comes up with extra registers or different values in the nonzero registers.
 
Last edited by nocash123,

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Lots of more info, especially about the DATA32 mode. This should allow to get the DATA32_SUPPORT working (at least for reading, I haven't yet tested writing).

First of, selecting DATA32 mode requires setting both 40048D8h.bit1 and 4004900h.bit1. For DATA16 mode, both bits should be probably cleared, but DATA16 seems to be working also when only either of the bits is zero (I haven't found any difference there, it doesn't seem to matter which bit is cleared, or if both are cleared).

Next, in DATA32 mode, one should use the two IRQ flags in 4004900h instead of the RXRDY, TXRQ, DATAEND, CMD_BUSY bits in 400481Ch. Those bits do still exist, but their meaning is somehow changed. For example, DATAEND and CMD_BUSY are getting toggled before reading the last block.

Below are 400481Ch (8 digits) and 4004900h (4 digits) values, logged before sending the command, and before/during/after reading data, showing the differences between DATA32 and DATA16 mode.
When BLK_COUNT=1 (single block):
Code:
  ;data32 mode:                              ;data16 mode:
  ; 20800421 1802                            ; 20800421 1800
  ;---
  ; 20800425 1B02  DATAEND + FLG32's         ; 41800421 1800  RXRDY+CMDBUSY
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1802  bit9 cleared              ; 21800425 1800  DATAEND + CMDrdy
  ;---
  ; 20800421 1802  DATAEND acked             ; 20800421 1800

When BLK_COUNT=2 (two blocks):
Code:
  ;data32 mode:                              ;data16 mode:
  ; 20800421 1802                            ; 20800421 1800
  ;---
  ; 41800421 1B02  RXDRY+FLG32's             ; 41800421 1800  RXRDY+CMDBUSY
  ; 41800421 1B02                            ; 41800421 1800
  ; 41800421 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1B02  bit8 set, DATAEND, NUM=1  ; 41800421 1800  NUM=2(not1)
  ;---
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1802  bit9 cleared, NUM=1(not0) ; 21800425 1800  DATAEND + CMDrdy, NUM=2(not0)
  ;---
  ; 20800421 1802  DATAEND acked

The bottom line would be using 4004900h.bit8 instead of RXRDY for reading (and probably also instead of TXRQ for writing; but haven't tested that yet).
And using 4004900h.bit9 instead of DATAEND/CMD_BUSY. Or, you could also combine them: Check DATAEND and CMD_BUSY as usually, but do also ensure 4004900h.bit9=0 before treating the transfer as completed.

Note that 4004900h.bit8/9 are cleared automatically by hardware (unlike RXRDY,TXRQ,DATAEND which must be acknowledged manually).
Another odd effect is that DATA32_BLK_COUNT is decremented during DATA32 transfer, except after the last block: at that point the transfer is completed, but without decreasing the counter from 0001h to 0000h (ie. it stays set to 0001h). Whilst, DATA16_BLK_COUNT isn't decremented (instead DATA16 mode is apparently using an internal counter register, which isn't visible via I/O ports).

And one small detail: 400481Ch.bit29 appears to be just inverse of 400481Ch.bit30, ie.
400481Ch.bit29 = CMD_READY (?)
400481Ch.bit30 = CMD_BUSY
don't know if that rule does always apply, and if the two bits are toggled exactly at the same time.

When looking at 4004900h.bit8/9 versus RXRDY, TXRQ, DATAEND, CMD_BUSY behaviour in DATA32 mode, it does somehow look as if DATA32 mode is transferring the incoming data to a FIFO (and toggles DATAEND/CMD_BUSY when writing to FIFO completed, but before the CPU starts reading the FIFO).
If that's right, then DATA16 mode might work without FIFO, ie. directly reading halfwords from the SD/MMC serial bus as they do arrive (that might work if it pauses the CLK signal when the CPU is reading too slow, or when applying WAITs when the CPU is reading too fast; though could mean really huge WAITs as the sd/mmc clock can be configured to very slow settings with only some kilobits/second.
Checking a hardware timer before/after reading could be used to confirm if there are WAITs occurring. Measuring the CLK signal would be also interesting to see if CLK gets paused between separate halfwords.
Anybody ready to do some scope tests (or donate the loads of spare microSD adaptors that you've hoarded in your stash)?
 
Last edited by nocash123,

Normmatt

Former AKAIO Programmer
Member
Joined
Dec 14, 2004
Messages
2,161
Trophies
1
Age
33
Website
normmatt.com
XP
2,187
Country
New Zealand
Lots of more info, especially about the DATA32 mode. This should allow to get the DATA32_SUPPORT working (at least for reading, I haven't yet tested writing).

First of, selecting DATA32 mode requires setting both 40048D8h.bit1 and 4004900h.bit1. For DATA16 mode, both bits should be probably cleared, but DATA16 seems to be working also when only either of the bits is zero (I haven't found any difference there, it doesn't seem to matter which bit is cleared, or if both are cleared).

Next, in DATA32 mode, one should use the two IRQ flags in 4004900h instead of the RXRDY, TXRQ, DATAEND, CMD_BUSY bits in 400481Ch. Those bits do still exist, but their meaning is somehow changed. For example, DATAEND and CMD_BUSY are getting toggled before reading the last block.

Below are 400481Ch (8 digits) and 4004900h (4 digits) values, logged before sending the command, and before/during/after reading data, showing the differences between DATA32 and DATA16 mode.
When BLK_COUNT=1 (single block):
Code:
  ;data32 mode:                              ;data16 mode:
  ; 20800421 1802                            ; 20800421 1800
  ;---
  ; 20800425 1B02  DATAEND + FLG32's         ; 41800421 1800  RXRDY+CMDBUSY
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1802  bit9 cleared              ; 21800425 1800  DATAEND + CMDrdy
  ;---
  ; 20800421 1802  DATAEND acked             ; 20800421 1800

When BLK_COUNT=2 (two blocks):
Code:
  ;data32 mode:                              ;data16 mode:
  ; 20800421 1802                            ; 20800421 1800
  ;---
  ; 41800421 1B02  RXDRY+FLG32's             ; 41800421 1800  RXRDY+CMDBUSY
  ; 41800421 1B02                            ; 41800421 1800
  ; 41800421 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1B02  bit8 set, DATAEND, NUM=1  ; 41800421 1800  NUM=2(not1)
  ;---
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1B02                            ; 41800421 1800
  ; 20800425 1A02  bit8 cleared              ; 41800421 1800
  ; 20800425 1802  bit9 cleared, NUM=1(not0) ; 21800425 1800  DATAEND + CMDrdy, NUM=2(not0)
  ;---
  ; 20800421 1802  DATAEND acked

The bottom line would be using 4004900h.bit8 instead of RXRDY for reading (and probably also instead of TXRQ for writing; but haven't tested that yet).
And using 4004900h.bit9 instead of DATAEND/CMD_BUSY. Or, you could also combine them: Check DATAEND and CMD_BUSY as usually, but do also ensure 4004900h.bit9=0 before treating the transfer as completed.

Note that 4004900h.bit8/9 are cleared automatically by hardware (unlike RXRDY,TXRQ,DATAEND which must be acknowledged manually).
Another odd effect is that DATA32_BLK_COUNT is decremented during DATA32 transfer, except after the last block: at that point the transfer is completed, but without decreasing the counter from 0001h to 0000h (ie. it stays set to 0001h). Whilst, DATA16_BLK_COUNT isn't decremented (instead DATA16 mode is apparently using an internal counter register, which isn't visible via I/O ports).

And one small detail: 400481Ch.bit29 appears to be just inverse of 400481Ch.bit30, ie.
400481Ch.bit29 = CMD_READY (?)
400481Ch.bit30 = CMD_BUSY
don't know if that rule does always apply, and if the two bits are toggled exactly at the same time.

When looking at 4004900h.bit8/9 versus RXRDY, TXRQ, DATAEND, CMD_BUSY behaviour in DATA32 mode, it does somehow look as if DATA32 mode is transferring the incoming data to a FIFO (and toggles DATAEND/CMD_BUSY when writing to FIFO completed, but before the CPU starts reading the FIFO).
If that's right, then DATA16 mode might work without FIFO, ie. directly reading halfwords from the SD/MMC serial bus as they do arrive (that might work if it pauses the CLK signal when the CPU is reading too slow, or when applying WAITs when the CPU is reading too fast; though could mean really huge WAITs as the sd/mmc clock can be configured to very slow settings with only some kilobits/second.
Checking a hardware timer before/after reading could be used to confirm if there are WAITs occurring. Measuring the CLK signal would be also interesting to see if CLK gets paused between separate halfwords.
Anybody ready to do some scope tests (or donate the loads of spare microSD adaptors that you've hoarded in your stash)?

Hmm yes your right... changing my code to use those flags instead of RX/TX seems to make it work (Atleast reading... haven't tried writting yet). :D

EDIT: Writting just hangs... I'm guessing theres a different flag for that.
 
Last edited by Normmatt,

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Just wondering, did you try using sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); instead of sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); recently?
I really think that it should work (and should be better and more stable).

Or if it doesn't work, then there must be some unidentified stuff going on, such like maybe writing "1" to one of "unused" bits causing to abort transfer and clear all status bits. In that case, setting only the known irq flags bits should solve the problem, ie. sdmmc_write16(REG_SDSTATUS1,8B7Fh AND ~TMIO_STAT1_RXRDY); (and for the lower 16bits, use ANDing 031Dh instead of 8B7Fh accordingly).

NB. the DSi firmware bootcode is using the same unstable-looking read-modify-write method. No idea why Nintendo is doing that, too. It's just looking wrong. Maybe it's "safe" in case of checksum-error bits (if it's done at a location where one can be 100% sure that hardware isn't currently transferring/verifying any checksums). But other stuff like cartridge insert/eject IRQs could occur at any time, so clearing a bit via read-modify-write is definetely unstable.
 

Normmatt

Former AKAIO Programmer
Member
Joined
Dec 14, 2004
Messages
2,161
Trophies
1
Age
33
Website
normmatt.com
XP
2,187
Country
New Zealand
Just wondering, did you try using sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); instead of sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); recently?
I really think that it should work (and should be better and more stable).

Or if it doesn't work, then there must be some unidentified stuff going on, such like maybe writing "1" to one of "unused" bits causing to abort transfer and clear all status bits. In that case, setting only the known irq flags bits should solve the problem, ie. sdmmc_write16(REG_SDSTATUS1,8B7Fh AND ~TMIO_STAT1_RXRDY); (and for the lower 16bits, use ANDing 031Dh instead of 8B7Fh accordingly).

NB. the DSi firmware bootcode is using the same unstable-looking read-modify-write method. No idea why Nintendo is doing that, too. It's just looking wrong. Maybe it's "safe" in case of checksum-error bits (if it's done at a location where one can be 100% sure that hardware isn't currently transferring/verifying any checksums). But other stuff like cartridge insert/eject IRQs could occur at any time, so clearing a bit via read-modify-write is definetely unstable.

Nope haven't tried direct write lately... pretty sure nintendo disables interrupts before doing the read/mod/write... I don't because I don't use interrupts and as far as I know no one else does either on arm9 3ds stuff. I got 32bit fifo working https://gist.github.com/Normmatt/a1df1329888444337dca both read and write appear to be working fine :D
 

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
Great that you got the 32bit xfer working in both directions.

pretty sure nintendo disables interrupts before doing the read/mod/write...
That wouldn't help, they could only disable interrupt execution, but they couldn't disable interrupt requests to occur-and-get-lost during read-modify-write.
At best, they could prevent stuff like vblank interrupts being executed during the unstable code (which would dramatically raise the chance to get lost sd/mmc irqs; during vblank handling).

--------------------- MERGED --------------------------- note to self: please don't work too fast.

Here comes the deluxe test: Combining the DATA32_BLK_COUNT decrease effect with STOP_INTERNAL_ACTION.bit8=0:

The result is that the counter does still decrease from 0002h to 0001h after first block, and still doesn't decrease from 0001h to zero after last block, and, now the stuff different to normal behaviour: CMD_BUSY stays set all the time, and 4004900h.bit8 is getting set again after last block (apparently wanting to transfer further blocks).
 
Last edited by nocash123,

nocash123

Well-Known Member
OP
Member
Joined
Aug 4, 2015
Messages
133
Trophies
0
XP
900
Country
Afghanistan
#ifdef DATA32_SUPPORT
if(!(ctl32 & 0x200))
Ehm... starting the transfer when the IRQ flag is not set? That would be a funny design decision, if it's really supposed to be handled that way.

Logged the IRQ flags during writing in similar fashion as above logs:
Code:
WRITE TWO BLOCKS:
  ;data32 mode:                              ;data16 mode:
  ; 20800421 1802                            ; 20800421 1800
  ;---
  ; 42800421 1802  TXRQ+CMDBUSY              ; 42800421 1800  TXRQ+CMDBUSY
  ; 40800421 1802                            ; 40800421 1800
  ; 40800421 1A02  bit9 set                  ; 40800421 1800
  ; 42800421 1802  bit9 off, TXRQ, NUM=1     ; 42800421 1800  TXRQ, NUM=2(not1)
  ;---
  ; 42800421 1802                            ; 42800421 1800
  ; 40800421 1802                            ; 40800421 1800
  ; 40800421 1A02  bit9 set                  ; 40800421 1800
  ; 20800425 1802  bit9 off, DATAEND, NUM=1  ; 20800425 1800  DATAEND + CMDrdy, NUM=2(not0)
  ;---
  ; 20800421 1802  DATAEND acked
That's really just a plain write-test, I haven't verified if the written data is actually stored on eMMC. Anyways, the status flags are looking right, so the write should have been successful.
As one can see, the IRQ flags in 400481Ch are exactly same in data16 and data32 modes, so all you need to do is handling that flags same as in data16 mode.
The additional IRQ flags in 4004900h aren't of any use. Bit8 doesn't seem to get set at all (or it gets set/cleared for some cycles, at points that haven't be logged in my test). And Bit9 appears to get set after writing the last word of a block, which is useless information, since one should know if one is writing the last word or not anyways.

Hmmmm, maybe Bit9 is indicating that the fifo content is forwarded to hardware, so maybe one should really check 4004900h.bit9=0 before writing new data (additionally to checking TXRQ), or before treating the transfer as completed (additionally to DATAEND, though DATAEND should imply that the fifo content was fully written). Does anybody know if the official firmware code is using 4004900h.Bit9 for sd/mmc writes? Or knows if TXRQ=1 occurs before-or-after 4004900h.bit9=0?
I think 4004900h.bit9 could be useful only for sensing the transfer progress when writing multiple blocks via DMA.

PS. why is now Friday? Last time I checked was Monday, or Tuesday maximum!
 
Last edited by nocash123,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    Xdqwerty @ Xdqwerty: Where's everybody?