interfaz serial síncrona en verilog

0

Tengo un ADC (ADS1672 hoja de datos ) (20MHz) con interfaz serial y xilinx spartan 3 XC3S400-208 (50MHz)

en su hoja de datos para la recuperación de datos viene esto:

para eso implementé este código:

entradas y salidas:

input wire DRDY;
input DOUT;
input OTRD;
input wire SCLK;
output CS_ADS;
output reg START;
output [1:0]DRATE;
output FPATH;
output LL_CONFIG;
output LVDS;
output SCLK_SEL;
output PDWN;

reg DRDY1=0,DRDY0=0;
reg [4:0] ADS_bit=0;
reg [23:0] ADS_buff=24'b0000_0000_0000_0000_0000_0000;
reg [23:0] ADS_buff_last=24'b0000_0000_0000_0000_0000_0000;
reg onetime =1'b1;

parámetros:

parameter PWRDWN=1'b0, PWRUP=1'b1 , FPATH_LowLatency=1'b1 , FPATH_WideBand=1'b0 , LL_CONFIG_SingleCycle=1'b0 , LL_CONFIG_FastResponse=1'b1;
parameter DRATE_00=2'b00 ,DRATE_01=2'b01 , DRATE_10=2'b10 , DRATE_11=2'b11, SCLK_SEL_Internal=1'b0 , SCLK_SEL_External=1'b1 , LVDS_LVDS=1'b0 ,LVDS_CMOS=1'b1;

Configuración:

assign DRATE            = DRATE_00;
assign FPATH            = FPATH_LowLatency;
assign LL_CONFIG        = LL_CONFIG_SingleCycle;
assign LVDS             = CMOS;
assign SCLK_SEL         = SCLK_SEL_Internal;
assign PDWN             = PWRUP;
assign CS_ADS           = 1'b0;
assign START            = 1'b1;

Segmento de recuperación de datos:

always @(posedge SCLK)
begin
    START<=1'b1;     //hold start hi

    DRDY1   <=  DRDY;
    DRDY0   <=  DRDY1;

    if((DRDY1==1) && (DRDY0==0))     //rising edge of DRDY to start retrieval
        begin
            ADS_buff[23]<=DOUT;    //First bit is comes with posedge DRDY
            ADS_bit<=5'd23;    //bit index Counter
            onetime<=1'b0;
        end
    else if(ADS_bit==5'b00000 && onetime==1'b0)
        begin
            ADS_buff_last<=ADS_buff[23:0];    //Update last final buffer value
            ADS_buff<=24'b0;     //flush buffer
            onetime<=1'b1;     // to prevent run this again
        end

    if(ADS_bit!=5'b00000)
        begin
            ADS_buff[ADS_bit-1]<=DOUT;     //get data
            ADS_bit<=ADS_bit-1'b1;     //count down (MSB to LSB)
        end
end

cuando establezco SCLK internamente (i significa SCLK_SEL = 0, hago que ads1672 lo genere), nunca obtengo el valor correcto (los valores no son cero)

pero cuando lo configuro externamente (y genero SCLK desde FPGA) por la misma recuperación de datos, ¡algunas veces obtengo el valor correcto y otras veces cero!

la diferencia justa es la frecuencia, cuando es externamente es 12.5 MHz. y cuando es internamente es 19.9 MHz.

SCLK Generator (para cuando está externamente):

always @(posedge main_clk)
begin
    SCLK_cnt <= SCLK_cnt + 8'd1;
    if(SCLK_cnt>=(8'd4-1))       //divide per 4(50MHz/4=12.5MHz)
        SCLK_cnt <= 8'd0;
end
assign SCLK = (SCLK_cnt<8'4/2)?1'b0:1'b1;

¿alguien tiene idea de por qué sucede esto?

    
pregunta Ali Damirchy

1 respuesta

0

Tiene tres relojes, su FPGA (50MHz) marca su reloj ADS1672 y su reloj de cambio. En un caso como este, donde el reloj de su sistema es mucho más alto, el mejor enfoque es ejecutar un sistema completamente sincrónico y usar una especie de "sobremuestreo".

Si haces que tu SCLK sea idéntico a tu reloj ADS1672, solo tienes dos relojes para enfrentar: FPGA y 12.5MHz.

Aún generas el reloj ADS1672 12.5MHz, pero no usas esa señal en tu FPGA como reloj . En su lugar, muestrea todas las entradas justo antes de configurar el reloj ADS1672. Eso le da al ADS1672 el tiempo máximo para que las señales salgan a su FPGA.

No puedo escribir todo el código pero todo se reduce a esto: (¡No probado!)

reg [1:0] clk_div;            // Divide by four
wire      ADC_clk=clk_div[1]; // FPGA clock/4 
reg       ADC_sclk;

always @(posedge clk or <your reset>)
begin
    if ( <your reset>)
      ... clear all your registers
    else
    begin
       clk_div <= clk_div+1;
       if (clk_div==2'b01)
       begin
          // Next cycle ADC clock goes high
          // At this moment we can read the inputs
          // They have had maximum time to get stable
          // ALL code to do with the interface (except shift clock) comes here.
          ........
       end

       // Here is you shift clock
       if (sclk_on)
       begin
          if (clk_div==2'b01)
             ADC_sclk<= 1'b1;
          if (clk_div==2'b11)
             ADC_sclk<= 1'b0;
       else
          ADC_sclk<= 1'b0;
    
respondido por el Oldfart

Lea otras preguntas en las etiquetas