// Timing subsytem // SNS LLRF Digital board // Target: XC2S150 // Larry Doolittle // January 18-20, 2002 // wishlist: // record length of RF pulse, for readback by software // detect skipped RF triggers // double buffer ttable, easy with an additional address bit // outputs: feedback_run, feedforward_run, adc_power, rf_gate // Input "ck" is the 40 MHz acquisition clock. // // quad_enable is the 10 MHz 1-out-of-4 synchronous enable signal // // rf is the rfon input, assumed already synchronized to 40 MHz // // halt and trig_fallback are control bits set by the host // // write_pulse, host_* are the host interface, whereby the host // can read and write the 8 x 16-bit timing control registers. // write_pulse is assumed long compared to the 25 ns cycle, and // repeated writes to the same register file location are OK. // host_data must remain valid for 100 ns after write_pulse falls. // // Thus, this module is the big exception to the "cross clock domains // at 60 Hz" rule. While I tried to code and simulate this process // carefully, it is fundamentally less robust than I want. I think // I could improve the situation by using SRL16's, like in dac_testpattern.v, // along with some simple interlock/handshake logic to keep the contents // from being used during the load sequence. Note that that could, // potentially, keep a pulse from triggering, if the computer stalled // in the middle of reloading the table. Could I set up a double-buffer? `timescale 1ns / 1ns module rf_timer(ck, rstn, quad_enable, halt, rf, self_retrigger, ignore_rf_off, write_pulse, host_address, host_data, host_read, feedback_run, integrate_run, feedforward_start, trace_enable, adc_power, host_interrupt, rf_gate, trigger_skipped ); input ck, rstn, quad_enable, halt, rf, self_retrigger, ignore_rf_off, write_pulse; input [2:0] host_address; input [15:0] host_data; output [15:0] host_read; reg [15:0] host_read; output feedback_run, integrate_run, feedforward_start, trace_enable; reg feedback_run, integrate_run, feedforward_start, trace_enable; output adc_power, host_interrupt, rf_gate, trigger_skipped; reg adc_power, host_interrupt, rf_gate, trigger_skipped; // Give ourselves oodles of time for this logic, and make everything // 10 MHz synchronous. reg phase1, phase2, phase3, a_select; always @ (posedge ck or negedge rstn) if (~rstn) begin phase1 <= 1'b0; phase2 <= 1'b0; phase3 <= 1'b0; a_select <= 1'b0; end else begin phase1 <= quad_enable; phase2 <= phase1; phase3 <= phase2; // of course, because all these quantities are registered, // a_select ends up high during phase2 and phase3. a_select <= phase1 | phase2; end // States: state_advance: if (state_advance) reload: // 0 8.437ms Wait for trigger rf t2 // 1 1.434ms Idle t2==0 t1 // 2 0.001ms Idle 1 t2 // 3 0.030ms ADC warmup | t2==0 t2 // 4 0.020ms Feedforward only !rf | t2==0 t2 // 5 1.015ms Feedback !rf | t2==0 t2 // 6 0.030ms Acquire decay curve t2==0 // 7 5.700ms Inhibit retrigger t1==0 t1 // // But: if ignore_rf_off is set, states 1, 3, 4, and 5 continue to timer // expiration even when rf is false. // And if self_retrigger is set, advance out of state 0 if t1==0. // // In a live system, both those command flags would be cleared. // If you only have a short trigger pulse, that is low by the time // rf should be turned on, set ignore_rf_off. For triggerless bench // testing, set both ignore_rf_off and self_retrigger. // The state register is used so much, I choose a single letter for // its Verilog name: "s". This keeps the lines shorter. reg [2:0] s; reg [17:0] t1; reg [15:0] t2; reg [15:0] ttable[7:0]; reg [2:0] host_address_hold; reg old_rf; wire ext_trigger, timer_zero, timer_inhibit, rf_kill_advance, start_cycle, state_advance; assign ext_trigger = rf & ~old_rf; assign timer_zero = (s==0 | s==7) ? t1==0 : t2==0; assign timer_inhibit = (s==0) & ~self_retrigger; assign rf_kill_advance = (s==4 | s==5) & ~rf & ~ignore_rf_off; assign start_cycle = (s==0) & ext_trigger; assign state_advance = start_cycle | (timer_zero & !timer_inhibit) | (s==2) | rf_kill_advance; wire t1_reload, t2_reload; assign t1_reload = halt | state_advance & (s==1 | s==7); assign t2_reload = state_advance & (s==0 | s==2 | s==3 | s==4 | s==5); // The RAM address is multiplexed between host R/W, and // making the output available for time reloading. wire [2:0] ta; assign ta = a_select ? host_address_hold : s; reg table_write; always @ (posedge ck or negedge rstn) if (~rstn) begin old_rf <= 1'b0; s <= 3'b0; t1 <= 18'b0; t2 <= 16'b0; trigger_skipped <= 1'b0; end else if (phase1) begin old_rf <= rf; trigger_skipped <= (s!=0) & ext_trigger; if (state_advance | halt) s <= halt ? 0 : s+1; t1 <= t1_reload ? {ttable[ta],2'b00} : t1-1; t2 <= t2_reload ? ttable[ta] : t2-1; end always @ (posedge ck or negedge rstn) if (~rstn) begin host_read <= 16'b0; end else if (phase3) begin if (table_write) begin $display("table[%d] replaced %x with %x",ta,ttable[ta],host_data); ttable[ta] <= host_data; end host_read <= ttable[ta]; end always @ (posedge ck or negedge rstn) if (~rstn) begin table_write <= 1'b0; host_address_hold <= 3'b0; end else begin if (phase3 | write_pulse) table_write <= write_pulse; if (~table_write) host_address_hold <= host_address; end // It's OK to register all of these, and delay everything 100 ns. // That amount of time shift for any one signal is probably irrelevant, // and registering them all balances the delays and guarantees no glitches. always @ (posedge ck or negedge rstn) if (~rstn) begin rf_gate <= 1'b0; adc_power <= 1'b0; feedback_run <= 1'b0; integrate_run <= 1'b0; feedforward_start <= 1'b0; trace_enable <= 1'b0; host_interrupt <= 1'b0; end else if (phase1) begin rf_gate <= ( s==4 | s==5 ) & (rf | ignore_rf_off); adc_power <= s==3 | s==4 | s==5 | s==6; feedback_run <= s==5; integrate_run <= s==4 | s==5; feedforward_start <= s==3 & t2==0; trace_enable <= s==4 | s==5 | s==6; host_interrupt <= s==6 & t2==0; end `ifdef SIMULATE // Remember, t2 uses units of 100 ns, but t1 uses units of 400ns. initial begin feedforward_start=0; s=3'b0; table_write=0; ttable[0]=10; // (t2) length of state 1 (idle) ttable[1]=80; // (t1) time until end of state 7 ttable[2]=10; // (t2) length of state 3 (ADC warmup) ttable[3]=20; // (t2) length of state 4 (feedforward only) ttable[4]=45; // (t2) length of state 5 (feedback) ttable[5]=35; // (t2) length of state 6 (acquire decay curve) ttable[6]=45; // unused ttable[7]=55; // (t1) length of state 0 (wait for trigger) end `endif endmodule