Commit 428053b8 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

reated a top module for all timestamp/synchroniztion functionality

parent 2675a8a1
Loading
Loading
Loading
Loading
+51 −37
Original line number Original line Diff line number Diff line
@@ -27,7 +27,8 @@
 // TODO: make a separate clock for transmission (program counters too?) and/or for the period timer?
 // TODO: make a separate clock for transmission (program counters too?) and/or for the period timer?
 // TODO: change timestamp to serial message
 // TODO: change timestamp to serial message
 // TODO: see what depends on pclk and if can be made independent of the sensor clock.
 // TODO: see what depends on pclk and if can be made independent of the sensor clock.

//`define GENERATE_TRIG_OVERDUE 1
`undef GENERATE_TRIG_OVERDUE
module camsync393       #(
module camsync393       #(
    parameter CAMSYNC_ADDR =                    'h160, //TODO: assign valid address
    parameter CAMSYNC_ADDR =                    'h160, //TODO: assign valid address
    parameter CAMSYNC_MASK =                    'h3f8,
    parameter CAMSYNC_MASK =                    'h3f8,
@@ -82,25 +83,29 @@ module camsync393 #(
    output                        triggered_mode, // use triggered mode (0 - sensors are free-running) @mclk
    output                        triggered_mode, // use triggered mode (0 - sensors are free-running) @mclk


    input                         frsync_chn0,   // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    input                         frsync_chn0,   // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    output                        trigger1_chn0, // @mclk 1 cycle-long trigger output
    output                        trig_chn0,     // @mclk 1 cycle-long trigger output
`ifdef GENERATE_TRIG_OVERDUE    
    output                        trigger_chn0,  // @mclk active high trigger to the sensor (reset by vacts)
    output                        trigger_chn0,  // @mclk active high trigger to the sensor (reset by vacts)
    output                        overdue_chn0,  // @mclk prevents lock-up when no vact was detected during one period and trigger was toggled
    output                        overdue_chn0,  // @mclk prevents lock-up when no vact was detected during one period and trigger was toggled

`endif
    input                         frsync_chn1,   // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    input                         frsync_chn1,   // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    output                        trigger1_chn1, // 1 cycle-long trigger output
    output                        trig_chn1,     // 1 cycle-long trigger output
`ifdef GENERATE_TRIG_OVERDUE    
    output                        trigger_chn1,  // active high trigger to the sensor (reset by vacts)
    output                        trigger_chn1,  // active high trigger to the sensor (reset by vacts)
    output                        overdue_chn1,  // prevents lock-up when no vact was detected during one period and trigger was toggled
    output                        overdue_chn1,  // prevents lock-up when no vact was detected during one period and trigger was toggled

`endif
    input                         frsync_chn2,  // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    input                         frsync_chn2,  // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    output                        trigger1_chn2, // 1 cycle-long trigger output
    output                        trig_chn2,    // 1 cycle-long trigger output
`ifdef GENERATE_TRIG_OVERDUE    
    output                        trigger_chn2, // active high trigger to the sensor (reset by vacts)
    output                        trigger_chn2, // active high trigger to the sensor (reset by vacts)
    output                        overdue_chn2, // prevents lock-up when no vact was detected during one period and trigger was toggled
    output                        overdue_chn2, // prevents lock-up when no vact was detected during one period and trigger was toggled

`endif
    input                         frsync_chn3,  // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    input                         frsync_chn3,  // @mclk trigrst,   // single-clock start of frame input (resets trigger output) posedge (@pclk)
    output                        trigger1_chn3, // 1 cycle-long trigger output
    output                        trig_chn3,    // 1 cycle-long trigger output
`ifdef GENERATE_TRIG_OVERDUE    
    output                        trigger_chn3, // active high trigger to the sensor (reset by vacts)
    output                        trigger_chn3, // active high trigger to the sensor (reset by vacts)
    output                        overdue_chn3, // prevents lock-up when no vact was detected during one period and trigger was toggled
    output                        overdue_chn3, // prevents lock-up when no vact was detected during one period and trigger was toggled
    
