DOOMSoC SD Card Interface¶
Back to project overview¶
The SD card interface is a memory mapped peripheral which consists of N control and status registers and a 512 byte block buffer. The interface consists of logic that facilitates initializing the card and moving data between the card and the block buffer.
The interface operates solely in SPI mode. It initializes the card and the1 n increases the SPI frequency to 50MHz.
Overview¶
The SD card interface logic consists of the following components: * An SPI PHY with programmable bus frequency * An automated block transfer FSM * Read and Write word registers * CSR registers to allow the CPU to interface with the aforementioned components
The SD initialization is performed via software by writing the necessary bytes
Register definitions:¶
0x000 : ctrl \ 0x004 : status \ 0x008 : tx_word \ 0x00C : rx_word \ 0x010 : block_addr \ 0x200 : block data start\ 0x2FF : block data end
ctrl: \ | 1 | 0 | \ | block_write | block_read |
status: \ | 1 | 0 | \ block_rd_in_prog | rx_rdy | tx_rdy |
Memory Map Overview¶
All registers are 32-bit wide and aligned to 4-byte boundaries unless otherwise noted. The block buffer occupies a contiguous 512-byte memory region mapped from 0x200 to 0x3FF.
| Base Offset | Register Name | Width | Access | Reset Value | Description |
|---|---|---|---|---|---|
0x000 |
CTRL |
32 | RW | 0x0 |
Control Register |
0x004 |
STATUS |
32 | RO | 0x0 |
Status Register |
0x008 |
TX_WORD |
8 | WO | 0x0 |
Transmit Single Word |
0x00C |
RX_WORD |
8 | RO | 0x0 |
Receive Single Word |
0x010 |
BLOCK_ADDR |
32 | RW | 0x0 |
External SD card Target Address |
0x200–0x3FF |
BLOCK_DATA |
32 | RW | 0x0 |
Internal Block Transfer Buffer (512 Bytes) |
Register Descriptions¶
CTRL (Control Register)¶
| Bits | Field Name | Access | Reset | Description |
|---|---|---|---|---|
5 |
BLOCK_WRITE |
WO (Strobe) | 0 |
Block Write Trigger: Writing 1 initiates an autonomous 512-byte transmission from BLOCK_DATA to the SD card starting at BLOCK_ADDR. Self-clearing. |
4 |
BLOCK_READ |
WO (Strobe) | 0 |
Block Read Trigger: Writing 1 initiates an autonomous 512-byte fetch from the SD card at BLOCK_ADDR into BLOCK_DATA. Self-clearing. |
3 |
CLR_CS |
WO (Strobe) | 0 |
Clear Chip Select: Writing 1 drives the active-low Chip Select (CS_N) high (deasserts slave select). Self-clearing. |
2 |
SET_CS |
WO (Strobe) | 0 |
Set Chip Select: Writing 1 drives the active-low Chip Select (CS_N) low (asserts slave select). Self-clearing. |
1 |
CLR_INIT_CLK |
WO (Strobe) | 0 |
Clear Init Clock Mode: Writing 1 switches the SPI clock generator back to high-speed operational mode (e.g., normal system transfer clock). Self-clearing. |
0 |
SET_INIT_CLK |
WO (Strobe) | 0 |
Set Init Clock Mode: Writing 1 gates/prescales the SPI clock generator to slow identification speed (100–400 kHz) for SD card initialization sequences. Self-clearing. |
STATUS (Status Register)¶
| Bits | Field Name | Access | Reset | Description |
|---|---|---|---|---|
3 |
BLOCK_WR_BUSY |
RO | 0 |
Block Write Active:0: Idle.1: Block FSM currently shifting out data. |
2 |
BLOCK_RD_BUSY |
RO | 0 |
Block Read Active:0: Idle.1: Block FSM currently shifting in data. |
1 |
RX_RDY |
RO | 0 |
Receive Data Ready:0: RX_WORD empty or transfer in progress.1: Valid word latched into RX_WORD and ready to read. Cleared by reading RX_WORD. |
0 |
TX_RDY |
RO | 0 |
Transmit Buffer Ready:0: Shift register / TX buffer busy.1: Ready to accept the next word written to TX_WORD. |
TX_WORD (Transmit Word Register)¶
| Bits | Field Name | Access | Reset | Description |
|---|---|---|---|---|
7:0 |
TX_DATA |
WO | 0x0 |
Single word to shift onto MOSI. Software must verify STATUS.TX_RDY == 1 before writing. |
RX_WORD (Receive Word Register)¶
| Bits | Field Name | Access | Reset | Description |
|---|---|---|---|---|
7:0 |
RX_DATA |
RO | 0x0 |
Data captured from MISO. Reading this register deasserts STATUS.RX_RDY. |
BLOCK_ADDR (Block Target Address)¶
| Bits | Field Name | Access | Reset | Description |
|---|---|---|---|---|
31:0 |
ADDR |
RW | 0x0 |
Holds the target byte address sent across SPI prior to streaming payload data during a block read or write command. |
BLOCK_DATA (Internal Block RAM Buffer)¶
Internal block data buffer:
* Pre-Write: CPU/DMA fills 0x200–0x3FF with payload data before setting CTRL.BLOCK_WRITE = 1.
* Post-Read: Once STATUS.BLOCK_RD_BUSY transitions from 1 to 0, the received 256 bytes are available in this address range.
SPI PHY interface:¶
Block copy FSM¶
flowchart TD
IDLE["IDLE"]
%% Branch from IDLE
IDLE -->|read_block| SEND_READ_CMD["SEND_READ_CMD<br/>(CMD17)"]
IDLE -->|write_block| SEND_WRITE_CMD["SEND_WRITE_CMD<br/>(CMD24)"]
%% Converge to Address & Checksum
SEND_READ_CMD --> SEND_ADDR["SEND_ADDR<br/>(4 bytes: block_addr)"]
SEND_WRITE_CMD --> SEND_ADDR
SEND_ADDR --> SEND_CHKSUM["SEND_CHKSUM<br/>(Fixed END_BIT = 0x01)"]
SEND_CHKSUM --> WAIT_R1["WAIT_R1<br/>(Poll until bit 7 == 0)"]
%% Fork to Read/Write paths
WAIT_R1 -->|read_active| READ_GAP["READ_GAP<br/>(Wait for 0xFE)"]
WAIT_R1 -->|write_active| WRITE_GAP["WRITE_GAP<br/>(1 dummy byte)"]
%% Read Path
READ_GAP --> READ_BLOCK["READ_BLOCK<br/>(512 bytes)"]
READ_BLOCK --> READ_CRC["READ_CRC<br/>(2 dummy bytes)"]
READ_CRC --> IDLE
%% Write Path
WRITE_GAP --> WRITE_START["WRITE_START<br/>(Send 0xFE)"]
WRITE_START --> WRITE_BLOCK["WRITE_BLOCK<br/>(512 bytes)"]
WRITE_BLOCK --> WRITE_CRC["WRITE_CRC<br/>(2 dummy bytes)"]
WRITE_CRC --> WAIT_WR_RESP["WAIT_WR_RESP<br/>(Wait data response token)"]
WAIT_WR_RESP --> WAIT_WR_COMMIT["WAIT_WR_COMMIT<br/>(Wait busy MISO high)"]
WAIT_WR_COMMIT --> IDLE