To implement SAR ADC mixed signal circuit, let's use a microarchitecture of SAR ADC function divided in analog and digital blocks.
To simplify the design process, it is advisable to migrate as many functions of SAR ADC as possible into the digital block. This is because the digital block can be implemented using synchronous logic, making it possible to synthesize using the digital block RTL source code. On the other hand, analog design is inherently complex and should be kept to a minimum in any mixed signal design.
For this example, I have chosen a 3-bit ADC SAR switched capacitor architecture:
In this architecture we really need only capacitors ( and their three position switches ), one comparator and one two position switch to be implemented in the analog block so all other functions could be implemented in the digital block.
§ Piloting all five analog switches could be done from the digital block as well
Here is an example of a possible microarchitecture split between analog and digital block to implement a 3-bit ADC SAR switched capacitor architecture in our example:
Under assumption that Vref = 1V, here is an explanation what is our ADC supposed to do:
§ translate Vin voltage ranges to their 3-bit equivalents:
Vref = 1V, Vin voltage ranges | ADC expected results per range |
> 0 and =< 0.125 | 000 |
> 0.125 and =< 0.25 | 001 |
> 0.25 and =< 0.375 | 010 |
> 0.375 and =< 0.5 | 011 |
> 0.5 and =< 0.625 | 100 |
> 0.635 and =< 0.75 | 101 |
> 0.75 and =< 0.875 | 110 |
> 0.875 and =< 1 | 111
|
Interface description:
· Top level signals:
o clk
§ digital block main ( and only ) clock
o reset
§ digital block power-on reset ( active low )
o adc_conversion_en
§ when adc_conversion_en is 1 => ADC function enabled
o Vin
§ analog signal sampled in ADC
· Analog-Digital interface
o adc_compout
§ analog block output of the comparator
o sw0_closed
§ digital output that determines if analog block switch Sw0 is connecting minus input of comparator to GND ( sw0_closed = 1 ) or not ( sw0_closed = 0)
o sw<x>_vin, sw<x>_vref , sw<x>_gnd
§ <x> is representing 1, 2, 3 or 4.
§ Each signal described as sw<x>* represents a control signal for piloting a three position analog switch: Sw4, Sw3, Sw2 or Sw1 connecting a respective capacitor to either Vin or Vref or GND.
· E.g. possible values of analog switch Sw4 in one clock cycle:
Sw4 connecting 4C capacitor to: | sw4_vin | sw4_vref | sw4_gnd |
Vin | 1 | 0 | 0 |
Vref | 0 | 1 | 0 |
GND | 0 | 0 | 1
|
As a proof of concept here is an example RTL code of the digital block, and for its quick verification ( sorry the verification is not self-checking) a crude Verilog analog block model and a top module where analog and digital block are connected.
To simplify the proof of the concept the assumption is made that analog switches and an analog comparator can finish a job in one digital clock cycle. If this is not true with a real life analog design, we would need a relatively simple change of state-machine timing of changing states to adjust for specified delays in the analog block.
Functional verification is done using Vref = 1V, and I used around 26 different Vi samples, keeping one sample for a period of two ADC cycles.
Here are 26 different Vi samples used and for each sample there is a 3-bit expected ADC result:
Vin[V], Vref = 1V | 3-bit ADC expected results |
0 | 000 |
0.01 | 000 |
0.124 | 000 |
0.125 | 000 |
0.126 | 001 |
0.24 | 001 |
0.25 | 001 |
0.26 | 010 |
0.374 | 010 |
0.375 | 010 |
0.376 | 011 |
0.49 | 011 |
0.50 | 011 |
0.51 | 100 |
0.624 | 100 |
0.625 | 100 |
0.626 | 101 |
0.749 | 101 |
0.750 | 101 |
0.751 | 110 |
0.874 | 110 |
0.875 | 110 |
0.876 | 111 |
0.99 | 111 |
1 | 111 |
Here are some snapshots of waveforms od ADC data and it's ready signal as a function of Vin:
Finally here is a Verilog code of the digital design and it's simple verification infrastructure:
///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// // top_test_adc_8bit.v module top_test_adc_8bit (); reg POR_sync_i ; reg clk ;
reg adc_conversion_en ; wire adc_comp_out ;
wire sw4_vin ; wire sw4_vref; wire sw4_gnd ;
wire sw3_vin ; wire sw3_vref; wire sw3_gnd ;
wire sw2_vin ; wire sw2_vref; wire sw2_gnd ;
wire sw1_vin ; wire sw1_vref; wire sw1_gnd ;
wire sw0_closed;
adc_8bit_dig_top adc_8bit_dig_top ( .POR_sync_i ( POR_sync_i ) , .clk ( clk ) ,
.adc_conversion_en_i ( adc_conversion_en ) ,
.adc_comp_out_i ( adc_comp_out ) ,
.sw4_vin_o ( sw4_vin ) , .sw4_vref_o ( sw4_vref ) , .sw4_gnd_o ( sw4_gnd ) ,
.sw3_vin_o ( sw3_vin ) , .sw3_vref_o ( sw3_vref ) , .sw3_gnd_o ( sw3_gnd ) ,
.sw2_vin_o ( sw2_vin ) , .sw2_vref_o ( sw2_vref ) , .sw2_gnd_o ( sw2_gnd ) ,
.sw1_vin_o ( sw1_vin ) , .sw1_vref_o ( sw1_vref ) , .sw1_gnd_o ( sw1_gnd ) ,
.sw0_closed_o ( sw0_closed ));
adc_8bit_analog_model adc_8bit_analog_model (
.POR_sync_i ( POR_sync_i ) , .clk ( clk ) ,
.adc_conversion_en_i ( adc_conversion_en ) ,
.adc_comp_out_o ( adc_comp_out ) ,
.sw4_vin_i ( sw4_vin ) , .sw4_vref_i ( sw4_vref ) , .sw4_gnd_i ( sw4_gnd ) ,
.sw3_vin_i ( sw3_vin ) , .sw3_vref_i ( sw3_vref ) , .sw3_gnd_i ( sw3_gnd ) ,
.sw2_vin_i ( sw2_vin ) , .sw2_vref_i ( sw2_vref ) , .sw2_gnd_i ( sw2_gnd ) ,
.sw1_vin_i ( sw1_vin ) , .sw1_vref_i ( sw1_vref ) , .sw1_gnd_i ( sw1_gnd ) ,
.sw0_closed_i ( sw0_closed ));
initial begin clk = 1'b0 ; adc_conversion_en = 1'b0 ;
POR_sync_i = 1'b1 ; #100 POR_sync_i = 1'b0 ; @ ( posedge clk ) ; @ ( posedge clk ) ; adc_conversion_en = 1'b1 ; end always begin #10 clk = !clk ; end
endmodule
|
//////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // adc_8bit_analog_model.v
module adc_8bit_analog_model ( POR_sync_i , clk ,
adc_conversion_en_i ,
adc_comp_out_o ,
sw4_vin_i , sw4_vref_i , sw4_gnd_i ,
sw3_vin_i , sw3_vref_i , sw3_gnd_i ,
sw2_vin_i , sw2_vref_i , sw2_gnd_i ,
sw1_vin_i , sw1_vref_i , sw1_gnd_i ,
sw0_closed_i);
input POR_sync_i ; input clk ;
input adc_conversion_en_i ;
output adc_comp_out_o ;
input sw4_vin_i ; input sw4_vref_i ; input sw4_gnd_i ;
input sw3_vin_i ; input sw3_vref_i ; input sw3_gnd_i ;
input sw2_vin_i ; input sw2_vref_i ; input sw2_gnd_i ;
input sw1_vin_i ; input sw1_vref_i ; input sw1_gnd_i ;
input sw0_closed_i ;
parameter START_ST = 3'b000 ; parameter MSB_ST = 3'b001 ; parameter BIT_1_ST = 3'b010 ; parameter LSB_ST = 3'b011 ;
reg[2:0] state, next_state ;
reg adc_comp_out_o ;
wire sw4_vin_i ; wire sw4_vref_i ; wire sw4_gnd_i ;
wire sw3_vin_i ; wire sw3_vref_i ; wire sw3_gnd_i ;
wire sw2_vin_i ; wire sw2_vref_i ; wire sw2_gnd_i ;
wire sw1_vin_i ; wire sw1_vref_i ; wire sw1_gnd_i ;
wire sw0_closed_i ;
real neg_inp_comp ;
real vin ;
reg[5:0] count, next_count ;
initial begin vin = 0 ; adc_comp_out_o = 0 ; end
always@( count ) begin
case ( count ) 6'd0,6'd1, 6'd2: vin = 0 ; 6'd3, 6'd4 : vin = 0.01 ; 6'd5, 6'd6 : vin = 0.124 ; 6'd7, 6'd8 : vin = 0.125 ; 6'd9, 6'd10 : vin = 0.126 ; 6'd11,6'd12 : vin = 0.24 ; 6'd13, 6'd14 : vin = 0.25 ; 6'd15, 6'd16 : vin = 0.26 ; 6'd17, 6'd18 : vin = 0.374 ; 6'd19, 6'd20 : vin = 0.375 ; 6'd21, 6'd22 : vin = 0.376 ; 6'd23, 6'd24 : vin = 0.49 ; 6'd25, 6'd26 : vin = 0.50 ; 6'd27, 6'd28 : vin = 0.51 ; 6'd29,6'd30 : vin = 0.624 ; 6'd31, 6'd32 : vin = 0.625 ; 6'd33, 6'd34: vin = 0.626 ; 6'd35, 6'd36: vin = 0.749 ; 6'd37, 6'd38: vin = 0.750 ; 6'd39, 6'd40: vin = 0.751 ; 6'd41, 6'd42: vin = 0.874 ; 6'd43, 6'd44: vin = 0.875 ; 6'd45, 6'd46: vin = 0.876 ; 6'd47, 6'd48: vin = 0.99 ; 6'd49, 6'd50: vin = 1 ; 6'd51, 6'd52: vin = 1.1 ; endcase end
/// SEQ always@ ( posedge clk or negedge POR_sync_i ) begin if( !POR_sync_i ) begin state <= START_ST ; count <= 6'd0 ; end else begin state <= next_state ; count <= next_count ; end
end
// COMB always@(*) begin next_state = state ; next_count = count ; case( state ) ////////////////////////////////////////////// START_ST: begin if( adc_conversion_en_i ) begin if( sw4_vin_i && sw3_vin_i && sw2_vin_i && sw1_vin_i && sw0_closed_i )begin neg_inp_comp = vin ; next_state = MSB_ST ; end end end /////////////////////////////////////////////// MSB_ST: begin if( adc_conversion_en_i ) begin if( sw4_vref_i && sw3_vin_i && sw2_vin_i && sw1_vin_i && !sw0_closed_i )begin neg_inp_comp = 0.5 - vin ;
if( ( 0.5 - vin ) >= 0 ) begin adc_comp_out_o = 1'b0 ; end else begin adc_comp_out_o = 1'b1 ; end next_state = BIT_1_ST ; end else begin neg_inp_comp = 1'b0 ; adc_comp_out_o = 1'bx ; next_state = START_ST ; end end end /////////////////////////////////////////////// BIT_1_ST: begin if( adc_conversion_en_i ) begin if( ( ( sw4_vref_i || sw4_gnd_i ) && !sw4_vin_i ) && sw3_vref_i && sw2_vin_i && sw1_vin_i && !sw0_closed_i )begin if( sw4_vref_i ) neg_inp_comp = ( 0.5 + 0.25 ) - vin ; if( sw4_gnd_i ) neg_inp_comp = 0.25 - vin ; if( neg_inp_comp >= 0 ) adc_comp_out_o = 1'b0 ; else adc_comp_out_o = 1'b1 ;
next_state = LSB_ST ; end end else begin neg_inp_comp = 1'b0 ; adc_comp_out_o = 1'bx ;
next_state = START_ST ; end end /////////////////////////////////////////////// LSB_ST: begin if( adc_conversion_en_i ) begin if( ( ( sw4_vref_i || sw4_gnd_i ) && !sw4_vin_i ) && ( ( sw3_vref_i || sw3_gnd_i ) && !sw3_vin_i ) && sw2_vref_i && sw1_vref_i && !sw0_closed_i )begin case( { sw4_vref_i, sw3_vref_i } ) 2'b00: neg_inp_comp = 0.5/4 - vin ;
2'b01: neg_inp_comp = 0.5/2 + 0.5/4 - vin ;
2'b10: neg_inp_comp = 0.5 + 0.5/4 - vin ;
2'b11: neg_inp_comp = 0.5 + 0.5/2 + 0.5/4 - vin ; endcase
if( neg_inp_comp >= 0 ) adc_comp_out_o = 1'b0 ; else adc_comp_out_o = 1'b1 ;
if( count <= 6'd51 ) next_count = count + 5'd1 ;
next_state = START_ST ;
end end else begin neg_inp_comp = 1'b0 ; adc_comp_out_o = 1'bx;
next_state = START_ST ; end end endcase
end
endmodule |
///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// // adc_8bit_dig_top.v module adc_8bit_dig_top ( POR_sync_i , clk , adc_conversion_en_i , adc_comp_out_i , sw4_vin_o , sw4_vref_o , sw4_gnd_o , sw3_vin_o , sw3_vref_o , sw3_gnd_o , sw2_vin_o , sw2_vref_o , sw2_gnd_o , sw1_vin_o , sw1_vref_o , sw1_gnd_o , sw0_closed_o);
input POR_sync_i ; input clk ; output adc_comp_out_i ; input adc_conversion_en_i ; output sw4_vin_o ; output sw4_vref_o ; output sw4_gnd_o ; output sw3_vin_o ; output sw3_vref_o ; output sw3_gnd_o ; output sw2_vin_o ; output sw2_vref_o ; output sw2_gnd_o ; output sw1_vin_o ; output sw1_vref_o ; output sw1_gnd_o ; output sw0_closed_o ; parameter START_ST = 3'b000 ; parameter MSB_ST = 3'b001 ; parameter BIT_1_ST = 3'b010 ; parameter LSB_ST = 3'b011 ; parameter END_ST = 3'b100 ; reg[2:0] state, next_state ; reg sw4_vin_o , next_sw4_vin ; reg sw4_vref_o , next_sw4_vref ; reg sw4_gnd_o , next_sw4_gnd ; reg sw3_vin_o , next_sw3_vin ; reg sw3_vref_o , next_sw3_vref ; reg sw3_gnd_o , next_sw3_gnd ; reg sw2_vin_o , next_sw2_vin ; reg sw2_vref_o , next_sw2_vref ; reg sw2_gnd_o , next_sw2_gnd ; reg sw1_vin_o , next_sw1_vin ; reg sw1_vref_o , next_sw1_vref ; reg sw1_gnd_o , next_sw1_gnd ; reg sw0_closed_o, next_sw0_closed ; reg[2:0] adc_data, next_adc_data ; reg adc_data_rdy, next_adc_data_rdy ; /// SEQ always@ ( posedge clk or negedge POR_sync_i ) begin
if( !POR_sync_i ) begin state <= START_ST ; sw4_vin_o <= 1'b0 ; sw4_vref_o <= 1'b0 ; sw4_gnd_o <= 1'b0 ; sw3_vin_o <= 1'b0 ; sw3_vref_o <= 1'b0 ; sw3_gnd_o <= 1'b0 ; sw2_vin_o <= 1'b0 ; sw2_vref_o <= 1'b0 ; sw2_gnd_o <= 1'b0 ; sw1_vin_o <= 1'b0 ; sw1_vref_o <= 1'b0 ; sw1_gnd_o <= 1'b0 ; sw0_closed_o <= 1'b0 ; adc_data <= 3'd0 ; adc_data_rdy <= 1'd0 ; end else begin state <= next_state ; sw4_vin_o <= next_sw4_vin ; sw4_vref_o <= next_sw4_vref ; sw4_gnd_o <= next_sw4_gnd ; sw3_vin_o <= next_sw3_vin ; sw3_vref_o <= next_sw3_vref ; sw3_gnd_o <= next_sw3_gnd ; sw2_vin_o <= next_sw2_vin ; sw2_vref_o <= next_sw2_vref ; sw2_gnd_o <= next_sw2_gnd ; sw1_vin_o <= next_sw1_vin ; sw1_vref_o <= next_sw1_vref ; sw1_gnd_o <= next_sw1_gnd ; sw0_closed_o <= next_sw0_closed ; adc_data <= next_adc_data ; adc_data_rdy <= next_adc_data_rdy ; end end
// COMB always@(*) begin next_state = state ; next_sw4_vin = sw4_vin_o ; next_sw4_vref = sw4_vref_o ; next_sw4_gnd = sw4_gnd_o ; next_sw3_vin = sw3_vin_o ; next_sw3_vref = sw3_vref_o ; next_sw3_gnd = sw3_gnd_o ; next_sw2_vin = sw2_vin_o ; next_sw2_vref = sw2_vref_o ; next_sw2_gnd = sw2_gnd_o ; next_sw1_vin = sw1_vin_o ; next_sw1_vref = sw1_vref_o ; next_sw1_gnd = sw1_gnd_o ; next_sw0_closed = sw0_closed_o ; next_adc_data = adc_data ; next_adc_data_rdy = adc_data_rdy ; case( state ) ////////////////////////////////////////////// START_ST: begin next_sw4_vin = 1'b1 ; next_sw4_vref = 1'b0 ; next_sw4_gnd = 1'b0 ; next_sw3_vin = 1'b1 ; next_sw3_vref = 1'b0 ; next_sw3_gnd = 1'b0 ; next_sw2_vin = 1'b1 ; next_sw2_vref = 1'b0 ; next_sw2_gnd = 1'b0 ; next_sw1_vin = 1'b1 ; next_sw1_vref = 1'b0 ; next_sw1_gnd = 1'b0 ; next_sw0_closed = 1'b1 ; if( adc_conversion_en_i ) begin next_sw4_vin = 1'b0 ; next_sw4_vref = 1'b1 ; next_sw0_closed = 1'b0 ; next_state = MSB_ST ; end end /////////////////////////////////////////////// MSB_ST: begin if( adc_conversion_en_i ) begin if( adc_comp_out_i == 1'b1 )begin next_adc_data[2] = 1'b1 ; next_sw4_vref = 1'b1 ; next_sw4_gnd = 1'b0 ; end else begin next_adc_data[2] = 1'b0 ; next_sw4_vref = 1'b0 ; next_sw4_gnd = 1'b1 ; end next_state = BIT_1_ST ; next_sw3_vin = 1'b0 ; next_sw3_vref = 1'b1 ; end else begin next_sw0_closed = 1'b1 ; next_sw4_vin = 1'b1 ; next_sw4_vref = 1'b0 ; next_sw4_gnd = 1'b0 ; next_state = START_ST ; end end /////////////////////////////////////////////// BIT_1_ST: begin if( adc_conversion_en_i ) begin if( adc_comp_out_i == 1'b1 )begin next_adc_data[1] = 1'b1 ; next_sw3_vref = 1'b1 ; next_sw3_gnd = 1'b0 ; end else begin next_adc_data[1] = 1'b0 ; next_sw3_vref = 1'b0 ; next_sw3_gnd = 1'b1 ; end next_state = LSB_ST ; next_sw2_vin = 1'b0 ; next_sw2_vref = 1'b1 ; next_sw1_vin = 1'b0 ; next_sw1_vref = 1'b1 ; end else begin next_sw0_closed = 1'b1 ; next_sw3_vin = 1'b1 ; next_sw3_vref = 1'b0 ; next_sw3_gnd = 1'b0 ; next_state = START_ST ; end end /////////////////////////////////////////////// LSB_ST: begin if( adc_conversion_en_i ) begin next_sw3_vin = 1'b0 ; if( adc_comp_out_i == 1'b1 )begin next_adc_data[0] = 1'b1 ; next_sw2_vref = 1'b1 ; next_sw2_gnd = 1'b0 ; next_sw1_vref = 1'b1 ; next_sw1_gnd = 1'b0 ; end else begin next_adc_data[0] = 1'b0 ; next_sw2_vref = 1'b0 ; next_sw2_gnd = 1'b1 ; next_sw1_vref = 1'b0 ; next_sw1_gnd = 1'b1 ; end next_adc_data_rdy = 1'b1 ; next_state = END_ST ; end else begin next_sw0_closed = 1'b1 ; next_sw2_vin = 1'b1 ; next_sw2_vref = 1'b0 ; next_sw2_gnd = 1'b0 ; next_sw1_vin = 1'b1 ; next_sw1_vref = 1'b0 ; next_sw1_gnd = 1'b0 ; next_state = START_ST ; end end /////////////////////////////////////////////// END_ST: begin next_sw0_closed = 1'b1 ; next_sw4_vin = 1'b1 ; next_sw4_vref = 1'b0 ; next_sw4_gnd = 1'b0 ; next_sw3_vin = 1'b1 ; next_sw3_vref = 1'b0 ; next_sw3_gnd = 1'b0 ; next_sw2_vin = 1'b1 ; next_sw2_vref = 1'b0 ; next_sw2_gnd = 1'b0 ; next_sw1_vin = 1'b1 ; next_sw1_vref = 1'b0 ; next_sw1_gnd = 1'b0 ; next_adc_data_rdy = 1'b0 ; next_adc_data = 3'd0 ; next_state = START_ST ; end endcase end
endmodule
|
© 2011-2023 ASIC Stoic. All rights reserved
No comments:
Post a Comment