Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
16 years ago

Verilog code fails timing analysis for register within same clock domain

Hello--

I've written some code in Verilog to sample data from six ADCs. Some signals in the code must cross clock domains. I have used a synchronizer to ensure that the signals are effectively propagated.

Essentially what I am doing is grabbing data from all of the ADCs at the same time, and then using a system of flags to offload the data to another module. The data that is being offloaded in the code below is "adc_data."

However, although the code is failing slow classical timing analysis in Quartus II, the code passes fast classical timing analysis.

I am using a 30MHz external clock, a 70MHz clock generated from the 30MHz by a PLL, and a 280MHz clock also generated by the same PLL.

Strangely enough, the timing analysis is failing for a register that appears to be in the same clock domain:

Info: Slack time is -2.175 ns for clock "my_pll:pll|altpll:altpll_component|_clk1" between source register "signal:my_signal|adc:my_adc|ccc" and destination register "signal:my_signal|adc:my_adc|adc_data"
    Info: Fmax is 174.03 MHz (period= 5.746 ns)
    Info: + Largest register to register requirement is 3.311 ns
        Info: + Setup relationship between source and destination is 3.571 ns
            Info: + Latch edge is 3.571 ns
                Info: Clock period of Destination clock "my_pll:pll|altpll:altpll_component|_clk1" is 3.571 ns with  offset of 0.000 ns and duty cycle of 50
                Info: Clock offset from Destination is based on specified offset of 0.000 ns and phase shift of 0.000 degrees of the derived clock
                Info: Multicycle Setup factor for Destination register is 1
            Info: - Launch edge is 0.000 ns
                Info: Clock period of Source clock "my_pll:pll|altpll:altpll_component|_clk1" is 3.571 ns with  offset of 0.000 ns and duty cycle of 50
                Info: Clock offset from Source is based on specified offset of 0.000 ns and phase shift of 0.000 degrees of the derived clock
                Info: Multicycle Setup factor for Source register is 1
        Info: + Largest clock skew is 0.004 ns
            Info: + Shortest clock path from clock "my_pll:pll|altpll:altpll_component|_clk1" to destination register is 2.515 ns
                Info: 1: + IC(0.000 ns) + CELL(0.000 ns) = 0.000 ns; Loc. = PLL_1; Fanout = 1; CLK Node = 'my_pll:pll|altpll:altpll_component|_clk1'
                Info: 2: + IC(0.916 ns) + CELL(0.000 ns) = 0.916 ns; Loc. = CLKCTRL_G2; Fanout = 126; COMB Node = 'my_pll:pll|altpll:altpll_component|_clk1~clkctrl'
                Info: 3: + IC(0.933 ns) + CELL(0.666 ns) = 2.515 ns; Loc. = LCFF_X14_Y4_N15; Fanout = 2; REG Node = 'signal:my_signal|adc:my_adc|adc_data'
                Info: Total cell delay = 0.666 ns ( 26.48 % )
                Info: Total interconnect delay = 1.849 ns ( 73.52 % )
            Info: - Longest clock path from clock "my_pll:pll|altpll:altpll_component|_clk1" to source register is 2.511 ns
                Info: 1: + IC(0.000 ns) + CELL(0.000 ns) = 0.000 ns; Loc. = PLL_1; Fanout = 1; CLK Node = 'my_pll:pll|altpll:altpll_component|_clk1'
                Info: 2: + IC(0.916 ns) + CELL(0.000 ns) = 0.916 ns; Loc. = CLKCTRL_G2; Fanout = 126; COMB Node = 'my_pll:pll|altpll:altpll_component|_clk1~clkctrl'
                Info: 3: + IC(0.929 ns) + CELL(0.666 ns) = 2.511 ns; Loc. = LCFF_X16_Y5_N3; Fanout = 26; REG Node = 'signal:my_signal|adc:my_adc|ccc'
                Info: Total cell delay = 0.666 ns ( 26.52 % )
                Info: Total interconnect delay = 1.845 ns ( 73.48 % )
        Info: - Micro clock to output delay of source is 0.304 ns
        Info: - Micro setup delay of destination is -0.040 ns
    Info: - Longest register to register delay is 5.486 ns
        Info: 1: + IC(0.000 ns) + CELL(0.000 ns) = 0.000 ns; Loc. = LCFF_X16_Y5_N3; Fanout = 26; REG Node = 'signal:my_signal|adc:my_adc|ccc'
        Info: 2: + IC(0.474 ns) + CELL(0.623 ns) = 1.097 ns; Loc. = LCCOMB_X16_Y5_N20; Fanout = 26; COMB Node = 'signal:my_signal|adc:my_adc|Add2~3'
        Info: 3: + IC(1.892 ns) + CELL(0.624 ns) = 3.613 ns; Loc. = LCCOMB_X15_Y3_N8; Fanout = 4; COMB Node = 'signal:my_signal|adc:my_adc|ShiftLeft0~30'
        Info: 4: + IC(1.141 ns) + CELL(0.624 ns) = 5.378 ns; Loc. = LCCOMB_X14_Y4_N14; Fanout = 1; COMB Node = 'signal:my_signal|adc:my_adc|adc_data~141'
        Info: 5: + IC(0.000 ns) + CELL(0.108 ns) = 5.486 ns; Loc. = LCFF_X14_Y4_N15; Fanout = 2; REG Node = 'signal:my_signal|adc:my_adc|adc_data'
        Info: Total cell delay = 1.979 ns ( 36.07 % )
        Info: Total interconnect delay = 3.507 ns ( 63.93 % )
