HierarchyFilesModulesSignalsTasksFunctionsHelp
// rf_timer.v
// LLRF Timing state machine
// $Id$
// Larry Doolittle, LBNL

// llc-suite Copyright (c) 2004, The Regents of the University of
// California, through Lawrence Berkeley National Laboratory (subject
// to receipt of any required approvals from the U.S. Dept. of Energy).
// All rights reserved.

// Your use of this software is pursuant to a "BSD-style" open
// source license agreement, the text of which is in license.txt
// (md5sum a1e0e81c78f6eba050b0e96996f49fd5) that should accompany
// this file.  If the license agreement is not there, or if you
// have questions about the license, please contact Berkeley Lab's
// Technology Transfer Department at TTD@lbl.gov referring to
// "llc-suite (LBNL Ref CR-1988)"

// Target: XC2S150 on SNS LLRF Digital board
// Initial coding: 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

// Input "clk40" is the 40 MHz acquisition clock.
//
// quad_sync 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?

// The totalizer_gate section is a total hack.  This feature should really
// be accomplished by sliding a couple more phases into the state machine,
// between ADC warmup and start of RF output.  Such a change would break
// software compatibility, and is therefore postponed.  All we do for
// now is construct a second, simpler timing system to handle just the
// totalizer.  Increase the address space from 8 to 16 words to leave
// space for configuring this new, temporary subsystem.
// The totalizer_gate pulse is always 1024 clock pulses long (25.6 us),
// and is delayed from the adc_power rising edge (beginning of main state 3)
// by (16*n+1)*0.1 us, where the 8-bit n is set by writing to
// address 0x00a0 (host_addr == 4'b1000).  Thus the delay allocated for
// ADC warmup before phase calibration can range from 0.1 us to 408.1 us.

