diff --git a/rtl/library/adc_driver.v b/rtl/library/adc_driver.v new file mode 100644 index 0000000..dc63015 --- /dev/null +++ b/rtl/library/adc_driver.v @@ -0,0 +1,152 @@ +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