-- trigger_burst.vhd -- bursts configurable bit patterns in configurable cycles -- Alexander Law (atlaw@ucsc.edu) -- August 2011 -- University of California, Santa Cruz library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_UNSIGNED.all; use ieee.numeric_std.all; entity trig_burst is generic( rptLen : integer := 16; -- bit length of trigger reitition counter rpt2Len : integer := 16; -- bit length of supercycle repitition counter waitLen : integer := 20; -- bit length of repeat cycle countdown timer wait2Len : integer := 20; -- bit length of supercycle countdown timer (VHDL integers have max. value 2**31-1) sqLenExp : integer := 3 -- exponent for bit length (2**sqLenExp) of trigger bit-sequence register ); port( --clock : in std_logic; -- global 40 MHz clock clock : in std_logic; -- global 80 MHz clock strobe40_i : in std_logic; -- 40MHz clock strobe - for 80MHz sync reset : in std_logic; -- global reset trigs_count_o : out std_logic_vector (15 downto 0); bursts_count_o : out std_logic_vector (15 downto 0); ready_o : out std_logic; running_o : out std_logic; finished_o : out std_logic; -- trigger_in : in std_logic; -- readout trigger giddyup_in : in std_logic; -- "go," since trigger might just mean "read config ports" seq_reset_i : in std_logic; -- config_in : in std_logic; -- if received concurrently with trigger, module reads and registers config inputs. rpt_in : in std_logic_vector (rptLen-1 downto 0); -- number of triggers within a burst -- *** all of our registers are 16b, so we need to make these values waitMin16_in : in std_logic_vector (15 downto 0); -- minimum number of clock ticks between triggers w/in a burst waitMax16_in : in std_logic_vector (15 downto 0); -- maximum number of clock ticks between triggers w/in a burst rpt2_in : in std_logic_vector (rpt2Len-1 downto 0); -- number of trigger bursts wait2_16_in : in std_logic_vector (15 downto 0); -- fixed number of clock ticks between bursts --sq_in : in std_logic_vector(2**sqLenExp-1 downto 0); --sqLen_in : in std_logic_vector(sqLenExp-1 downto 0); busy_i : in std_logic; -- input for back-pressure or flow control outBit_out : out std_logic; -- trigger signal output testFlag_out : out std_logic -- long-lived flag for oscilloscope inspection ); -- Declarations end trig_burst ; architecture rtl of trig_burst is -- *** >16b inputs move here signal waitMin_in : std_logic_vector(waitLen-1 downto 0); signal waitMax_in : std_logic_vector(waitLen-1 downto 0); signal wait2_in : std_logic_vector(wait2Len-1 downto 0); signal sq_in : std_logic_vector(2**sqLenExp-1 downto 0); signal sqLen_in : std_logic_vector(sqLenExp-1 downto 0) := "001"; signal configuring : std_logic; signal giddyup_q : std_logic; signal seq_reset_q : std_logic; signal waitMinReg, waitMinReg_next, dif, dif_next : integer range 0 to 2**waitLen-1 := 0; signal dif_slv : std_logic_vector(waitLen-1 downto 0) := (others => '0'); signal wait2reg, wait2reg_next, ctdn, ctdn_next : integer range 0 to 2**wait2Len-1 := 0; signal rptReg, rptReg_next, rpt, rpt_next : integer range 0 to 2**rptLen-1 := 0; signal rpt2, rpt2_next : integer range 0 to 2**rpt2Len-1 := 0; signal outBit, outBit_next : std_logic; signal testFlag_next : std_logic := '0'; -- testFlag_out should get testFlag_next on clock edge signal testFlag_ctdn, testFlag_ctdn_next : integer range 0 to 2**wait2Len-1; type states is (waiting, countdown, trigger, cycle_finished); signal state, nextState : states := cycle_finished; signal sq, sq_next : std_logic_vector(2**sqLenExp-1 downto 0) := (others => '0'); signal sqLen, sqLen_next : integer range 1 to 2**sqLenExp-1 := 1; -- has to have 1 bit of output, or what's the point? -- LFSR hardcoded to 32 bits because if you change the length, you have to change the XOR taps -- to avoid short repeat cycles, and that's too complicated to reconfigure in firmwre. signal LFSR, LFSR_next : std_logic_vector(31 downto 0) := x"5F0BE833"; signal jitterMask, jitterMask_next : std_logic_vector(waitLen-1 downto 0) := (others => '0'); signal jitterValid_ctdn, jitterValid_ctdn_next : integer range 0 to waitLen-1 := 0; signal jitter, jitter_next : integer range 0 to 2**30-1 := 0; -- range restricted due to VHDL integer type range limitation begin -- *** building the large registers from multiple 16b inputs waitMin_in <= waitMin16_in & "0000"; -- range 38Hz-40MHz, 2.5MHz steps waitMax_in <= waitMax16_in & "0000"; -- range 38Hz-40MHz, 2.5MHz steps --wait2_in <= wait2_in & "00000000"; -- range 25ns-0.4s, 6.2us steps, wait2_in <= wait2_16_in & "0000"; -- range 25ns-26ms, 400us steps, -- disabled pattern - sequencer is for that sqLen_in <= "001"; --tpattern_reg_in(sqLenExp-1+8 downto 8); sq_in <= x"01"; --tpattern_reg_in(2**sqLenExp-1 downto 0); -- *** need to register here to stretch giddy to the 40MHz domain giddyup_q <= giddyup_in when rising_edge(clock); seq_reset_q <= seq_reset_i when rising_edge(clock); -------- synchronous assignments ---------------- process(clock, reset, sqLen) begin if(reset = '1') then state <= cycle_finished; rptReg <= 0; waitMinReg <= 0; wait2reg <= 0; rpt <= 0; ctdn <= 0; rpt2 <= 0; outBit <= '0'; testFlag_out <= '0'; testFlag_ctdn <= 0; -- next 2 lines set default sequence to "000...001" sq(2**sqLenExp-1 downto 1) <= (others => '0'); sq(0) <= '1'; sqLen <= 1; LFSR <= x"01121971"; -- random. Init. value: doesn't particularly matter, just can't be all 0. jitterMask <= (others => '0'); jitterValid_ctdn <= 0; jitter <= 0; dif <= 0; elsif(clock'event and clock = '1')then if (strobe40_i = '0') then if (seq_reset_i = '1') or (seq_reset_q = '1') then state <= waiting; else state <= nextState; end if; rptReg <= rptReg_next; waitMinReg <= waitMinReg_next; wait2reg <= wait2reg_next; rpt <= rpt_next; ctdn <= ctdn_next; rpt2 <= rpt2_next; outBit <= outbit_next; testFlag_out <= testFlag_next; testFlag_ctdn <= testFlag_ctdn_next; sq <= sq_next; sqLen <= sqLen_next; LFSR <= LFSR_next; jitterMask <= jitterMask_next; jitterValid_ctdn <= jitterValid_ctdn_next; jitter <= jitter_next; dif <= dif_next; end if; end if; end process; ------------------------------------------------- --------------- persistent assignments ------------------- outBit_out <= outBit; dif_slv <= std_logic_vector(to_unsigned(dif, waitLen)); -------------------------------------------------------------- -------------- test flag logic ----------------------- -- raises a long-lived flag visible on 'scope/logic analyzer over full-burst timescales testFlag_ctdn_next <= waitMinReg/8 when outbit = '1' else testFlag_ctdn-1 when testFlag_ctdn > 0 else 0; testFlag_next <= '1' when testflag_ctdn > 0 else '0'; ------------------------------------------------------- ------------------ jitter mask/prng ------------------- -- linear feedback shift register for pseudo-random number generation -- jitter sampled by burster OR value out-of-range -- for 32-bit LFSR, maximal cycle obtained from tapping bits 1, 2, 22, 32 LFSR_next <= (LFSR(31) xor (LFSR(21) xor (LFSR(1) xor LFSR(0)))) & LFSR(31 downto 1) when ((state = trigger and ctdn = 0) or (jitter > dif and jitterValid_ctdn = 0)) else LFSR; -- LFSR mask: Only 1/2 of masked LFSR values (worst-case) are out-of-range. jitterMask_next <= (others => '0') when configuring <= '0' -- clear on reconfig. signal else dif_slv(waitLen-1) & (jitterMask(waitLen-1 downto 1) or dif_slv(waitLen-2 downto 0)); -- max-min difference should be valid on first clock after config trigger jitterValid_ctdn_next <= waitLen-1 when configuring = '1' else jitterValid_ctdn-1 when jitterValid_ctdn > 0 else 0; jitter_next <= to_integer(unsigned(LFSR(waitLen-1 downto 0) and jitterMask(waitLen-1 downto 0))); ------------------------------------------------------- process(rptReg, waitMinReg, wait2reg, giddyup_in, jitterValid_ctdn_next, rpt_in, waitMin_in, waitMax_in, rpt2_in, wait2_in, sq, sq_in, sqLen, sqLen_in, dif, state, --trigger_in, config_in, busy_i, jitter, rpt, ctdn, rpt2, giddyup_q, seq_reset_i, seq_reset_q ) begin ----- defaults ------- rptReg_next <= rptReg; waitMinReg_next <= waitMinReg; wait2reg_next <= wait2reg; rpt_next <= rpt; ctdn_next <= ctdn; rpt2_next <= rpt2; outBit_next <= '0'; sq_next <= sq; sqLen_next <= sqLen; dif_next <= dif; ready_o <= '0'; running_o <= '0'; finished_o <= '0'; -- *** because we don't use config_in, I need a way to update things that -- use it, meet "configuring" that is hi when waiting configuring <= '0'; ----------------------- case state is when waiting => configuring <= '1'; ready_o <= '1'; -- *** we already store these valuses, so I just update them until the sm moves on rptReg_next <= to_integer(unsigned(rpt_in)); waitMinReg_next <= to_integer(unsigned(waitMin_in)); dif_next <= to_integer(unsigned(waitMax_in)) - to_integer(unsigned(waitMin_in)); wait2reg_next <= to_integer(unsigned(wait2_in)); sq_next <= sq_in; sqLen_next <= to_integer(unsigned(sqLen_in)); rpt_next <= to_integer(unsigned(rpt_in)); ctdn_next <= to_integer(unsigned(waitMax_in)); rpt2_next <= to_integer(unsigned(rpt2_in)); -- *** don't need trigger_in now - we only have one "go" - giddyup --if(trigger_in = '1')then if(giddyup_in = '1') or (giddyup_q = '1') then --*** need giddy active for 2 80M cycles configuring <= '0'; nextState <= countdown; ctdn_next <= 32; -- 32 ticks before first readout ensures jitter mask has time to stabilize. else nextState <= waiting; ctdn_next <= 0; end if; --if(config_in = '1')then -- -- register config. values -- rptReg_next <= to_integer(unsigned(rpt_in)); -- waitMinReg_next <= to_integer(unsigned(waitMin_in)); -- dif_next <= to_integer(unsigned(waitMax_in)) - to_integer(unsigned(waitMin_in)); -- wait2reg_next <= to_integer(unsigned(wait2_in)); -- sq_next <= sq_in; -- sqLen_next <= to_integer(unsigned(sqLen_in)); -- rpt_next <= to_integer(unsigned(rpt_in)); -- ctdn_next <= to_integer(unsigned(waitMax_in)); -- rpt2_next <= to_integer(unsigned(rpt2_in)); --end if; ---- else current values persist --else -- nextState <= waiting; --end if; -- end if(trigger_in='1') when countdown => running_o <= '1'; -- should iherit ctdn = 32 from "waiting" state -- should inherit ctdn = waitmin + jitter from "trigger" state if(ctdn = 0 and busy_i = '0')then nextState <= trigger; ctdn_next <= sqLen-1; elsif(ctdn = 0)then nextState <= countdown; ctdn_next <= 0; else nextState <= countdown; ctdn_next <= ctdn -1; end if; when trigger => running_o <= '1'; outBit_next <= sq(ctdn); -- should inherit ctdn = sqLen-1; if(ctdn = 0 and rpt = 0 and rpt2 = 0)then --***nextState <= waiting; nextState <= cycle_finished; rpt_next <= 0; ctdn_next <= 0; rpt2_next <= 0; elsif(ctdn = 0 and rpt = 0)then nextState <= countdown; ctdn_next <= wait2reg; rpt_next <= rptReg; rpt2_next <= rpt2-1; elsif(ctdn = 0)then nextState <= countdown; ctdn_next <= waitMinReg + jitter; rpt_next <= rpt-1; rpt2_next <= rpt2; else ctdn_next <= ctdn-1; nextState <= trigger; end if; when cycle_finished => nextState <= cycle_finished; finished_o <= '1'; -- seq_reset now acts on the state variable directly - more control --if (seq_reset_i = '1') or (seq_reset_q = '1') then -- nextState <= waiting; --end if; end case; end process; trigs_count_o <= std_logic_vector(to_unsigned(rpt, 16)); bursts_count_o <= std_logic_vector(to_unsigned(rpt2, 16)); end rtl;