`endif    
    // getting timestamp from rtc module, all @posedge mclk (from timestmp_snapshot)
    // getting timestamp from rtc module, all @posedge mclk (from timestmp_snapshot)
    // this timestmp is used either to send local timestamp for synchronization, or
    // this timestmp is used either to send local timestamp for synchronization, or
    // to acquire local timestamp of sync pulse for logging
    // to acquire local timestamp of sync pulse for logging
@@ -216,10 +221,13 @@ module camsync393 #(
    reg           trigger_condition_d; // GPIO input trigger condition met, delayed (for edge detection)
    reg           trigger_condition_d; // GPIO input trigger condition met, delayed (for edge detection)
    reg           trigger_condition_filtered; // trigger condition filtered
    reg           trigger_condition_filtered; // trigger condition filtered
    reg    [6:0]  trigger_filter_cntr;
    reg    [6:0]  trigger_filter_cntr;
    reg    [3:0]  trigger1_r;
    reg    [3:0]  trig_r;
    wire   [3:0]  trigger1_r_mclk;
    wire   [3:0]  trig_r_mclk;
//    wire          trigger1_dly16; // trigger1 delayed by 16 clk cycles to get local timestamp
//    wire          trig_dly16; // trigger1 delayed by 16 clk cycles to get local timestamp
`ifdef GENERATE_TRIG_OVERDUE    
    reg    [3:0]  trigger_r=0;       // for happy simulator
    reg    [3:0]  trigger_r=0;       // for happy simulator
    reg     [3:0] overdue;
`endif    
    reg           start_dly;      // start delay (external input filtered or from internal single/rep)
    reg           start_dly;      // start delay (external input filtered or from internal single/rep)
    reg   [31:0]  dly_cntr_chn0;       // trigger delay counter
    reg   [31:0]  dly_cntr_chn0;       // trigger delay counter
    reg   [31:0]  dly_cntr_chn1;       // trigger delay counter
    reg   [31:0]  dly_cntr_chn1;       // trigger delay counter
@@ -276,7 +284,6 @@ module camsync393 #(
    wire    [3:0] frame_sync;
    wire    [3:0] frame_sync;
    reg     [3:0] ts_snap_triggered;     // make a timestamp pulse  single @(posedge pclk)
    reg     [3:0] ts_snap_triggered;     // make a timestamp pulse  single @(posedge pclk)
    wire    [3:0] ts_snap_triggered_mclk;     // make a timestamp pulse  single @(posedge pclk)
    wire    [3:0] ts_snap_triggered_mclk;     // make a timestamp pulse  single @(posedge pclk)
    reg     [3:0] overdue;
//! in testmode GPIO[9] and GPIO[8] use internal signals instead of the outsync:
//! in testmode GPIO[9] and GPIO[8] use internal signals instead of the outsync:
//! bit 11 - same as TRIGGER output to the sensor (signal to the sensor may be disabled externally)
//! bit 11 - same as TRIGGER output to the sensor (signal to the sensor may be disabled externally)
//!          then that bit will be still from internall trigger to frame valid
//!          then that bit will be still from internall trigger to frame valid
@@ -290,17 +297,23 @@ module camsync393 #(


    assign  gpio_out[7: 0] = out_data? gpio_active[7: 0]: ~gpio_active[7: 0];
    assign  gpio_out[7: 0] = out_data? gpio_active[7: 0]: ~gpio_active[7: 0];
    assign  gpio_out[8] = (testmode? dly_cntr_run[0]:  out_data)? gpio_active[8]: ~gpio_active[8];
    assign  gpio_out[8] = (testmode? dly_cntr_run[0]:  out_data)? gpio_active[8]: ~gpio_active[8];
`ifdef GENERATE_TRIG_OVERDUE    
    assign  gpio_out[9] = (testmode? trigger_r[0]:  out_data)? gpio_active[9]: ~gpio_active[9];
    assign  gpio_out[9] = (testmode? trigger_r[0]:  out_data)? gpio_active[9]: ~gpio_active[9];
