///////////////////////////////////////////
// sd_top.sv
//
// Written: Richard Davis
// Modified: Ross Thompson September 19, 2021
//
// Purpose: SD card controller
// 
// A component of the Wally configurable RISC-V project.
// 
// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University
//
// MIT LICENSE
// Permission is hereby granted, free of charge, to any person obtaining a copy of this 
// software and associated documentation files (the "Software"), to deal in the Software 
// without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 
// to whom the Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in all copies or 
//   substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
//   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
//   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 
//   OR OTHER DEALINGS IN THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////////////////////

`include "wally-config.vh"

module sd_top #(parameter g_COUNT_WIDTH = 8)
  (
   input logic 			   CLK, // 1.2 GHz (1.0 GHz typical)
   input logic a_RST, // Reset signal (Must be held for minimum of 24 clock cycles)
   // a_RST MUST COME OUT OF RESET SYNCHRONIZED TO THE 1.2 GHZ CLOCK!
   // io_SD_CMD_z    : inout std_logic;   // SD CMD Bus
   (* mark_debug = "true" *)input logic i_SD_CMD, // CMD Response from card
   (* mark_debug = "true" *)output logic o_SD_CMD, // CMD Command from host
   (* mark_debug = "true" *)output logic o_SD_CMD_OE, // Direction of SD_CMD
   (* mark_debug = "true" *)input logic [3:0] i_SD_DAT, // SD DAT Bus
   (* mark_debug = "true" *)output logic o_SD_CLK, // SD CLK Bus
   // For communication with core cpu
   input logic [32:9] 		   i_BLOCK_ADDR, // see "Addressing" in parts.fods (only 8GB total capacity is used)
   output logic 		   o_READY_FOR_READ, // tells core that initialization sequence is completed and
   // sd card is ready to read a 512 byte block to the core.
   // Held high during idle until i_READ_REQUEST is received
   output logic 		   o_SD_RESTARTING, // inform core the need to restart
  
   input logic 			   i_READ_REQUEST, // After Ready for read is sent to the core, the core will
   // pulse this bit high to indicate it wants the block at this address
   output logic [3:0] 		   o_DATA_TO_CORE, // nibble being sent to core when DATA block is
   output logic [4095:0] 	   ReadData, // full 512 bytes to Bus
   // being published
   output logic 		   o_DATA_VALID, // held high while data being read to core to indicate that it is valid
   output logic 		   o_LAST_NIBBLE, // pulse when last nibble is sent
   output logic [2:0] 		   o_ERROR_CODE_Q, // indicates which error occured
   output logic 		   o_FATAL_ERROR, // indicates that the FATAL ERROR register has updated
   // For tuning
   input logic [g_COUNT_WIDTH-1:0] i_COUNT_IN_MAX,
   input logic 			   LIMIT_SD_TIMERS
   );

  localparam logic 		   c_CMD = 1'b0;
  localparam logic 		   c_ACMD = 1'b1;

  // packet bit names
  localparam logic 		   c_start_bit = 1'b0;  // bit 47
  localparam logic 		   c_stop_bit = 1'b1;  // bit 0, AKA "end bit"
  // transmitter bit, bit 46
  localparam logic 		   c_tx_host_command = 1'b1;
  localparam logic 		   c_tx_card_response = 1'b0;

  // response types
  localparam logic [2:0] 	   c_response_type_R0_NONE = 3'd0;
  localparam logic [2:0] 	   c_response_type_R1_NORMAL = 3'd1;
  localparam logic [2:0] 	   c_response_type_R2_CID_CSD = 3'd2;
  localparam logic [2:0] 	   c_response_type_R3_OCR = 3'd3;
  localparam logic [2:0] 	   c_response_type_R6_RCA = 3'd6;
  localparam logic [2:0] 	   c_response_type_R7_CIC = 3'd7;

  // uses dat
  localparam logic [1:0] 	   c_DAT_none = 2'b00;
  localparam logic [1:0] 	   c_DAT_busy = 2'b01;
  localparam logic [1:0] 	   c_DAT_wide = 2'b10;
  localparam logic [1:0] 	   c_DAT_block = 2'b11;

  // tx source selection
  localparam logic [1:0] 	   c_tx_low =   2'b00; 
  localparam logic [1:0] 	   c_tx_high =  2'b01; 
  localparam logic [1:0] 	   c_tx_head =  2'b10; 
  localparam logic [1:0] 	   c_tx_tail   = 2'b11;

  // command indexes
  localparam logic [45:40] 	   c_Go_Idle_State = 6'd00;  //  CMD0
  localparam logic [45:40] 	   c_All_Send_CID = 6'd02;  //  CMD2
  localparam logic [45:40] 	   c_SD_Send_RCA = 6'd03;  //  CMD3
  localparam logic [45:40] 	   c_Switch_Function = 6'd06;  //  CMD6
  localparam logic [45:40] 	   c_Set_Bus_Width = 6'd06;  // ACMD6
  localparam logic [45:40] 	   c_Select_Card = 6'd07;  //  CMD7
  localparam logic [45:40] 	   c_Send_IF_State = 6'd08;  //  CMD8
  localparam logic [45:40] 	   c_Read_Single_Block = 6'd17;  //  CMD17
  localparam logic [45:40] 	   c_SD_Send_OCR = 6'd41;  // ACMD41
  localparam logic [45:40] 	   c_App_Command = 6'd55;  //  CMD55

  // bitmasks
  localparam logic [127:96] 	   c_CMD0_mask_check_redo_bits = 32'h00000000;  // Go_Idle_State
  localparam logic [127:96] 	   c_CMD0_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD0_mask_check_error_bits = 32'h00000000;
  localparam logic [127:96] 	   c_CMD0_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_CMD2_mask_check_redo_bits = 32'h00000000;  // All_Send_CID
  localparam logic [127:96] 	   c_CMD2_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD2_mask_check_error_bits = 32'h00000000;
  localparam logic [127:96] 	   c_CMD2_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_CMD3_mask_check_redo_bits = 32'h00000000;  // SD_Send_RCA
  localparam logic [127:96] 	   c_CMD3_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD3_mask_check_error_bits = 32'h00002000;
  localparam logic [127:96] 	   c_CMD3_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_CMD6_mask_check_redo_bits = 32'h00000000;  // Switch_Function
  localparam logic [127:96] 	   c_CMD6_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD6_mask_check_error_bits = 32'h82380000;
  localparam logic [127:96] 	   c_CMD6_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_ACMD6_mask_check_redo_bits = 32'h00000000;  // Set_Bus_Width
  localparam logic [127:96] 	   c_ACMD6_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_ACMD6_mask_check_error_bits = 32'h8F398020;
  localparam logic [127:96] 	   c_ACMD6_ans_error_free = 32'h00000020;

  localparam logic [127:96] 	   c_CMD7_mask_check_redo_bits = 32'h00000000;  // Select_Card
  localparam logic [127:96] 	   c_CMD7_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD7_mask_check_error_bits = 32'h0F398000;
  localparam logic [127:96] 	   c_CMD7_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_CMD8_mask_check_redo_bits = 32'h00000000;  //  Send_IF_State
  localparam logic [127:96] 	   c_CMD8_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD8_mask_check_error_bits = 32'h00000FFF;
  localparam logic [127:96] 	   c_CMD8_ans_error_free = 32'h000001FF;

  localparam logic [127:96] 	   c_CMD17_mask_check_redo_bits = 32'h00000000;  // Read_Single_Block
  localparam logic [127:96] 	   c_CMD17_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD17_mask_check_error_bits = 32'hCF398000;
  localparam logic [127:96] 	   c_CMD17_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_ACMD41_mask_check_redo_bits = 32'h80000000; //32'h80000000;  // SD_Send_OCR
  localparam logic [127:96] 	   c_ACMD41_ans_dont_redo = 32'h80000000; //32'h80000000;
  localparam logic [127:96] 	   c_ACMD41_mask_check_error_bits = 32'h41FF8000; // 32'h41FF8000;
  localparam logic [127:96] 	   c_ACMD41_ans_error_free = 32'h40FF8000; // 32'h40FF8000

  localparam logic [127:96] 	   c_CMD55_mask_check_redo_bits = 32'h00000000;  // App_Command
  localparam logic [127:96] 	   c_CMD55_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_CMD55_mask_check_error_bits = 32'h0F398000;
  localparam logic [127:96] 	   c_CMD55_ans_error_free = 32'h00000000;

  localparam logic [127:96] 	   c_ACMD55_mask_check_redo_bits = 32'h00000000;  // App_Command
  localparam logic [127:96] 	   c_ACMD55_ans_dont_redo = 32'h00000000;
  localparam logic [127:96] 	   c_ACMD55_mask_check_error_bits = 32'h0F398000;
  localparam logic [127:96] 	   c_ACMD55_ans_error_free = 32'h00000000;

  // SD_CMD_FSM Connections
  logic 			   w_TIMER_LOAD, w_TIMER_EN;
  logic [18:0] 			   w_TIMER_IN;
  logic [18:0] 			   r_TIMER_OUT;
  logic 			   w_COUNTER_LOAD, w_COUNTER_EN;
  logic [7:0] 			   w_COUNTER_IN;
  logic [7:0] 			   r_COUNTER_OUT;
  logic 			   w_SD_CLK_EN;
  logic 			   w_CLOCK_CHANGE_DONE, w_START_CLOCK_CHANGE;  // to clk fsm
  logic 			   w_HS_TO_INIT_CLK_DIVIDER_RST;
  (* mark_debug = "true" *)logic 			   w_IC_RST, w_IC_EN, w_IC_UP_DOWN;
  (* mark_debug = "true" *)logic 			   w_SD_CMD_OE;
  logic 			   w_TX_PISO40_LOAD, w_TX_PISO40_EN;
  logic 			   w_TX_PISO8_LOAD, w_TX_PISO8_EN;
  logic 			   w_TX_CRC7_PIPO_RST, w_TX_CRC7_PIPO_EN;
  logic [1:0] 			   w_TX_SOURCE_SELECT;
  logic 			   w_CMD_TX_IS_CMD55_RST;
  logic 			   w_CMD_TX_IS_CMD55_EN;
  //logic w_CMD_RX;
  logic 			   w_RX_SIPO48_RST, w_RX_SIPO48_EN;
  (* mark_debug = "true" *)logic [39:8] 			   r_RESPONSE_CONTENT;
  (* mark_debug = "true" *)logic [45:40] 		   r_RESPONSE_INDEX;
  logic 			   w_RX_CRC7_SIPO_RST, w_RX_CRC7_SIPO_EN;
  logic [6:0] 			   r_RX_CRC7_Q;
  logic 			   w_RCA_REGISTER_RST, w_RCA_REGISTER_EN;
  logic 			   w_CMD_TX_DONE;
  logic 			   w_DAT_RX_DONE;
  logic 			   w_DAT_ERROR_FD_RST_DAT, w_DAT_ERROR_FD_RST_CMD, w_DAT_ERROR_FD_RST, w_DAT_ERROR_FD_EN;
  (* mark_debug = "true" *)logic 			   r_DAT_ERROR_Q;     // CRC16 error or time out
  (* mark_debug = "true" *)logic 			   w_NOT_DAT_ERROR_Q;  // '0'=no error, '1'=tx error on DAT bus
  (* mark_debug = "true" *)logic 			   w_ERROR_DAT_TIMES_OUT;
  (* mark_debug = "true" *)logic 			   w_FATAL_ERROR;
  (* mark_debug = "true" *)logic [2:0] 			   r_ERROR_CODE_Q;  // indicates which fatal error occured

  // Communication with core
  (* mark_debug = "true" *)logic 			   w_READY_FOR_READ;
  (* mark_debug = "true" *)logic 			   w_READ_REQUEST;
  (* mark_debug = "true" *)logic [3:0] 			   r_DATA_TO_CORE;
  (* mark_debug = "true" *)logic 			   w_DATA_VALID;
  (* mark_debug = "true" *)logic 			   w_LAST_NIBBLE;

  //SD_DAT_FSM Connections
  logic 			   w_DAT_TIMER_LOAD, w_DAT_TIMER_EN;
  logic 			   w_DAT_COUNTER_RST, w_DAT_COUNTER_EN;
  logic 			   w_CRC16_EN, w_CRC16_RST;
  logic 			   w_BUSY_RST, w_BUSY_EN;
  logic 			   w_NIBO_EN;
  logic 			   w_DATA_CRC16_GOOD;
  logic 			   w_VALID_BLOCK_D, w_VALID_BLOCK_EN, w_VALID_WIDE_D, w_VALID_WIDE_EN;
  logic [22:0] 			   w_DAT_TIMER_IN;
  logic [22:0] 			   r_DAT_TIMER_OUT;
  logic [10:0] 			   r_DAT_COUNTER_OUT;
  (* mark_debug = "true" *)logic [3:0] 			   r_DAT_Q;

  // RCA Register
  logic [15:0] 			   w_RCA_D_Q;
  logic [15:0] 			   r_RCA_Q2;

  // Multiplexer Logics
  logic [132:0] 		   w_instruction_control_bits;
  logic [132:130] 		   w_R_TYPE                  ;
  logic [129:128] 		   w_USES_DAT                ;
  logic [127:96] 		   w_NO_REDO_MASK            ;
  logic [95:64] 		   w_NO_REDO_ANS             ;
  logic [63:32] 		   w_NO_ERROR_MASK           ;
  logic [31:0] 			   w_NO_ERROR_ANS            ;
  logic [45:40] 		   w_command_index           ;
  logic [39:8] 			   w_command_arguments       ;
  logic [47:8] 			   w_command_head            ;
  (* mark_debug = "true" *)logic [6:0] 			   w_OPCODE_Q                ;

  // TOP_LEVEL Connections
  logic [40:9] 			   w_BLOCK_ADDR                     ;
  (* mark_debug = "true" *)logic [3:0] 			   r_IC_OUT                         ;
  logic [2:0] 			   r_command_index_is_55_history    ;  // [0] is live index, [1] is currently saved index, [2] is index of previous command
  logic 			   r_previous_command_index_was_55_q;  // is index of previous command 55, wired to r_command_index_is_55_history[2]
  logic 			   r_ACMD_Q;  // if the previous command sent to the SD card successfully had index 55, then the SD card thinks the current command is ACMD
  logic [4095:0] 		   r_block_data                     ;  // data block from CMD17

  // TX
  logic [45:8] 			   w_command_content;                  // first 40 bits of command packet
  logic 			   w_tx_head_Q;            // transmission of first part of command packet
  logic 			   w_tx_tail_Q;            // transmission of last part of command packet
  logic [7:0] 			   r_command_tail;  // last 8 bits of command packet
  logic [6:0] 			   r_TX_CRC7;
  //logic w_TX_Q:= '0';  // actual transmission when tx is enabled

  // RX
  logic [47:0] 			   r_RX_RESPONSE;

  // Tri state IO Driver BC18MIMS
  logic 			   w_SD_CMD_TX_Q;  // Write Data
   logic 			   w_SD_CMD_RX;         // Read Data


  // CLOCKS
  //logic r_CLK_HS := '0';  // 50 MHz Divided Clock [static]
  //logic r_SD_CLK_ungated := '0';  // Selected clock before it is clock gated

  //logic r_SD_CLK := '0';  // GATED CLOCKS
  logic 			   r_TO_SD_CLK;  // What is actually sent to the SD card

  logic 			   w_G_CLK_SD_EN;
  logic 			   r_CLK_SD, r_G_CLK_SD;              // clocks
  logic 			   r_G_CLK_SD_n;
  logic [15:0] 			   r_CLK_FSM_RST                 ;  // a_rst logic delayed by one 1.2 GHz period
  logic 			   w_SD_CLK_SELECTED;

  //DAT FSM Connections
  logic [15:0] 			   r_DAT3_CRC16, r_DAT2_CRC16, r_DAT1_CRC16;
  logic [15:0] 			   r_DAT0_CRC16;
  
  assign w_BLOCK_ADDR = {8'h00, i_BLOCK_ADDR};  // (40 downto 36 are zero since card is 64 GB)
                                       // (35 downto 32 are zero since memeory is only 8GB total)

  assign o_READY_FOR_READ = w_READY_FOR_READ;
  assign w_READ_REQUEST   = i_READ_REQUEST;
  assign o_DATA_TO_CORE   = r_DATA_TO_CORE;
  assign o_DATA_VALID     = w_DATA_VALID;
  assign o_LAST_NIBBLE    = (w_LAST_NIBBLE | w_FATAL_ERROR);  // indicate done if (last nibble OR Fatal Error go high)
  assign o_FATAL_ERROR    = w_FATAL_ERROR;

    sd_cmd_fsm my_sd_cmd_fsm
    (
      .CLK(r_G_CLK_SD),
      .i_RST(a_RST),
      .o_TIMER_LOAD(w_TIMER_LOAD),
      .o_TIMER_EN(w_TIMER_EN),
      .o_TIMER_IN(w_TIMER_IN),
      .i_TIMER_OUT(r_TIMER_OUT),
      .o_COUNTER_LOAD(w_COUNTER_LOAD),
      .o_COUNTER_EN(w_COUNTER_EN),
      .o_COUNTER_IN(w_COUNTER_IN),
      .i_COUNTER_OUT(r_COUNTER_OUT),
      .o_SD_CLK_EN(w_SD_CLK_EN),
      .i_CLOCK_CHANGE_DONE(w_CLOCK_CHANGE_DONE),
      .o_START_CLOCK_CHANGE(w_START_CLOCK_CHANGE),
      .o_IC_RST(w_IC_RST),
      .o_IC_EN(w_IC_EN),
      .o_IC_UP_DOWN(w_IC_UP_DOWN),
      .i_IC_OUT(r_IC_OUT),
      .i_USES_DAT(w_USES_DAT),
      .i_OPCODE(w_OPCODE_Q),
      .i_R_TYPE(w_R_TYPE),
      .i_NO_REDO_MASK(w_NO_REDO_MASK),
      .i_NO_REDO_ANS(w_NO_REDO_ANS),
      .i_NO_ERROR_MASK(w_NO_ERROR_MASK),
      .i_NO_ERROR_ANS(w_NO_ERROR_ANS),
      .o_SD_CMD_OE(w_SD_CMD_OE),
      .o_TX_PISO40_LOAD(w_TX_PISO40_LOAD),
      .o_TX_PISO40_EN(w_TX_PISO40_EN),
      .o_TX_PISO8_LOAD(w_TX_PISO8_LOAD),
      .o_TX_PISO8_EN(w_TX_PISO8_EN),
      .o_TX_CRC7_PIPO_RST(w_TX_CRC7_PIPO_RST),
      .o_TX_CRC7_PIPO_EN(w_TX_CRC7_PIPO_EN),
      .o_TX_SOURCE_SELECT(w_TX_SOURCE_SELECT),
      .o_CMD_TX_IS_CMD55_RST(w_CMD_TX_IS_CMD55_RST),
      .o_CMD_TX_IS_CMD55_EN(w_CMD_TX_IS_CMD55_EN),
      .i_SD_CMD_RX(w_SD_CMD_RX),
      .o_RX_SIPO48_RST(w_RX_SIPO48_RST),
      .o_RX_SIPO48_EN(w_RX_SIPO48_EN),
      .i_RESPONSE_CONTENT(r_RESPONSE_CONTENT),
      .i_RESPONSE_INDEX(r_RESPONSE_INDEX),
      .o_RX_CRC7_SIPO_RST(w_RX_CRC7_SIPO_RST),
      .o_RX_CRC7_SIPO_EN(w_RX_CRC7_SIPO_EN),
      .i_RX_CRC7(r_RX_CRC7_Q),
      .o_RCA_REGISTER_RST(w_RCA_REGISTER_RST),
      .o_RCA_REGISTER_EN(w_RCA_REGISTER_EN),
      .o_CMD_TX_DONE(w_CMD_TX_DONE),
      .i_DAT_RX_DONE(w_DAT_RX_DONE),
      .i_ERROR_CRC16(w_NOT_DAT_ERROR_Q),
      .i_ERROR_DAT_TIMES_OUT(w_ERROR_DAT_TIMES_OUT),
      .i_READ_REQUEST(w_READ_REQUEST),
      .o_READY_FOR_READ(w_READY_FOR_READ),
      .o_SD_RESTARTING(o_SD_RESTARTING),
      .o_DAT_ERROR_FD_RST(w_DAT_ERROR_FD_RST_CMD),
      .o_ERROR_CODE_Q(r_ERROR_CODE_Q),
      .o_FATAL_ERROR(w_FATAL_ERROR),
      .LIMIT_SD_TIMERS(LIMIT_SD_TIMERS));

  assign o_ERROR_CODE_Q = r_ERROR_CODE_Q;
  
  sd_dat_fsm my_sd_dat_fsm
    (.CLK(r_G_CLK_SD),
    .i_RST(a_RST),
    .o_TIMER_LOAD(w_DAT_TIMER_LOAD),
    .o_TIMER_EN(w_DAT_TIMER_EN),
    .o_TIMER_IN(w_DAT_TIMER_IN),
    .i_TIMER_OUT(r_DAT_TIMER_OUT),
    .i_SD_CLK_SELECTED(w_SD_CLK_SELECTED),
    .o_COUNTER_RST(w_DAT_COUNTER_RST),
    .o_COUNTER_EN(w_DAT_COUNTER_EN),
    .i_COUNTER_OUT(r_DAT_COUNTER_OUT),
    .o_CRC16_EN(w_CRC16_EN),
    .o_CRC16_RST(w_CRC16_RST),
    .i_DATA_CRC16_GOOD(w_DATA_CRC16_GOOD),
    .o_BUSY_RST(w_BUSY_RST),
    .o_BUSY_EN(w_BUSY_EN),
    .i_DAT0_Q(r_DAT_Q[0]),
    .o_NIBO_EN(w_NIBO_EN),
    .i_USES_DAT(w_USES_DAT),
    .i_CMD_TX_DONE(w_CMD_TX_DONE),
    .o_DAT_RX_DONE(w_DAT_RX_DONE),
    .o_ERROR_DAT_TIMES_OUT(w_ERROR_DAT_TIMES_OUT),
    .o_DATA_VALID(w_DATA_VALID),
    .o_LAST_NIBBLE(w_LAST_NIBBLE),
    .o_DAT_ERROR_FD_RST(w_DAT_ERROR_FD_RST_DAT),
    .o_DAT_ERROR_FD_EN(w_DAT_ERROR_FD_EN),
    .LIMIT_SD_TIMERS(LIMIT_SD_TIMERS));

  assign w_DAT_ERROR_FD_RST = w_DAT_ERROR_FD_RST_CMD | w_DAT_ERROR_FD_RST_DAT;

  flopenr #(1) dat_error_fd
    (.clk(r_G_CLK_SD),
    .d(w_DATA_CRC16_GOOD),
    .q(r_DAT_ERROR_Q),
    .en(w_DAT_ERROR_FD_EN),
    .reset((w_DAT_ERROR_FD_RST)));
  
  assign w_NOT_DAT_ERROR_Q = ~r_DAT_ERROR_Q;

  up_down_counter #(23) dat_fsm_timer
    (
    .CountIn(w_DAT_TIMER_IN),
    .CountOut(r_DAT_TIMER_OUT),
    .Load(w_DAT_TIMER_LOAD),
    .Enable(w_DAT_TIMER_EN),
    .UpDown(1'b0),                 // Count DOWN only
    .clk(r_G_CLK_SD),
    .reset(1'b0));                // No Reset, Just Load
  
  SDCcounter #(11) dat_nibble_counter
    (
    .CountIn('0),
    .CountOut(r_DAT_COUNTER_OUT),
    .Load(1'b0),
    .Enable(w_DAT_COUNTER_EN),
    .clk(r_G_CLK_SD),
    .reset(w_DAT_COUNTER_RST));

  regfile_p2r1w1_nibo #(.DEPTH(10), .WIDTH(4) ) regfile_cmd17_data_block  // Nibble In - Nibble Out (NINO)
    (.clk(r_G_CLK_SD),
    .we1(w_NIBO_EN),
    .ra1(r_DAT_COUNTER_OUT[9:0]),       // Nibble Read (to core) Address
    .rd1(r_DATA_TO_CORE),                    // output nibble to core
     .Rd1All(ReadData),
    .wa1(r_DAT_COUNTER_OUT[9:0]),     // Nibble Write (to host) Address
    .wd1(r_DAT_Q));                          // input nibble from card
  
  crc16_sipo_np_ce crc16_sipo_np_ce_DAT3
    (.CLK(r_G_CLK_SD),
    .RST(w_CRC16_RST),
    .i_enable(w_CRC16_EN),
    .i_message_bit(r_DAT_Q[3]),
    .o_crc16(r_DAT3_CRC16));

  crc16_sipo_np_ce crc16_sipo_np_ce_DAT2
    (.CLK(r_G_CLK_SD),
    .RST(w_CRC16_RST),
    .i_enable(w_CRC16_EN),
    .i_message_bit(r_DAT_Q[2]),
    .o_crc16(r_DAT2_CRC16));

  crc16_sipo_np_ce crc16_sipo_np_ce_DAT1
    (.CLK(r_G_CLK_SD),
    .RST(w_CRC16_RST),
    .i_enable(w_CRC16_EN),
    .i_message_bit(r_DAT_Q[1]),
    .o_crc16(r_DAT1_CRC16));

  crc16_sipo_np_ce crc16_sipo_np_ce_DAT0
    (.CLK(r_G_CLK_SD),
    .RST(w_CRC16_RST),
    .i_enable(w_CRC16_EN),
    .i_message_bit(r_DAT_Q[0]),
    .o_crc16(r_DAT0_CRC16));


  assign w_DATA_CRC16_GOOD = ({r_DAT3_CRC16, r_DAT2_CRC16, r_DAT1_CRC16, r_DAT0_CRC16}) == 64'h0000000000000000;

  flopenr #(4) busy_bit_fd
    (.en(w_BUSY_EN),
    .clk(r_G_CLK_SD),
    .d(i_SD_DAT),
    .q(r_DAT_Q),
    .reset(w_BUSY_RST));

  sd_clk_fsm my_clk_fsm
    (.CLK(CLK),
    .i_RST(a_RST),
    .o_DONE(w_CLOCK_CHANGE_DONE),
    .i_START(w_START_CLOCK_CHANGE),
    .o_HS_TO_INIT_CLK_DIVIDER_RST(w_HS_TO_INIT_CLK_DIVIDER_RST),
    .o_SD_CLK_SELECTED(w_SD_CLK_SELECTED),
    .i_FATAL_ERROR(w_FATAL_ERROR),
    .o_G_CLK_SD_EN(w_G_CLK_SD_EN));
  
    up_down_counter #(19) cmd_fsm_timer
    (.CountIn(w_TIMER_IN),
      .CountOut(r_TIMER_OUT),
      .Load(w_TIMER_LOAD),
      .Enable(w_TIMER_EN),
      .UpDown(1'b0),                 // Count DOWN only
      .clk(r_G_CLK_SD),
      .reset(1'b0));                // No Reset, Just Load

  up_down_counter #(8) cmd_fsm_counter
    (.CountIn(w_COUNTER_IN),
    .CountOut(r_COUNTER_OUT),
    .Load(w_COUNTER_LOAD),
    .Enable(w_COUNTER_EN),
    .UpDown(1'b0),                 // Count DOWN only
    .clk(r_G_CLK_SD),
    .reset(1'b0));                // No RESET, only LOAD

  up_down_counter #(4) instruction_counter
    (.CountIn('0),     // No CountIn, only RESET
    .CountOut(r_IC_OUT),
    .Load(1'b0),                // No LOAD, only RESET
    .Enable(w_IC_EN),
    .UpDown(w_IC_UP_DOWN),
    .clk(r_G_CLK_SD),
    .reset(w_IC_RST | a_RST));

  // Clock selection
  clkdivider #(g_COUNT_WIDTH) slow_clk_divider        // Divide 50 MHz to <400 KHz (Initial clock)
    (.i_COUNT_IN_MAX(i_COUNT_IN_MAX),
     .i_EN(w_SD_CLK_SELECTED),
     //.i_EN(1'b1),
     //.i_RST(w_HS_TO_INIT_CLK_DIVIDER_RST),
     .i_RST(a_RST),
     .i_CLK(CLK),
     .o_CLK(r_CLK_SD));

  clockgater sd_clk_gater              // Select which clock goes to components
    (.CLK(r_CLK_SD),
    .E(w_G_CLK_SD_EN | a_RST),
    .SE(1'b0),
    .ECLK(r_G_CLK_SD));

  clockgater to_sd_clk_gater           // Enable activity on the SD_CLK line
    (.CLK(r_G_CLK_SD),
    .E(w_SD_CLK_EN),
    .SE(1'b0),
    .ECLK(r_TO_SD_CLK));
  
  flopenr #(16) RCA_register_CE
    (.clk(r_G_CLK_SD),
    .en(w_RCA_REGISTER_EN),
    .d(w_RCA_D_Q),
    .q(r_RCA_Q2),
    .reset(w_RCA_REGISTER_RST));

  // ACMD_Detector
  flopenr #(1) index_history_fd_2to1
    (.clk(r_G_CLK_SD),
    .reset(w_CMD_TX_IS_CMD55_RST),
    .en(w_CMD_TX_IS_CMD55_EN),
    .d(r_command_index_is_55_history[2]),
    .q(r_command_index_is_55_history[1]));

  flopenr #(1) index_history_fd_1to0
    (.clk(r_G_CLK_SD),
    .reset(w_CMD_TX_IS_CMD55_RST),
    .en(w_CMD_TX_IS_CMD55_EN),
    .d(r_command_index_is_55_history[1]),
    .q(r_command_index_is_55_history[0]));



  assign r_command_index_is_55_history[2] = (w_command_index == 55);
  

  assign r_previous_command_index_was_55_q = r_command_index_is_55_history[0];
  assign r_ACMD_Q                          = r_previous_command_index_was_55_q;  // if the previous command WAS 55, the current command is ACMD

  assign o_SD_CLK = r_TO_SD_CLK;



  // Multiplexers
  //Fetch index and argument of command
  assign w_command_content = (r_IC_OUT == 0) ? ({c_Go_Idle_State, 32'h00000000}) :  // CMD0
                             (r_IC_OUT == 1) ? ({c_Send_IF_State, 32'h000001FF}) :   // CMD8
			     (r_IC_OUT == 2) ? ({c_App_Command, 32'h00000000}) :  // CMD55
                             (r_IC_OUT == 3) ? ({c_SD_Send_OCR, 32'h40FF8000}) :  // ACMD41
			     (r_IC_OUT == 4) ? ({c_All_Send_CID, 32'h00000000}) :  // CMD2
			     (r_IC_OUT == 5) ? ({c_SD_Send_RCA, 32'h00000000}) :  // CMD3
			     (r_IC_OUT == 6) ? ({c_Select_Card, r_RCA_Q2[15:0], 16'h0000}) :  // CMD7
			     (r_IC_OUT == 7) ? ({c_App_Command, r_RCA_Q2[15:0], 16'h0000}) :  // CMD55
			     (r_IC_OUT == 8) ? ({c_Set_Bus_Width, 32'h00000002}) :  // ACMD6
			     (r_IC_OUT == 9) ? ({c_Switch_Function, 32'h80FFFFF1}) :  // CMD6
			     (r_IC_OUT == 10) ? ({c_Read_Single_Block, w_BLOCK_ADDR}) :  // CMD17
			     ({c_Read_Single_Block, w_BLOCK_ADDR});  // when in doubt just send CMD17

  assign w_command_index     = w_command_content[45:40];
  assign w_command_arguments = w_command_content[39:8];
  assign w_command_head      = {c_start_bit, c_tx_host_command, w_command_content};

  assign w_OPCODE_Q = {r_ACMD_Q, w_command_index};

  // TX
  
  crc7_pipo tx_crc7_pipo 
    (.CLK(r_G_CLK_SD),
    .i_DATA(w_command_head),
    .i_CRC_ENABLE(w_TX_CRC7_PIPO_EN),
    .RST(w_TX_CRC7_PIPO_RST),
    .o_CRC(r_TX_CRC7));

  assign r_command_tail = {r_TX_CRC7, c_stop_bit};

  piso_generic_ce #(40) tx_piso40_command_head
   (.clk(r_G_CLK_SD),
    .i_load(w_TX_PISO40_LOAD),
    .i_data(w_command_head),
    .i_en(w_TX_PISO40_EN),
    .o_data(w_tx_head_Q));

  piso_generic_ce #(8) tx_piso8_command_tail
    (.clk(r_G_CLK_SD),
    .i_load(w_TX_PISO8_LOAD),
    .i_data(r_command_tail),
    .i_en(w_TX_PISO8_EN),
    .o_data(w_tx_tail_Q));
  
  assign w_SD_CMD_TX_Q = (w_TX_SOURCE_SELECT == c_tx_low) ? 1'b0 :
                   (w_TX_SOURCE_SELECT == c_tx_high) ? 1'b1 :
                   (w_TX_SOURCE_SELECT == c_tx_head) ? w_tx_head_Q : 
                   (w_TX_SOURCE_SELECT == c_tx_tail) ? w_tx_tail_Q :
                   1'b0;

  assign w_SD_CMD_RX = i_SD_CMD;

  flopenr #(1) sd_cmd_out_reg
    (.d(w_SD_CMD_TX_Q),
    .q(o_SD_CMD),
    .en(1'b1),
    .clk(~r_G_CLK_SD),
    .reset(a_RST));
  
  flopenr #(1) sd_cmd_out_oe_reg
    (.d(w_SD_CMD_OE),
    .q(o_SD_CMD_OE),
    .en(1'b1),
    .clk(~r_G_CLK_SD),
    .reset(a_RST));
  
  // RX
  sipo_generic_ce #(48) rx_sipo48_response_content
    (.clk(r_G_CLK_SD),
    .rst(w_RX_SIPO48_RST),
    .i_enable(w_RX_SIPO48_EN),
    .i_message_bit(w_SD_CMD_RX),
    .o_data(r_RX_RESPONSE));
  
  assign r_RESPONSE_CONTENT = r_RX_RESPONSE[39:8];
  assign r_RESPONSE_INDEX  = r_RX_RESPONSE[45:40];
  assign w_RCA_D_Q          = r_RESPONSE_CONTENT[39:24];

  crc7_sipo_np_ce  rx_crc7_sipo
    (.clk(r_G_CLK_SD),
    .rst(w_RX_CRC7_SIPO_RST),
    .i_enable(w_RX_CRC7_SIPO_EN),
    .i_message_bit(w_SD_CMD_RX),
    .o_crc7(r_RX_CRC7_Q));

  // Fetch control bits using r_opcode
  assign w_instruction_control_bits = (w_OPCODE_Q == ({c_CMD, c_Go_Idle_State})) ? ({c_response_type_R0_NONE, c_DAT_none, c_CMD0_mask_check_redo_bits, c_CMD0_ans_dont_redo, c_CMD0_mask_check_error_bits, c_CMD0_ans_error_free}) :  // CMD0

                                      (w_OPCODE_Q == ({c_CMD, c_All_Send_CID})) ? ({c_response_type_R2_CID_CSD, c_DAT_none, c_CMD2_mask_check_redo_bits, c_CMD2_ans_dont_redo, c_CMD2_mask_check_error_bits, c_CMD2_ans_error_free}):  // CMD2

				      (w_OPCODE_Q == ({c_CMD, c_SD_Send_RCA})) ? ({c_response_type_R6_RCA, c_DAT_none, c_CMD3_mask_check_redo_bits, c_CMD3_ans_dont_redo, c_CMD3_mask_check_error_bits, c_CMD3_ans_error_free}) : // CMD3

				      (w_OPCODE_Q == ({c_CMD, c_Switch_Function})) ? ({c_response_type_R1_NORMAL, c_DAT_wide, c_CMD6_mask_check_redo_bits, c_CMD6_ans_dont_redo, c_CMD6_mask_check_error_bits, c_CMD6_ans_error_free}):  // CMD6

				      (w_OPCODE_Q == ({c_ACMD, c_Set_Bus_Width})) ? ({c_response_type_R1_NORMAL, c_DAT_none, c_ACMD6_mask_check_redo_bits, c_ACMD6_ans_dont_redo, c_ACMD6_mask_check_error_bits, c_ACMD6_ans_error_free}):  //ACMD6

				      (w_OPCODE_Q == ({c_CMD, c_Select_Card})) ? ({c_response_type_R1_NORMAL, c_DAT_busy, c_CMD7_mask_check_redo_bits, c_CMD7_ans_dont_redo, c_CMD7_mask_check_error_bits, c_CMD7_ans_error_free}):  // CMD7

				      (w_OPCODE_Q == ({c_CMD, c_Send_IF_State})) ? ({c_response_type_R7_CIC, c_DAT_none, c_CMD8_mask_check_redo_bits, c_CMD8_ans_dont_redo, c_CMD8_mask_check_error_bits, c_CMD8_ans_error_free}):  // CMD8

				      (w_OPCODE_Q == ({c_CMD, c_Read_Single_Block})) ? ({c_response_type_R1_NORMAL, c_DAT_block, c_CMD17_mask_check_redo_bits, c_CMD17_ans_dont_redo, c_CMD17_mask_check_error_bits, c_CMD17_ans_error_free}):  // CMD17

                                      (w_OPCODE_Q == ({c_ACMD, c_SD_Send_OCR})) ? ({c_response_type_R3_OCR, c_DAT_none, c_ACMD41_mask_check_redo_bits, c_ACMD41_ans_dont_redo, c_ACMD41_mask_check_error_bits, c_ACMD41_ans_error_free}) : //ACMD41

				      (w_OPCODE_Q == ({c_CMD, c_App_Command})) ? ({c_response_type_R1_NORMAL, c_DAT_none, c_CMD55_mask_check_redo_bits, c_CMD55_ans_dont_redo, c_CMD55_mask_check_error_bits, c_CMD55_ans_error_free}) : // CMD55

				      (w_OPCODE_Q == ({c_ACMD, c_App_Command})) ? ({c_response_type_R1_NORMAL, c_DAT_none, c_ACMD55_mask_check_redo_bits, c_ACMD55_ans_dont_redo, c_ACMD55_mask_check_error_bits, c_ACMD55_ans_error_free}) : //ACMD55

                                      ({c_response_type_R1_NORMAL, c_DAT_none, c_ACMD55_mask_check_redo_bits, c_ACMD55_ans_dont_redo, c_ACMD55_mask_check_error_bits, c_ACMD55_ans_error_free});  // when in doubt just send ACMD55
  
  assign w_R_TYPE        = w_instruction_control_bits[132:130];
  assign w_USES_DAT      = w_instruction_control_bits[129:128];
  assign w_NO_REDO_MASK  = w_instruction_control_bits[127:96];
  assign w_NO_REDO_ANS   = w_instruction_control_bits[95:64];
  assign w_NO_ERROR_MASK = w_instruction_control_bits[63:32];
  assign w_NO_ERROR_ANS  = w_instruction_control_bits[31:0];

    
endmodule

