module adc_driver( input clk, input rstn, input adc_so, output reg adc_si, output reg adc_ss, reg adc_sck, output reg [1:0] channel, output reg [11:0] adc_val, output reg vld, input ack ); reg [2:0] sck_strobe = 0; wire strobe = &sck_strobe; always @(posedge clk) begin if (rstn) sck_strobe <= sck_strobe + 1; else sck_strobe <= 0; end localparam INIT = 0, CONFIG = 1, ADC = 2; reg [1:0] state = INIT; reg [1:0] state_next; reg [3:0] bit_pos = 0; reg [3:0] bit_pos_next; reg [15:0] so_ff = 0; reg [15:0] so_ff_next; reg write_out; reg sck_next; reg ss_next; reg si_next; reg adc_sck_ff = 1; assign adc_sck = adc_sck_ff; wire config_bit; assign config_bit = { 1'b0, // ADC Mode Control 4'b0100, // Standard_Ext 4'b0011, // Up to AIN3 2'b01, // reset FIFO 2'b00, // normal PM mode 1'b1, // include channel number in output 1'b0, // SWCNV enable, not used in external clock mode 1'b0, // reserved }[15-bit_pos]; always @* begin state_next = state; bit_pos_next = bit_pos; write_out = 0; so_ff_next = so_ff; sck_next = 1; ss_next = 1; if (rstn) begin // latch sck and ss state until the strobe happens si_next = adc_si; ss_next = adc_ss; sck_next = adc_sck; if (sck_strobe) begin case (state) INIT: begin ss_next = 0; state_next = CONFIG; bit_pos_next = 0; end CONFIG: begin ss_next = 0; sck_next = ~adc_sck; // update on the falling edge if (adc_sck) begin bit_pos_next = bit_pos + 1; si_next = config_bit; // deassert slave select so it can be triggered on the next frame if (bit_pos == 15) begin ss_next = 1; //bit_pos_next = 0; // don't need this because it's going to // overflow end end // switch state on the rising edge after the overflow // and reassert ss to start the next frame if (~adc_sck & (bit_pos == 0)) begin ss_next = 0; state_next = ADC; end end ADC: begin ss_next = 0; si_next = 0; sck_next = ~adc_sck; // update bit pos state on the falling edge // shift in data as well if (adc_sck) begin bit_pos_next = bit_pos + 1; so_ff_next = {so_ff[14:0], adc_si}; // after reading the last bit // deassert ss so it can be reasserted on the next rising edge if (bit_pos == 15) begin ss_next = 1; end end // write the data out on the rising edge after the overflow if (~adc_sck & (bit_pos == 0)) begin write_out = 1; end end endcase end end else begin state_next = INIT; bit_pos_next = 0; sck_next = 1; ss_next = 1; end end always @(posedge clk) begin state <= state_next; bit_pos <= bit_pos_next; adc_sck_ff <= sck_next; adc_ss <= ss_next; adc_si <= si_next; so_ff <= so_ff_next; // write the data out when write_out is asserted if (write_out) begin {adc_channel, adc_val} <= so_ff[13:0]; vld <= 1; end // deassert vld when the data is acknowledged if (ack) vld <= 0; end endmodule