`else
    assign  gpio_out[9] = (out_data)? gpio_active[9]: ~gpio_active[9];
`endif
    assign  restart= restart_cntr_run[1] && !restart_cntr_run[0];
    assign  restart= restart_cntr_run[1] && !restart_cntr_run[0];
    
    
    assign  pre_set_bit=     (|cmd_data[31:8]==0) && |cmd_data[7:1]; // 2..255
    assign  pre_set_bit=     (|cmd_data[31:8]==0) && |cmd_data[7:1]; // 2..255
    assign  pre_start0=       |cmd_data[31:0] && !pre_set_bit;
    assign  pre_start0=       |cmd_data[31:0] && !pre_set_bit;
    assign  pre_set_period = !pre_set_bit;
    assign  pre_set_period = !pre_set_bit;


    assign {trig_chn3, trig_chn2, trig_chn1, trig_chn0} =  trig_r_mclk;

`ifdef GENERATE_TRIG_OVERDUE    
    assign {trigger_chn3,  trigger_chn2,  trigger_chn1,  trigger_chn0} =   trigger_r;
    assign {trigger_chn3,  trigger_chn2,  trigger_chn1,  trigger_chn0} =   trigger_r;
    assign {trigger1_chn3, trigger1_chn2, trigger1_chn1, trigger1_chn0} =  trigger1_r_mclk;
    assign {overdue_chn3,  overdue_chn2,  overdue_chn1,  overdue_chn0} =   overdue;
    assign {overdue_chn3,  overdue_chn2,  overdue_chn1,  overdue_chn0} =   overdue;
    
`endif    
    assign frame_sync = {frsync_chn3, frsync_chn2, frsync_chn1, frsync_chn0}; 
    assign frame_sync = {frsync_chn3, frsync_chn2, frsync_chn1, frsync_chn0}; 
    
    
    assign set_mode_reg_w =     cmd_we && (cmd_a == CAMSYNC_MODE);
    assign set_mode_reg_w =     cmd_we && (cmd_a == CAMSYNC_MODE);
@@ -394,7 +407,7 @@ module camsync393 #(
    end    
    end    
    always @ (posedge pclk) begin
    always @ (posedge pclk) begin
        ts_snap_triggered <=  chn_en & ({4{(start_pclk[2] & ts_snd_en_pclk)}} | //strobe by internal generator if output timestamp is enabled
        ts_snap_triggered <=  chn_en & ({4{(start_pclk[2] & ts_snd_en_pclk)}} | //strobe by internal generator if output timestamp is enabled
                              (trigger1_r & ~{4{ts_external_pclk}}));  // get local timestamp of the trigger (ext/int)
                              (trig_r & ~{4{ts_external_pclk}}));  // get local timestamp of the trigger (ext/int)


        ts_snd_en_pclk<=ts_snd_en;
        ts_snd_en_pclk<=ts_snd_en;
        input_use_intern <= pre_input_use_intern;
        input_use_intern <= pre_input_use_intern;
@@ -443,21 +456,22 @@ module camsync393 #(
                  (dly_cntr_chn0[31:0]!=0)?1'b1:1'b0};
                  (dly_cntr_chn0[31:0]!=0)?1'b1:1'b0};
    end
    end
 
 
 `ifdef GENERATE_TRIG_OVERDUE    
     always @ (posedge rst or posedge mclk) begin
     always @ (posedge rst or posedge mclk) begin
        if      (rst)             trigger_r <= 0;
        if      (rst)             trigger_r <= 0;
        else if (!triggered_mode) trigger_r <= 0;
        else if (!triggered_mode) trigger_r <= 0;
        else                      trigger_r <= ~frame_sync & (trigger1_r_mclk ^ trigger_r);
        else                      trigger_r <= ~frame_sync & (trig_r_mclk ^ trigger_r);


        if      (rst)             overdue <= 0;
        if      (rst)             overdue <= 0;
        else if (!triggered_mode) overdue <= 0;
        else if (!triggered_mode) overdue <= 0;
        else                      overdue <= ((overdue ^ trigger_r) & trigger1_r_mclk) ^ overdue;
        else                      overdue <= ((overdue ^ trigger_r) & trig_r_mclk) ^ overdue;
        
        
    end
    end
    
 `endif   
     
     
