module sd_regs ( input i_clk, input i_reset, output reg [1:0] o_sd_clk_config, output reg [5:0] o_command_index, output reg [31:0] o_command_argument, output reg o_command_long_response, output reg o_command_skip_response, input [5:0] i_command_index, input [31:0] i_command_response, output reg o_command_start, input i_command_busy, input i_command_timeout, input i_command_response_crc_error, output reg o_dat_width, output reg o_dat_direction, output reg [6:0] o_dat_block_size, output reg [7:0] o_dat_num_blocks, output reg o_dat_start, output reg o_dat_stop, input i_dat_busy, input i_dat_crc_error, output reg o_rx_fifo_flush, output reg o_rx_fifo_pop, input i_rx_fifo_empty, input i_rx_fifo_full, input i_rx_fifo_overrun, input [8:0] i_rx_fifo_items, input [31:0] i_rx_fifo_data, output reg o_tx_fifo_flush, output o_tx_fifo_push, input i_tx_fifo_empty, input i_tx_fifo_full, input i_tx_fifo_underrun, input [8:0] i_tx_fifo_items, output [31:0] o_tx_fifo_data, output [3:0] o_dma_bank, output [23:0] o_dma_address, output [14:0] o_dma_length, input [3:0] i_dma_bank, input [23:0] i_dma_address, input [14:0] i_dma_left, output o_dma_load_bank_address, output o_dma_load_length, output reg o_dma_direction, output reg o_dma_start, output reg o_dma_stop, input i_dma_busy, input i_request, input i_write, output o_busy, output reg o_ack, input [3:0] i_address, output reg [31:0] o_data, input [31:0] i_data ); localparam [2:0] SD_REG_SCR = 3'd0; localparam [2:0] SD_REG_ARG = 3'd1; localparam [2:0] SD_REG_CMD = 3'd2; localparam [2:0] SD_REG_RSP = 3'd3; localparam [2:0] SD_REG_DAT = 3'd4; localparam [2:0] SD_REG_DMA_SCR = 3'd5; localparam [2:0] SD_REG_DMA_ADDR = 3'd6; localparam [2:0] SD_REG_DMA_LEN = 3'd7; wire w_write_request = i_request && i_write && !o_busy; wire w_read_request = i_request && !i_write && !o_busy; always @(*) begin o_dma_bank = i_data[31:28]; o_dma_address = i_data[25:2]; o_dma_length = i_data[14:0]; o_dma_load_bank_address = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_ADDR); o_dma_load_length = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_LEN); o_tx_fifo_data = i_data; o_tx_fifo_push = w_write_request && i_address[3] && !i_tx_fifo_full && !i_dma_busy; o_busy = 1'b0; end always @(posedge i_clk) begin o_command_start <= 1'b0; o_dat_start <= 1'b0; o_rx_fifo_flush <= 1'b0; o_tx_fifo_flush <= 1'b0; o_dma_start <= 1'b0; o_dat_stop <= 1'b0; o_dma_stop <= 1'b0; if (i_reset) begin o_sd_clk_config <= 2'd0; o_dat_width <= 1'b0; o_dat_direction <= 1'b0; o_dat_block_size <= 7'd0; o_dat_num_blocks <= 8'd0; o_dma_direction <= 1'b0; end else if (w_write_request) begin if (!i_address[3]) begin case (i_address[2:0]) SD_REG_SCR: begin if (!i_command_busy && !i_dat_busy) begin o_sd_clk_config <= i_data[1:0]; end if (!i_dat_busy) begin o_dat_width <= i_data[2]; end end SD_REG_ARG: begin if (!i_command_busy) begin o_command_argument <= i_data; end end SD_REG_CMD: begin if (!i_command_busy) begin { o_command_skip_response, o_command_long_response, o_command_start, o_command_index } <= i_data[8:0]; end end SD_REG_RSP: begin end SD_REG_DAT: begin if (!i_dat_busy || i_data[1]) begin { o_tx_fifo_flush, o_rx_fifo_flush, o_dat_num_blocks, o_dat_block_size, o_dat_direction, o_dat_stop, o_dat_start } <= i_data[19:0]; end end SD_REG_DMA_SCR: begin if (!i_dma_busy || i_data[1]) begin { o_dma_direction, o_dma_stop, o_dma_start } <= i_data[2:0]; end end SD_REG_DMA_ADDR: begin end SD_REG_DMA_LEN: begin end endcase end end end always @(posedge i_clk) begin o_rx_fifo_pop <= 1'b0; o_ack <= 1'b0; if (i_reset) begin o_data <= 32'h0000_0000; end else if (w_read_request) begin o_ack <= 1'b1; if (!i_address[3]) begin case (i_address[2:0]) SD_REG_SCR: begin o_data <= {29'd0, o_dat_width, o_sd_clk_config}; end SD_REG_ARG: begin o_data <= o_command_argument; end SD_REG_CMD: begin o_data <= { 23'd0, i_command_response_crc_error, i_command_timeout, i_command_busy, i_command_index }; end SD_REG_RSP: begin o_data <= i_command_response; end SD_REG_DAT: begin o_data <= { 6'd0, i_tx_fifo_items, i_tx_fifo_full, i_tx_fifo_empty, i_tx_fifo_underrun, i_rx_fifo_items, i_rx_fifo_full, i_rx_fifo_empty, i_rx_fifo_overrun, i_dat_crc_error, i_dat_busy }; end SD_REG_DMA_SCR: begin o_data <= {29'd0, o_dma_direction, 1'b0, i_dma_busy}; end SD_REG_DMA_ADDR: begin o_data <= {i_dma_bank, 2'd0, i_dma_address, 2'b00}; end SD_REG_DMA_LEN: begin o_data <= {17'd0, i_dma_left}; end endcase end else begin if (!i_rx_fifo_empty && !i_dma_busy) begin o_rx_fifo_pop <= 1'b1; end o_data <= i_rx_fifo_data; end end end endmodule