
The tt_um_dma_multi_channel is a Direct Memory Access (DMA) controller for Tiny Tapeout. It provides dual independent DMA channels with error detection and programmable transfer widths. This design enables efficient memory-to-memory data transfers on a single chip tile while monitoring for address boundary violations and transfer errors.
The DMA controller transfers data between memory locations under software control. Each transfer operation is initiated by asserting a START signal and specifying source/destination addresses and transfer mode.
Basic Transfer Operation:
Configure Transfer: Set source address, destination address, and mode via ui_in[7:0]
ui_in[7] = START (pulse high to begin)ui_in[6:4] = Source address (3 bits, range 0-7)ui_in[3:1] = Destination address (3 bits, range 0-7)ui_in[0] = Transfer mode (0 = single word, 1 = burst 3 words)Select Channel: Use uio_in[0] to choose between two independent channels (0 or 1)
Execute Transfer:
dma_done pulse when completeMonitor Output:
uo_out[7] = Transfer complete pulse (1 cycle)uo_out[6:0] = Last data transferreduio_out[5:3] = Error flags (if any boundaries violated)Memory Organization:
Each channel has 8 locations × 7 bits:
Example Transfer Sequence:
Cycle 1: Reset asserted (rst_n = LOW)
Cycle 3: rst_n returns HIGH, FSM in IDLE
Cycle 5: Set ui_in = 0x88 (START=1, SRC=0, DST=1, MODE=0)
→ Start single-word transfer from address 0 to 1
Cycle 6: FSM in TRANSFER state
→ mem[1] ← mem[0] (0x61 copied)
→ data_out shows 0x61
Cycle 7: FSM moves to DONE state
→ dma_done pulse goes HIGH
Cycle 8: FSM returns to IDLE
→ dma_done goes LOW
→ Transfer complete!
Dual-Channel Feature:
Both channels operate independently with separate memories and FSMs. Channel selection via uio_in[0] switches which channel's output appears on uo_out. This allows:
Error Detection:
The controller monitors for three error conditions:
If any error detected, transfer skips and dma_done still pulses. Error flags remain valid on uio_out[5:3] until next transfer starts.
Apply clock and reset signals. Set the configuration inputs (ui_in[7:0]) with source address, destination address, and transfer mode. Pulse the START signal (ui_in[7]) high for one cycle to initiate the transfer.
Monitor the output (uo_out[7]) for the done pulse, which indicates the transfer is complete. The transferred data appears on uo_out[6:0]. Check error flags on uio_out[5:3] if boundary violations occur.
Basic test sequence:
Run simulations with Cocotb to verify transfers in different modes (single/burst) and test both channels independently by toggling uio_in[0].
Required Components:
| # | Input | Output | Bidirectional |
|---|---|---|---|
| 0 | cfg[0] - Transfer Mode (0=single, 1=burst) | data[0] - Output Data Bit 0 (LSB) | ch_sel - Channel Select (0=CH0, 1=CH1) |
| 1 | cfg[1] - Destination Address Bit 0 | data[1] - Output Data Bit 1 | width[0] - Word Width Selector Bit 0 |
| 2 | cfg[2] - Destination Address Bit 1 | data[2] - Output Data Bit 2 | width[1] - Word Width Selector Bit 1 |
| 3 | cfg[3] - Destination Address Bit 2 | data[3] - Output Data Bit 3 | err[0] - Source Boundary Error (OUTPUT) |
| 4 | cfg[4] - Source Address Bit 0 | data[4] - Output Data Bit 4 | err[1] - Destination Boundary Error (OUTPUT) |
| 5 | cfg[5] - Source Address Bit 1 | data[5] - Output Data Bit 5 | err[2] - Address Mismatch Error (OUTPUT) |
| 6 | cfg[6] - Source Address Bit 2 | data[6] - Output Data Bit 6 (MSB) | |
| 7 | cfg[7] - Start Signal (pulse high to initiate transfer) | dma_done - Transfer Complete Pulse |