// Detecting input sync pulse (filter - 64 pclk, pulse is 256 pclk)
// Detecting input sync pulse (filter - 64 pclk, pulse is 256 pclk)


/// Now trigger1_r toggles trigger output to prevent lock-up if no vacts
/// Now trig_r toggles trigger output to prevent lock-up if no vacts
/// Lock-up could take place if:
/// Lock-up could take place if:
/// 1 - Sensor is in snapshot mode
/// 1 - Sensor is in snapshot mode
/// 2 - trigger was applied before end of previous frame.
/// 2 - trigger was applied before end of previous frame.
@@ -500,11 +514,11 @@ module camsync393 #(
        if (dly_cntr_run[3]) dly_cntr_chn3[31:0] <= dly_cntr_chn3[31:0] -1;
        if (dly_cntr_run[3]) dly_cntr_chn3[31:0] <= dly_cntr_chn3[31:0] -1;
        else                 dly_cntr_chn3[31:0] <= input_dly_chn3[31:0];
        else                 dly_cntr_chn3[31:0] <= input_dly_chn3[31:0];
        
        
        /// bypass delay to trigger1_r in internal trigger mode
        /// bypass delay to trig_r in internal trigger mode
        trigger1_r[0] <= (input_use_intern && (master_chn ==0)) ? (start_late && start_en):(dly_cntr_run_d[0] && !dly_cntr_run[0]);
        trig_r[0] <= (input_use_intern && (master_chn ==0)) ? (start_late && start_en):(dly_cntr_run_d[0] && !dly_cntr_run[0]);
        trigger1_r[1] <= (input_use_intern && (master_chn ==1)) ? (start_late && start_en):(dly_cntr_run_d[1] && !dly_cntr_run[1]);
        trig_r[1] <= (input_use_intern && (master_chn ==1)) ? (start_late && start_en):(dly_cntr_run_d[1] && !dly_cntr_run[1]);
        trigger1_r[2] <= (input_use_intern && (master_chn ==2)) ? (start_late && start_en):(dly_cntr_run_d[2] && !dly_cntr_run[2]);
        trig_r[2] <= (input_use_intern && (master_chn ==2)) ? (start_late && start_en):(dly_cntr_run_d[2] && !dly_cntr_run[2]);
        trigger1_r[3] <= (input_use_intern && (master_chn ==3)) ? (start_late && start_en):(dly_cntr_run_d[3] && !dly_cntr_run[3]);
        trig_r[3] <= (input_use_intern && (master_chn ==3)) ? (start_late && start_en):(dly_cntr_run_d[3] && !dly_cntr_run[3]);
        
        
/// 64-bit serial receiver (52 bit payload, 6 pre magic and 6 bits post magic for error checking
/// 64-bit serial receiver (52 bit payload, 6 pre magic and 6 bits post magic for error checking
        if      (!rcv_run_or_deaf)         bit_rcv_duration[7:0] <= bit_length_short[7:0]; // 3/4 bit length-1
        if      (!rcv_run_or_deaf)         bit_rcv_duration[7:0] <= bit_length_short[7:0]; // 3/4 bit length-1