What could I do to improve timing analysis? Here is the code for my module:

module adc(    // inputs    
        rst,
        set,    
        clk,
        hs_clk,
        vhs_clk,    
        sc_enable,
        miso,
        rs_offload_flag,
        
        // outputs
         tr_adc,
         sclk_adc,
         adc_data, 
         offload_flag
        );
        
                
// Inputs
input rst;                // reset
input set;                // helps to ensure state
input clk;                // main clk for the system (30 MHz)
input hs_clk;            // High speed 70 MHz clock for ADCs
input vhs_clk;            // Very high speed clock for sampling SPI (280MHz)
input sc_enable;        // samples will occcur on sample_clk
input  miso;        // six (6) inputs for each ADC
input rs_offload_flag;    // resets the offload_flag
// Outputs    
output  tr_adc;    // used to trigger ADCs
output  sclk_adc;    // sclk line for the ADCs
// data from the ADCs 
output  adc_data;  // this is the freeze register
output offload_flag;    //  Data offloaded from the ADCs    
// Regs
reg  tr_adc;    // six (6) triggers for each ADC
reg sample_flag;    // flag to indicate the beginning of each sample
// ADC data register holds the output from the ADCs
reg  adc_data;
reg offload_flag = 0;
reg  ccc;        // ccc = 20, so this is the maximum number
reg  sclk_adc;
// Flags
reg collect_data = 0;
// Shift registers
reg  miso_sr;
reg  adc_sclk_sr;
// determine the falling edge of MISO
// note the use of the reduction OR | operator
// which is used to check if all of MISO from each
// ADC is low
always @(posedge vhs_clk) 
    miso_sr <= { miso_sr, |miso };
wire falling_edge_miso = (miso_sr == 2'b10);
// determine the rising and falling edges of sclk_adc
always @(posedge vhs_clk) 
    adc_sclk_sr <= { adc_sclk_sr, hs_clk };
wire rising_edge_sclk_adc = (adc_sclk_sr == 2'b01);
wire falling_edge_sclk_adc = (adc_sclk_sr == 2'b10);
// initiate the conversion by toggling tr_adc
always @(posedge clk) begin
    if( rst || !set ) begin
        sample_flag <= 1'b0;
        tr_adc <= 5'b0;
    end
    else begin
        if(sc_enable && !sample_flag) begin
            tr_adc <= 6'b111111;
            sample_flag <= 1'b1;
        end
        else begin
            tr_adc <= 6'b0;
        end
    
        if(!sc_enable) begin
            sample_flag <= 1'b0;
            tr_adc <= 6'b0;
        end
    end        
end
// these signals are cascaded to ensure that we pass timing analysis
reg rloc_set;
Signal_CrossDomain my_first_crossdomain(
                                .clkA(clk),
                                .SignalIn(set),
                                .clkB(hs_clk),
                                .SignalOut(rloc_set)
                                );
wire loc_set;
Signal_CrossDomain my_second_crossdomain(
                                .clkA(hs_clk),
                                .SignalIn(rloc_set),
                                .clkB(vhs_clk),
                                .SignalOut(loc_set)
                                );
                                
                                
wire loc_rs_offload_flag;
Signal_CrossDomain my_third_crossdomain(
                                .clkA(hs_clk),
                                .SignalIn(rs_offload_flag),
                                .clkB(vhs_clk),
                                .SignalOut(loc_rs_offload_flag)
                                );                                
// sample all of the ADCs at the same time
always @(posedge vhs_clk) begin
    if( (rst || !loc_set) ) begin
        collect_data <= 0;
    end
    
    else begin
    
            // wait until the falling edge of miso
            if(falling_edge_miso && !collect_data) begin
                collect_data <= 1;                        
            end
            
            if(collect_data) begin
            
                if(rising_edge_sclk_adc) begin
                    sclk_adc <= 6'b111111;
                    ccc <= ccc + 1'b1;
                    
                    if( ccc >= 1 )
                        adc_data <= (adc_data | (miso << (6 * (ccc-1) )));
                        
                end
                
                if(falling_edge_sclk_adc) begin
                    sclk_adc <= 6'b000000;
                    if(ccc == 19) begin
                        collect_data <= 0;
                        offload_flag <= 1;
                    end
                end    
            end // collect_data
            
            // clear the offload_flag and the adc_data in preparation
            // for the next measurement
            if( loc_rs_offload_flag ) begin 
                offload_flag <= 0;
                adc_data <= 0;
                ccc <= 0;
            end
            
    end //else
end // posedge vhs_clk
// ends the module
endmodule

11 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    For my specific application, I had to modify things a little bit more before I could reliably read from SPI.

    I changed the PLL so that the SPI ADCs were clocked at 60MHz, and the SPI MISO bus was sampled at 180MHz, which is 3x the speed of 60MHz. Running the design with these clocks allowed for timing analysis to be met.

    Once again, thank you for your help, FvM!