`timescale 1ns / 1ns

[Up: llrf_fcm rf_timer]
module rf_timerIndex(
	input      rst,                // interconnect
	input      host_clk,           // interconnect
	input  [13:0] host_addr,       // interconnect
	input      host_we,            // interconnect
	input  [15:0] host_data,       // interconnect
	output reg [15:0] host_read,   // register TIMERS
	input      hselect,            // select   TIMERS
	input      clk40,              // interconnect
	input      quad_sync,          // interconnect
	output reg rf_on_sync,         // interconnect
	input [15:0] totalizer_delay,  // register TOTAL_DEL
	input [15:0] config_word,      // register CONFIG
	output     feedback_enable,    // interconnect
	output     integrate_enable,   // interconnect
	output reg feedforward_start,  // interconnect
	output reg host_interrupt,     // interconnect
	output     trace_enable,       // interconnect
	output reg trigger_skipped,    // interconnect
	output reg totalizer_gate,     // interconnect
	output     dds_run,            // interconnect
	output     dds_substitute,     // interconnect
	output     totalizer_channel,  // interconnect
	output     phase_sense_adjust, // interconnect

	input  rf_on_,                 // interconnect
	output rf_kill_,               // interconnect
	output power_down,             // interconnect
	output LED3,                   // pin d
	output LED4                    // pin e
);

wire write_pulse = host_we & hselect;

wire   trace_select       = config_word[0];
assign phase_sense_adjust = config_word[1];   // only used in MEBT configuration
                  // unused config_word[2];
wire   pulse_run          = config_word[3];   // initial condition: 0 means don't run until told to!
wire   ignore_rf_off      = config_word[4];
wire   self_retrigger     = config_word[5];
                  // unused config_word[6];
                  // unused config_word[7];
                  // unused config_word[8];
wire   feedback_select    = config_word[9];
wire   integrate_select   = config_word[10];
wire   rf_permit          = config_word[11];
wire   cw_request         = config_word[12];
wire   dds_passthru       = config_word[13];
assign totalizer_channel  = config_word[14];
wire   raw_dds_select     = config_word[15];   // simply passed to interconnect

reg adc_power;
assign power_down = ~adc_power;
wire   halt = ~pulse_run;
assign dds_run = adc_power | dds_passthru;
assign rf_kill_ = ~rf_permit;
assign LED3 = ~adc_power;
assign LED4 = halt;

reg feedback_run, integrate_run, trace_run;
assign  feedback_enable   =   feedback_run  &  feedback_select;
assign integrate_enable   =  integrate_run  & integrate_select;
wire     raw_dds_enable   =   feedback_run  &   raw_dds_select;
assign     trace_enable   =      trace_run  &     trace_select;
assign   dds_substitute   =    dds_passthru | raw_dds_enable;

always @(posedge clk40) rf_on_sync <= rf_on_;
wire rf = rf_on_sync;

reg [2:0] host_address_hold1;
always @(posedge host_clk) if (hselect) host_address_hold1 <= host_addr[2:0];

// Give ourselves oodles of time for this logic, and make everything
// 10 MHz synchronous.
reg phase1, phase2, phase3, a_select;
always @ (posedge clk40 or posedge rst) if (rst) begin
	phase1 <= 1'b0;
	phase2 <= 1'b0;
	phase3 <= 1'b0;
	a_select <= 1'b0;
end else begin
	phase1 <= quad_sync;
	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;
reg cw_op;

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 clk40 or posedge rst) if (rst) 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 clk40 or posedge rst) if (rst) begin
	host_read <= 16'b0;
end else if (phase3) begin
	host_read <= ttable[ta];
end
// Can't reset a register file
always @ (posedge clk40) if (phase3 & table_write) begin
	$display("table[%d] replaced %x with %x",ta,ttable[ta],host_data);
	ttable[ta] <= host_data;
end

always @ (posedge clk40 or posedge rst) if (rst) 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_hold1;
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 clk40 or posedge rst) if (rst) begin
	adc_power         <= 1'b0;
	feedback_run      <= 1'b0;
	integrate_run     <= 1'b0;
	feedforward_start <= 1'b0;
	trace_run         <= 1'b0;
	host_interrupt    <= 1'b0;
	cw_op             <= 1'b0;
end else if (phase1) begin
	adc_power         <= s==3 | s==4 | s==5 | s==6 | cw_op;
	feedback_run      <= s==5 | cw_op;
	integrate_run     <= s==4 | s==5 | cw_op;
	feedforward_start <= s==3 & t2==0 & ~cw_op;
	trace_run         <= s==4 | s==5 | s==6;
	host_interrupt    <= s==6 & t2==0;
	if (s==4 & t2==0 ) cw_op <= cw_request;
end

reg [1:0] totalizer_mode;
reg [11:0] totalizer_counter;
wire totalizer_mode_step = ~totalizer_mode[1] ?
	( ~totalizer_mode[0] ? s==3 : totalizer_counter==0 ) :
	( ~totalizer_mode[0] ? totalizer_counter==0 : s==2 ) ;
wire [11:0] totalizer_load = ~totalizer_mode[0] ? {totalizer_delay[7:0],{4{1'b0}}} : 12'h100 ;
always @(posedge clk40 or posedge rst) if (rst) begin
	totalizer_mode    <= 2'b0;
	totalizer_gate    <= 1'b0;
	totalizer_counter <= 12'b0;
end else begin
	if (phase1 & totalizer_mode_step) totalizer_mode <= totalizer_mode + 1'b1;
	if (phase1 & (totalizer_mode_step | (totalizer_mode==1) | (totalizer_mode==2)))
		totalizer_counter <=
		totalizer_mode_step ? totalizer_load : totalizer_counter-1;
	totalizer_gate    <= totalizer_mode==2;
end

`ifdef SIMULATE
// Remember, t2 uses units of 100 ns, but t1 uses units of 400ns.
initial begin
	// totalizer_delay=8'd13;  // 13*1.6 = 20.8 us delay
	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

HierarchyFilesModulesSignalsTasksFunctionsHelp

This page: Maintained by: ldoolitt@recycle.lbl.gov
Created:Wed May 19 11:23:19 2004
From: ../source/rf_timer.v

Verilog converted to html by v2html 7.30 (written by Costas Calamvokis).Help