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