// this module configures the DRV8353,
// leaving the PWM for a different module
// if the fault pin is triggered
// it will read the fault status registers
// and assert the appropriate output wire
// it also allows disabling/enabling
// the MOSFETs using the coast/brake pins
// in the driver control register
module drv8353r_driver(
  input clk,
  input rst,
  input en,

  input drv_fault_n,
  output drv_en,

  output drv_cs_n,
  output drv_sck,
  output drv_sdi,
  input drv_sdo,

  // assert stop with the coast_nbrake
  // bit to disable the gate drivers
  input stop,
  input coast_nbrake,
  input clear_fault,

  output rdy,
  output fault_a,
  output fault_b,
  output fault_c
  );

parameter WAIT_TIME = 98;

wire [15:0] driver_control;
wire [15:0] hs_control;
wire [15:0] ls_control;
wire [15:0] ocp_control;
wire [15:0] csa_control;

assign driver_control = {
  4'b0010, // address = 0x02
  1'b1, // write
  1'b0, // OCP_ACT, shutdown affected half bridge on fault
  1'b0, // DIS_GDUV, undervoltage enabled
  1'b0, // DIS_GDF, gate drive fault enabled
  1'b0, // OTW_REP, overtemp not reported
  2'b01, // PWM_MODE, 3x PWM mode
  1'b0, // 1PWM_COM, 1x PWM uses synchronous rectifier
  1'b0, // 1PWM_DIR, default
  1'b0, // COAST, disabled
  1'b0, // BRAKE, disabled
  1'b0 // CLR_FLT, no clear
};

// TODO actually calculate HS/LS gate drive
// requirements
assign hs_control = {
  4'b0011, // address = 0x03
  1'b1, // write
  3'b000, // LOCK, don't lock or unlock
  4'b0100, // IDRIVEP_HS, 300 mA
  4'b0010 // IDRIVEN_HS, 200 mA
};
assign ls_control = {
  4'b0100, // address = 0x04
  1'b1, // write
  1'b1, // CBC, OCP conditions reset when PWM is provided
  2'b11, // TDRIVE = 4000 ns gate-current drive time
  4'b0100, // IDRIVEP_LS, 300 mA
  4'b0010 // IDRIVEN_LS, 200 mA
};
assign ocp_control = {
  4'b0101, // address = 0x05
  1'b1, // write
  1'b0, // TRETRY, VDS_OCP/SEN_OCP retry time is 8ms
  2'b01, // DEAD_TIME, 100 ns deadtime
  2'b01, // OCP_MODE, overcurrent causes automatic retry
  2'b10, // OCP_DEG, 4us overcurrent deglitch
  4'b1101 // VDS_LVL, 1V
};
assign csa_control = {
  4'b0110, // address = 0x06
  1'b1, // write
  1'b0, // CSA_FET = SPx
  1'b0, // VREF_DIV, unidirectional
  1'b0, // LS_REF, VDS_OCP is measured from SHx to SNx
  2'b10, // CSA_GAIN, 20V/V
  1'b0, // DIS_SEN overcurrent fault enabled
  1'b0, // CSA_CAL_A, normal operation
  1'b0, // CSA_CAL_B, normal operation
  1'b0, // CSA_CAL_C, normal operation
  2'b11 // SEN_LVL, OCP 1 V
};

wire [3:0] cur_bit;
wire [7:0] config_bits;

assign config_bits[0] = driver_control[cur_bit];
assign config_bits[1] = hs_control[cur_bit];
assign config_bits[2] = ls_control[cur_bit];
assign config_bits[3] =
  ocp_control[cur_bit];
assign config_bits[4] =
  csa_control[cur_bit];
assign config_bits[7:5] = 3'b000;

localparam IDLE = 0,
    INIT = 1,
    CONFIG = 2,
    READY = 3;
reg [2:0] state = IDLE;
reg [2:0] state_next;

assign rdy = (state == READY);

reg drv_en_ff = 0;
reg drv_cs_n_ff = 1;
reg drv_sck_ff = 1;
reg drv_sdi_ff = 0;

reg drv_en_next, drv_cs_n_next;
reg drv_sck_next, drv_sdi_next;

assign drv_en = drv_en_ff;
assign drv_cs_n = drv_cs_n_ff;
assign drv_sck = drv_sck_ff;
assign drv_sdi = drv_sdi_ff;

reg [7:0] count = 0;
reg [7:0] count_next;
reg [7:0] msb_count = 0;
reg [7:0] msb_count_next;

assign cur_bit = 15 - count[6:3];

reg [2:0] cur_reg = 0;
reg [2:0] cur_reg_next;

reg fault_a_ff = 0;
reg fault_b_ff = 0;
reg fault_c_ff = 0;

assign fault_a = fault_a_ff;
assign fault_b = fault_b_ff;
assign fault_c = fault_c_ff;

always @* begin
  state_next = state;

  drv_en_next = 0;
  drv_cs_n_next = 1;
  drv_sck_next = 0;
  drv_sdi_next = 0;
  cur_reg_next = 0;

  count_next = count;
  msb_count_next = msb_count;

  case (state)
    IDLE: begin
      if (en) begin
        state_next = INIT;
        drv_en_next = 1;
        count_next = 0;
      end
    end
    INIT: begin
      drv_en_next = 1;

      // wait 1 ms before trying to configure
      count_next = count + 1;
      if (&count_next) begin
        msb_count_next = msb_count;
      end
      if (msb_count == WAIT_TIME) begin
        cur_reg_next = 0;
        state_next = CONFIG;
        count_next = 0;
      end
    end
    CONFIG: begin
      // write all the configuration bits
      // to the DRV8353

      drv_en_next = 1;
      drv_cs_n_next = drv_cs_n_ff;
      drv_sck_next = drv_sck_ff;
      drv_sdi_next = drv_sdi_ff;
      cur_reg_next = cur_reg;

      count_next = count + 1;
      // 8x downsample
      if (&count[1:0]) begin
        if (count[7:2] == 0) begin
          drv_cs_n_next = 0;
        end
        drv_sck_next = count[2];
        if (count[7:2] == 33) begin
          drv_cs_n_next = 1;
          count_next = 0;
          cur_reg_next = cur_reg + 1;
          drv_sck_next = 0;
          if (cur_reg == 4) begin
            state_next = READY;
          end
        end
        // write the next bit on high transitions
        if (count[2]) begin
          drv_sdi_next = config_bits[cur_reg];
        end
      end
    end
    READY: begin
      drv_en_next = 1;
      // TODO if there are faults read
      // the status register to output
      // the appropriate info
      // TODO if a stop is commanded
      // write to the register
      // to stop the motor driver
      // TODO if a stop is deasserted
      // write to the register to
      // reenable the motor driver
    end
  endcase
  
  if (rst) begin
    state_next = IDLE;

    drv_en_next = 0;
    drv_cs_n_next = 1;
    drv_sck_next = 1;
    drv_sdi_next = 0;
  end
end

always @(posedge clk) begin
  state <= state_next;

  drv_en_ff <= drv_en_next;
  drv_cs_n_ff <= drv_cs_n_next;
  drv_sck_ff <= drv_sck_next;
  drv_sdi_ff <= drv_sdi_next;

  count <= count_next;
  msb_count <= msb_count_next;

  cur_reg <= cur_reg_next;
end

endmodule