@@ -663,10 +677,10 @@ module camsync393 #(
    pulse_cross_clock i_local_got_pclk2(.rst(1'b0), .src_clk(mclk), .dst_clk(pclk), .in_pulse(local_got[2]), .out_pulse(local_got_pclk[2]),.busy());
    pulse_cross_clock i_local_got_pclk2(.rst(1'b0), .src_clk(mclk), .dst_clk(pclk), .in_pulse(local_got[2]), .out_pulse(local_got_pclk[2]),.busy());
    pulse_cross_clock i_local_got_pclk3(.rst(1'b0), .src_clk(mclk), .dst_clk(pclk), .in_pulse(local_got[3]), .out_pulse(local_got_pclk[3]),.busy());
    pulse_cross_clock i_local_got_pclk3(.rst(1'b0), .src_clk(mclk), .dst_clk(pclk), .in_pulse(local_got[3]), .out_pulse(local_got_pclk[3]),.busy());


    pulse_cross_clock i_trigger1_r_mclk0 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trigger1_r[0]), .out_pulse(trigger1_r_mclk[0]),.busy());
    pulse_cross_clock i_trig_r_mclk0 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trig_r[0]), .out_pulse(trig_r_mclk[0]),.busy());
    pulse_cross_clock i_trigger1_r_mclk1 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trigger1_r[1]), .out_pulse(trigger1_r_mclk[1]),.busy());
    pulse_cross_clock i_trig_r_mclk1 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trig_r[1]), .out_pulse(trig_r_mclk[1]),.busy());
    pulse_cross_clock i_trigger1_r_mclk2 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trigger1_r[2]), .out_pulse(trigger1_r_mclk[2]),.busy());
    pulse_cross_clock i_trig_r_mclk2 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trig_r[2]), .out_pulse(trig_r_mclk[2]),.busy());
    pulse_cross_clock i_trigger1_r_mclk3 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trigger1_r[3]), .out_pulse(trigger1_r_mclk[3]),.busy());
    pulse_cross_clock i_trig_r_mclk3 (.rst(1'b0), .src_clk(pclk), .dst_clk(mclk), .in_pulse(trig_r[3]), .out_pulse(trig_r_mclk[3]),.busy());
    
    
endmodule
endmodule
+50 −7
Original line number Original line Diff line number Diff line
@@ -24,12 +24,16 @@


module  rtc393 #(
module  rtc393 #(
        parameter RTC_ADDR =                  'h170, //TODO: assign valid address
        parameter RTC_ADDR =                  'h170, //TODO: assign valid address
        parameter RTC_STATUS_REG_ADDR =          7,  // address where status can be read out (currnelti just sequence # and alternating bit) 
        parameter RTC_SEC_USEC_ADDR =            8,  // address where seconds of the snapshot can be read (microseconds - next adderss)
        
        parameter RTC_MASK =                  'h3fc,
        parameter RTC_MASK =                  'h3fc,
        parameter RTC_MHZ =                      25, // RTC input clock in MHz (should be interger number)
        parameter RTC_MHZ =                      25, // RTC input clock in MHz (should be interger number)
        parameter RTC_BITC_PREDIV =              5, // number of bits to generate 2 MHz pulses counting refclk 
        parameter RTC_BITC_PREDIV =              5, // number of bits to generate 2 MHz pulses counting refclk 
        parameter RTC_SET_USEC =                 0, // 20-bit number of microseconds
        parameter RTC_SET_USEC =                 0, // 20-bit number of microseconds
        parameter RTC_SET_SEC =                  1, // 32-bit full number of seconds (und actually update timer)
        parameter RTC_SET_SEC =                  1, // 32-bit full number of seconds (und actually update timer)
        parameter RTC_SET_CORR=                  2 // write correction 16-bit signed
        parameter RTC_SET_CORR =                 2, // write correction 16-bit signed
        parameter RTC_SET_STATUS =               3  // set status mode, and take a time snapshot (wait response and read time)


)   (
)   (
    input                         rst,
    input                         rst,
@@ -39,8 +43,13 @@ module rtc393 #(
    input                   [7:0] cmd_ad,      // byte-serial command address/data (up to 6 bytes: AL-AH-D0-D1-D2-D3 
    input                   [7:0] cmd_ad,      // byte-serial command address/data (up to 6 bytes: AL-AH-D0-D1-D2-D3 
    input                         cmd_stb,     // strobe (with first byte) for the command a/d
    input                         cmd_stb,     // strobe (with first byte) for the command a/d


    output                  [7:0] status_ad,    // status address/data - up to 5 bytes: A - {seq,status[1:0]} - status[2:9] - status[10:17] - status[18:25]
    output                        status_rq,    // input request to send status downstream
    input                         status_start, // Acknowledge of the first status packet byte (address)
    
    output                 [31:0] live_sec,
    output                 [31:0] live_sec,
    output                 [19:0] live_usec);
    output                 [19:0] live_usec);
