module sd_dat ( input clk, input reset, sd_scb.dat sd_scb, fifo_bus.fifo fifo_bus, input sd_clk_rising, input sd_clk_falling, inout [3:0] sd_dat ); // Input and output data sampling logic sd_dat_oe; logic sd_dat_out; logic [3:0] sd_dat_in; logic sd_dat_oe_data; logic [3:0] sd_dat_data; assign sd_dat = sd_dat_oe ? sd_dat_out : 4'hZ; always_ff @(posedge clk) begin sd_dat_oe <= sd_dat_oe_data; sd_dat_out <= sd_dat_data; sd_dat_in <= sd_dat; end always_ff @(posedge clk) begin sd_scb.card_busy <= !sd_dat_in[0]; end // FIFO logic rx_full; logic rx_almost_full; logic rx_write; logic [7:0] rx_wdata; logic tx_empty; logic tx_almost_empty; logic tx_read; logic [7:0] tx_rdata; fifo_8kb fifo_8kb_rx_inst ( .clk(clk), .reset(reset || sd_scb.dat_fifo_flush), .empty(fifo_bus.rx_empty), .almost_empty(fifo_bus.rx_almost_empty), .read(fifo_bus.rx_read), .rdata(fifo_bus.rx_rdata), .full(rx_full), .almost_full(rx_almost_full), .write(rx_write), .wdata(rx_wdata), .count(sd_scb.rx_count) ); fifo_8kb fifo_8kb_tx_inst ( .clk(clk), .reset(reset || sd_scb.dat_fifo_flush), .empty(tx_empty), .almost_empty(tx_almost_empty), .read(tx_read), .rdata(tx_rdata), .full(fifo_bus.tx_full), .almost_full(fifo_bus.tx_almost_full), .write(fifo_bus.tx_write), .wdata(fifo_bus.tx_wdata), .count(sd_scb.tx_count) ); // DAT state typedef enum bit [1:0] { STATE_IDLE, STATE_RX_WAIT, STATE_RX } e_state; e_state state; e_state next_state; always_ff @(posedge clk) begin if (reset || sd_scb.dat_stop) begin state <= STATE_IDLE; end else begin state <= next_state; end end assign sd_scb.dat_busy = (state != STATE_IDLE); logic [10:0] counter; logic [7:0] blocks_remaining; always_comb begin next_state = state; case (state) STATE_IDLE: begin if (sd_scb.dat_start_read) begin next_state = STATE_RX_WAIT; end if (sd_scb.dat_start_write) begin end end STATE_RX_WAIT: begin if (sd_clk_rising) begin if (!sd_dat_in[0]) begin next_state = STATE_RX; end end end STATE_RX: begin if (sd_clk_rising) begin if (counter == 11'd1041) begin if (blocks_remaining == 8'd0) begin next_state = STATE_IDLE; end else begin next_state = STATE_RX_WAIT; end end end end endcase end // CRC16 units logic crc_reset; logic crc_enable; logic crc_shift; logic [3:0] crc_data; logic [15:0] crc_result [0:3]; sd_crc_16 sd_crc_16_inst_0 ( .clk(clk), .reset(crc_reset), .enable(crc_enable), .shift(crc_shift), .data(crc_data[0]), .result(crc_result[0]) ); sd_crc_16 sd_crc_16_inst_1 ( .clk(clk), .reset(crc_reset), .enable(crc_enable), .shift(crc_shift), .data(crc_data[1]), .result(crc_result[1]) ); sd_crc_16 sd_crc_16_inst_2 ( .clk(clk), .reset(crc_reset), .enable(crc_enable), .shift(crc_shift), .data(crc_data[2]), .result(crc_result[2]) ); sd_crc_16 sd_crc_16_inst_3 ( .clk(clk), .reset(crc_reset), .enable(crc_enable), .shift(crc_shift), .data(crc_data[3]), .result(crc_result[3]) ); // Data shifting assign crc_data = rx_wdata[3:0]; always_ff @(posedge clk) begin rx_write <= 1'b0; crc_reset <= 1'b0; crc_enable <= 1'b0; crc_shift <= 1'b0; if (reset || sd_scb.dat_stop) begin sd_dat_oe_data <= 1'b0; sd_dat_data <= 4'hF; end else begin case (state) STATE_IDLE: begin if (sd_scb.dat_start_read || sd_scb.dat_start_write) begin sd_scb.dat_error <= 1'b0; blocks_remaining <= sd_scb.dat_blocks; end end STATE_RX_WAIT: begin if (sd_clk_rising) begin if (!sd_dat_in[0]) begin counter <= 8'd1; crc_reset <= 1'b1; end end end STATE_RX: begin if (sd_clk_rising) begin counter <= counter + 1'd1; rx_wdata <= {rx_wdata[3:0], sd_dat_in}; if (counter <= 11'd1024) begin crc_enable <= 1'b1; if (!counter[0]) begin if (rx_full) begin sd_scb.dat_error <= 1'b1; end else begin rx_write <= 1'b1; end end end else begin crc_shift <= 1'b1; if ({crc_result[3][15], crc_result[2][15], crc_result[1][15], crc_result[0][15]} != sd_dat_in) begin sd_scb.dat_error <= 1'b1; end end if (counter == 11'd1041) begin blocks_remaining <= blocks_remaining - 1'd1; end end end endcase end end endmodule