//    output reg                    snap);       // take a snapshot (externally)
    
    
    wire  [31:0] cmd_data;
    wire  [31:0] cmd_data;
    wire   [2:0] cmd_a;
    wire   [2:0] cmd_a;
@@ -49,6 +58,7 @@ module rtc393 #(
    wire         set_usec_w;  
    wire         set_usec_w;  
    wire         set_sec_w;  
    wire         set_sec_w;  
    wire         set_corr_w;
    wire         set_corr_w;
    wire         set_status_w;
    
    
    reg  [19:0] wusec;   
    reg  [19:0] wusec;   
    reg  [31:0] wsec;   
    reg  [31:0] wsec;   
@@ -72,16 +82,30 @@ module rtc393 #(
    reg  [19:0] usec_plus1;
    reg  [19:0] usec_plus1;
    reg  [31:0] sec_plus1;
    reg  [31:0] sec_plus1;
    
    
    reg   [31:0] pio_sec;       // seconds snapshot to be read as PIO  
    reg   [19:0] pio_usec;      // micro seconds snapshot to be read as PIO
    reg          pio_alt_snap;  // FF to invert after each PIO snapshot (used to generate status)
    
    
    
    
    assign set_usec_w = cmd_we && (cmd_a == RTC_SET_USEC);
    assign set_usec_w = cmd_we && (cmd_a == RTC_SET_USEC);
    assign set_sec_w =  cmd_we && (cmd_a == RTC_SET_SEC);
    assign set_sec_w =  cmd_we && (cmd_a == RTC_SET_SEC);
    assign set_corr_w = cmd_we && (cmd_a == RTC_SET_CORR);
    assign set_corr_w = cmd_we && (cmd_a == RTC_SET_CORR);
    assign set_status_w = cmd_we && (cmd_a == RTC_SET_STATUS);
    assign next_acc[24:0]= {1'b0,acc[23:0]} + {1'b0,~corr [15], {7{corr [15]}}, corr[15:0]};
    assign next_acc[24:0]= {1'b0,acc[23:0]} + {1'b0,~corr [15], {7{corr [15]}}, corr[15:0]};
    
    
    assign live_sec = sec;
    assign live_sec = sec;
    assign live_usec = usec;
    assign live_usec = usec;
    
    
    always @ (posedge rst or posedge mclk) begin
        if      (rst)          pio_alt_snap <= 0;
        else if (set_status_w) pio_alt_snap <= ~pio_alt_snap; 
    end 

    always @ (posedge mclk) begin
        if (set_status_w) pio_sec <=  live_sec; 
        if (set_status_w) pio_usec <= live_usec; 
    end 
    
    always @ (posedge mclk) begin
    always @ (posedge mclk) begin
        if (set_usec_w) wusec <= cmd_data[19:0]; 
        if (set_usec_w) wusec <= cmd_data[19:0]; 
        if (set_sec_w)  wsec <=  cmd_data[31:0]; 
        if (set_sec_w)  wsec <=  cmd_data[31:0]; 
@@ -146,5 +170,24 @@ module rtc393 #(
        .we         (cmd_we)    // output
        .we         (cmd_we)    // output
    );
    );
 
 
        status_generate #(
        .STATUS_REG_ADDR     (RTC_STATUS_REG_ADDR),
        .PAYLOAD_BITS        (1),
        .REGISTER_STATUS     (0),
        .EXTRA_WORDS         (2),
        .EXTRA_REG_ADDR      (RTC_SEC_USEC_ADDR)
    ) status_generate_i (
        .rst           (), // input
        .clk           (mclk), // input
        .we            (set_status_w), // input
        .wd            (cmd_data[7:0]), // input[7:0] 
        .status        ({12'b0,pio_usec,pio_sec,pio_alt_snap}), // input[14:0] 
        .ad            (status_ad), // output[7:0] 
        .rq            (status_rq), // output
        .start         (status_start) // input
    );


    
endmodule
endmodule

timing/timing393.v

0 → 100644
+273 −0

File added.

Preview size limit exceeded, changes collapsed.