-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- + Indiana University Cyclotron Facility + -- + STAR Endcap Electromagnetic Calorimeter - MAPMT Readout + -- + Readout Board FPGA + -- + G. Visser - gvisser@iucf.indiana.edu - 812 855 7880 + -- + file created 2/5/2003 + -- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- $Id: rdo.vhd,v 1.11 2005/03/18 03:55:08 gvisser Exp $ library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; -- numeric_std could replace std_logic_arith and std_logic_unsigned combination, -- but appears to fail for simulation.....??????? --use IEEE.numeric_std.all; library work; use work.miscellaneous.all; use work.mapmt_system.all; library unisim; use unisim.vcomponents.all; entity rdo is port ( -- configuration and config memory interface done_n: out std_logic; cmcs_n,cmclk,cmdin_n: out std_logic; cmdout: in std_logic; -- clock and timing, and trigger command input tcd_rs,tcd_5x_rs: in std_logic; tcd_d: in std_logic_vector(3 downto 0); clk,clk_div4,lock: in std_logic; local_tcd_n,qrsten: out std_logic; dlycs_n,dlysclk,dlydin: out std_logic; osc: in std_logic; localclk: out std_logic; -- slow controls interface sc_a: in std_logic_vector(16 downto 1); sc_d: inout std_logic_vector(15 downto 0); sc_write_n,sc_cs3_n,sc_ds_n: in std_logic; sc_dtack_n: out std_logic; -- hotlink data output fock,fodv_n,fosc,fosvs: out std_logic; fod: out std_logic_vector(7 downto 0); -- fee bus A (to fee assemblies #0 - #5) feed_a: inout std_logic_vector(7 downto 0); cmd_a: out std_logic_vector(4 downto 0); xclk_a,write_a_n: out std_logic; en_a_n,cmdv_a_n: out std_logic_vector(5 downto 0); -- fee bus B (to fee assemblies #6 - #11) feed_b: inout std_logic_vector(7 downto 0); cmd_b: out std_logic_vector(4 downto 0); xclk_b,write_b_n: out std_logic; en_b_n,cmdv_b_n: out std_logic_vector(11 downto 6); -- miscellaneous gsr_n: in std_logic; moncs_n,monsclk,mondin: out std_logic; mondout: in std_logic; led_r,led_g: out std_logic; spare_a,spare_b: out std_logic ); attribute period: string; attribute loc: string; attribute slew: string; attribute keeper: string; attribute pullup: string; attribute iostandard: string; attribute drive: string; attribute nodelay: string; attribute clock_buffer: string; attribute period of clk: signal is "22.7ns"; -- really 4*10MHz is req'd, but use 4*11MHz to be sure attribute period of tcd_5x_rs: signal is "18.1 ns"; -- really 5*10MHz is req'd, but use 5*11MHz... attribute period of osc: signal is "25 ns"; attribute loc of done_n: signal is "P107"; attribute loc of cmcs_n: signal is "P86"; attribute drive of cmcs_n: signal is "24"; -- investigate - is this necessary? attribute loc of cmclk: signal is "P88"; attribute drive of cmclk: signal is "24"; -- investigate - this seems necessary... why...? attribute loc of cmdin_n: signal is "P87"; attribute drive of cmdin_n: signal is "24"; -- investigate - is this necessary? attribute loc of cmdout: signal is "P153"; attribute loc of tcd_rs: signal is "P84"; attribute nodelay of tcd_rs: signal is "true"; attribute clock_buffer of tcd_rs: signal is "ibuf"; attribute loc of tcd_5x_rs: signal is "P80"; attribute loc of tcd_d: signal is "P102 P101 P100 P99"; attribute nodelay of tcd_d: signal is "true"; attribute loc of clk: signal is "P185"; attribute loc of clk_div4: signal is "P74"; attribute loc of lock: signal is "P73"; attribute loc of local_tcd_n: signal is "P75"; attribute loc of qrsten: signal is "P82"; attribute loc of dlycs_n: signal is "P94"; attribute loc of dlysclk: signal is "P95"; attribute loc of dlydin: signal is "P96"; attribute loc of osc: signal is "P77"; attribute loc of localclk: signal is "P81"; attribute drive of localclk: signal is "24"; attribute loc of sc_a: signal is "P181 P180 P179 P178 P176 P175 P174 P173 P172 P168 P167 P166 P165 P164 P163 P162"; attribute loc of sc_d: signal is "P206 P205 P204 P203 P202 P201 P200 P199 P195 P194 P193 P192 P191 P189 P188 P187"; -- attribute keeper of sc_d: signal is "YES"; attribute loc of sc_write_n: signal is "P148"; attribute loc of sc_cs3_n: signal is "P150"; attribute loc of sc_ds_n: signal is "P151"; attribute loc of sc_dtack_n: signal is "P152"; attribute loc of fock: signal is "P58"; attribute slew of fock: signal is "FAST"; attribute loc of fodv_n: signal is "P57"; attribute loc of fosc: signal is "P59"; attribute loc of fosvs: signal is "P60"; attribute loc of fod: signal is "P61 P62 P63 P67 P68 P69 P70 P71"; attribute loc of feed_a: signal is "P30 P31 P33 P34 P35 P36 P37 P41"; attribute nodelay of feed_a: signal is "true"; attribute loc of cmd_a: signal is "P21 P22 P23 P24 P27"; attribute loc of xclk_a: signal is "P29"; attribute loc of write_a_n: signal is "P42"; attribute loc of en_a_n: signal is "P14 P16 P18 P44 P46 P48"; attribute loc of cmdv_a_n: signal is "P10 P15 P17 P43 P45 P47"; attribute loc of feed_b: signal is "P125 P126 P127 P129 P132 P133 P134 P135"; attribute nodelay of feed_b: signal is "true"; attribute loc of cmd_b: signal is "P115 P119 P120 P121 P122"; attribute loc of xclk_b: signal is "P123"; attribute loc of write_b_n: signal is "P136"; attribute loc of en_b_n: signal is "P108 P110 P112 P138 P140 P142"; attribute loc of cmdv_b_n: signal is "P109 P111 P113 P139 P141 P146"; attribute loc of gsr_n: signal is "P160"; attribute pullup of gsr_n: signal is "true"; attribute loc of moncs_n: signal is "P4"; attribute loc of monsclk: signal is "P3"; attribute loc of mondin: signal is "P5"; attribute loc of mondout: signal is "P6"; attribute loc of led_r: signal is "P98"; attribute loc of led_g: signal is "P97"; attribute loc of spare_a: signal is "P20"; attribute loc of spare_b: signal is "P114"; attribute iostandard of done_n,cmcs_n,cmclk,cmdin_n,cmdout,tcd_rs,tcd_5x_rs,tcd_d,clk,clk_div4,lock, local_tcd_n,qrsten,dlycs_n,dlysclk,dlydin,sc_a,sc_d,sc_write_n,sc_cs3_n,sc_ds_n,sc_dtack_n, fock,fodv_n,fosc,fosvs,fod,feed_a,cmd_a,xclk_a,write_a_n,en_a_n,cmdv_a_n, feed_b,cmd_b,xclk_b,write_b_n,en_b_n,cmdv_b_n,moncs_n,monsclk,mondin,mondout, led_r,led_g,spare_a,spare_b: signal is "LVTTL"; end rdo; architecture rdo_0 of rdo is component ROM32X1 is generic(INIT: string); port(A0,A1,A2,A3,A4: in std_logic; O: out std_logic); end component; -- NOTE: the signal 'gsr' is here to cause the compiler to infer the initialization state of FF's, but is -- not explicitly driven in the real hardware. (Effect of GSR at end of configuration is controlled by this -- mechanism.) However, 'gsr' must NOT be driven '0' in the VHDL source - let the compiler optimize it away -- after it happily infers the desired initialization states. Setting it to '0' does NOT work! signal gsr: std_logic; type led_state_type is (off, red, green, yellow, flashred, flashgreen, flashyellow); signal led_state: led_state_type; signal flash,fastflash: std_logic; signal cycle: std_logic_vector(3 downto 0); signal trig_word: std_logic_vector(19 downto 0); signal tcd_cmd_trig,equ_tcd_cmd_trig_and_cyc2, tcd_cmd_testPulse,equ_tcd_cmd_testPulse_and_cyc2,clr_busy: std_logic; signal trig_cmd_captured: std_logic_vector(3 downto 0); signal token_captured: std_logic_vector(11 downto 0); signal feeConfigXclkInUse,feeConfigDone: std_logic; signal fee_din_pre,fee_cclk_pre,fee_prog_n_pre: std_logic; signal feed_a_int,feed_b_int,feed_pre: std_logic_vector(feed_a'range); signal feed_enable_n,feed_enable_pre: std_logic; signal boxName: std_logic_vector(31 downto 0); signal readoutUnitId: std_logic_vector(7 downto 0); signal csr: std_logic_vector(15 downto 0); signal shadow_lock_err: std_logic; signal ptLatency: std_logic_vector(6 downto 0); -- pulser to readout trigger latency register - 7 bits signal ltLatency: std_logic_vector(6 downto 0); -- LED to readout trigger latency register - 7 bits signal fee_read,fee_write,fee_finish,csr_read,csr_write,boxName_read, readoutUnitId_read,ptLatency_write,ptLatency_read,ltLatency_write,ltLatency_read, version_write,version_read,dly_write,dly_finish,mon_read,mon_finish, eventLogCount_read,eventLog_clear,eventLog_read,rs_period_write,rs_period_read: std_logic; signal sc_din_reg,sc_dout_pre: std_logic_vector(sc_d'range); signal sc_a_reg: std_logic_vector(sc_a'range); signal feed_a_reg,feed_b_reg,feed_reg_selected: std_logic_vector(7 downto 0); signal feeBusBusy,send_feeCmd_addr,send_feeCmd_read,send_feeCmd_write: std_logic; signal feeModuleAddr,feeModuleAddr_reg: UNSIGNED(3 downto 0); signal readoutFeeAddr: UNSIGNED(5 downto 0); signal readoutRun: std_logic; signal feeModuleSelect: std_logic_vector(0 to 11); signal skipper: integer range 0 to 63; begin gsr <= not gsr_n; -- NOTE: gsr is NOT connected on board; normally never asserted done_n <= '0'; -- releases configuration memory chip after configuration -- status LED output with led_state select led_g <= '0' when off | red | flashred, '1' when green, flash when flashgreen, fastflash when yellow, flash and fastflash when flashyellow; with led_state select led_r <= '0' when off | green | flashgreen, '1' when red, flash when flashred, not fastflash when yellow, flash and not fastflash when flashyellow; led_state <= flashgreen when csr(0)='1' else red when feeConfigDone='0' else flashyellow when lock='0' else green when csr(1)='0' else off; -- changed from cycle(0) to cycle(2) to accompany fix of ADCCLK timing w.r.t. acquisition buffer qrsten <= cycle(2); -- I think we want to move this to cycle(3)??? - change FEE FPGA... spare_b <= cycle(2) and bool2std(skipper=0); -- put skipper=0 condition in qrsten for reset-skipping mode process(clk,gsr) begin if gsr='1' then skipper <= 0; elsif clk'event and clk='1' then if cycle(2)='1' then skipper <= skipper-1; end if; end if; end process; -- for kluge use of old (REV - ) readout board in local oscillator mode (for slow controls development -- work), just comment out the local oscillator divider circuit (losing localclk and flasher outputs), -- comment out the clock period constraint on osc input (OR ELSE COMPILER WILL CRASH!) and -- tie local_tcd_n <= '1' local_tcd_n <= csr(2); -- ++++++++++++++++++++++++++++ -- + local oscillator divider + -- ++++++++++++++++++++++++++++ lc: block signal x: integer range 0 to 2**23-1; begin process(osc,gsr) begin if gsr='1' then x <= 0; localclk <= '0'; elsif osc'event and osc='1' then x <= x+1; localclk <= bool2std( (x mod 4)/2 = 0 ); end if; end process; flash <= bool2std( x/(2**22) = 0 ); fastflash <= bool2std( (x/(2**15)) mod 2 = 0 ); end block lc; -- +++++++++++++++++++++++++ -- + clock cycle sequencer + -- +++++++++++++++++++++++++ -- This generates the one-hot clock cyle identifier 'cycle' = 0001,0010,0100,1000. -- The relation to the system clock signals is: -- RHIC strobe input _____-------\\\\\\\\\____________ -- RHIC strobe delayed -----______-------\\\\\\\\\___________ -- clk edge name 0 1 2 3 0 1 -- clk (2QB1 on CY7B9930V) ____----____----____----____----____----____---- -- clk_div4 (QFA0 on CY7B9930V) ____----------------________________------------ -- clk_div4_reg ________----------------________________-------- -- clk_div4_reg_reg ________________----------------____________---- -- cycle(3 downto 0) 0001 | 0010 | 0100 | 1000 | 0001 | 0010 | clk_cyc: block signal clk_div4_reg,clk_div4_reg_reg: std_logic; begin process(clk,gsr) begin if gsr='1' then -- careful - don't make a spurious initial pulse on cycle(0) clk_div4_reg <= '1'; clk_div4_reg_reg <= '1'; elsif clk'event and clk='0' then -- NOTE FALLING EDGE CLOCK HERE clk_div4_reg <= clk_div4; clk_div4_reg_reg <= clk_div4_reg; end if; end process; process(clk,gsr) begin if gsr='1' then cycle <= "0000"; elsif clk'event and clk='1' then cycle(0) <= (not clk_div4_reg) and (clk_div4_reg_reg); cycle(1) <= cycle(0); cycle(2) <= cycle(1); cycle(3) <= cycle(2); end if; end process; end block clk_cyc; -- ++++++++++++++++++++++++++ -- + trigger/clock receiver + -- ++++++++++++++++++++++++++ -- This block reads the trigger command and token data and puts it on 'trig_word'; this occurs in response -- to clock edge #2. 'trig_word' remains valid until the next clock edge #2, but in general the intention -- is to execute the command using clock edge #3, that is, qualified by cycle(3). tcd_receiver: block signal tcd_rs_reg,tcd_rs_reg_reg,busy,waiting,tcd_cmd_ledPulse: std_logic; signal tcd_d_reg: std_logic_vector(23 downto 0); signal trig_word_rs5f: std_logic_vector(19 downto 0); signal trigDelayTimer: integer range 0 to 127; begin process(tcd_5x_rs,gsr) begin if gsr='1' then tcd_rs_reg <= '0'; tcd_rs_reg_reg <= '0'; tcd_d_reg <= X"000000"; trig_word_rs5f <= X"00000"; elsif tcd_5x_rs'event and tcd_5x_rs='0' then -- NOTE FALLING EDGE USED HERE! tcd_rs_reg <= tcd_rs; tcd_rs_reg_reg <= tcd_rs_reg; tcd_d_reg(3 downto 0) <= tcd_d; for i in 1 to 5 loop tcd_d_reg(3+4*i downto 4*i) <= tcd_d_reg(3+4*(i-1) downto 4*(i-1)); end loop; if ((tcd_rs_reg='1') and (tcd_rs_reg_reg='0')) then trig_word_rs5f <= tcd_d_reg(23 downto 4); -- note that tcd_d_reg(3 downto 0) is the NEXT trig cmd! -- (but we don't do anything with that) end if; end if; end process; -- here is the one place where data moves from tcd_5x_rs clock domain to clk clock domain... we should -- protect with suitable timespec, but that may only be possible in ucf file??? anyway, come back to -- this issue later... be careful here! -- setup time here should be = t_box_delay+(1/5)*t_cycle-t_cko(from the ff above) +/- t_skew's process(clk,gsr) begin if gsr='1' then trig_word <= X"00000"; elsif clk'event and clk='1' then if cycle(2)='1' then trig_word <= trig_word_rs5f; end if; end if; end process; -- busy FF and trigger command decoding (including test pulse to trigger latency, and LED pulse to -- trigger latency) process(clk,gsr) begin if gsr='1' then busy <= '0'; trigDelayTimer <= 0; waiting <= '0'; elsif clk'event and clk='1' then busy <= ((tcd_cmd_trig or tcd_cmd_testPulse or tcd_cmd_ledPulse) and cycle(2)) -- note cycle(2) AS ABOVE or (busy and not clr_busy); if cycle(2)='1' then -- again, should be same cycle as above... if tcd_cmd_testPulse='1' then trigDelayTimer <= CONV_INTEGER(UNSIGNED(ptLatency)); waiting <= '1'; elsif tcd_cmd_ledPulse='1' then trigDelayTimer <= CONV_INTEGER(UNSIGNED(ltLatency)); waiting <= '1'; elsif trigDelayTimer /= 0 then trigDelayTimer <= trigDelayTimer-1; else waiting <= '0'; end if; -- capture trig cmd and token that we're responding to... (protect by busy!) if ((busy='0') and ((trig_word(19 downto 16)=X"4") or (trig_word(19 downto 16)=X"8") or (trig_word(19 downto 16)=X"9"))) then trig_cmd_captured <= trig_word(19 downto 16); token_captured <= trig_word(11 downto 0); end if; end if; end if; end process; tcd_cmd_trig <= ((not busy) and bool2std(trig_word(19 downto 16)=X"4")) or (waiting and bool2std(trigDelayTimer=0)); -- note this is NOT protected by busy! tcd_cmd_testPulse <= (not busy) and bool2std(trig_word(19 downto 16)=X"8"); tcd_cmd_ledPulse <= (not busy) and bool2std(trig_word(19 downto 16)=X"9"); -- This is a period counter to measure the input RHIC strobe period (the direct input, not the delayed -- and PLL-regenerated clock), relative to the 40 MHz oscillator, for diagnostic purposes. rs_period_counter: block signal div: std_logic_vector(27 downto 0); signal count_bin: UNSIGNED(31 downto 0); signal count_gray,rs_period_count_pre,rs_period_count: std_logic_vector(count_bin'range); begin process(osc) begin if osc'event and osc='1' then count_bin <= count_bin+CONV_UNSIGNED(1,count_bin'high+1); count_gray(count_bin'high) <= count_bin(count_bin'high); for i in count_bin'high-1 downto 0 loop count_gray(i) <= count_bin(i+1) xor count_bin(i); end loop; end if; end process; process(tcd_rs) begin if tcd_rs'event and tcd_rs='1' then div(0) <= not div(0); end if; end process; ripple: for i in 1 to div'high generate process(div(i-1)) begin if div(i-1)'event and div(i-1)='1' then div(i) <= not div(i); end if; end process; end generate; process(div(div'high)) begin if div(div'high)'event and div(div'high)='1' then rs_period_count_pre <= count_gray; end if; end process; process(clk,gsr) -- to synchronously read the period count, make a dummy write, then begin -- this here will capture the value synchronously if gsr='1' then rs_period_count <= X"00000000"; elsif clk'event and clk='1' then if rs_period_write='1' then rs_period_count <= rs_period_count_pre; end if; end if; end process; sc_dout_pre <= rs_period_count(31 downto 16) when (rs_period_read and not sc_a_reg(1))='1' else "ZZZZZZZZZZZZZZZZ"; sc_dout_pre <= rs_period_count(15 downto 0) when (rs_period_read and sc_a_reg(1))='1' else "ZZZZZZZZZZZZZZZZ"; end block rs_period_counter; spare_a <= busy; end block tcd_receiver; -- ++++++++++++++++++++++++ -- + data readout machine + -- ++++++++++++++++++++++++ -- Here we gather data from the FEE boards in a "chained block transfer"; this here logic expects that the -- right FEE boards are sending the data at the right times, and simply pulls data off at the right times -- and sends it to the hotlink transmitter... If the data's not there, garbage will go in its place... -- ------------------------- -- to do still: feeBusBusy (or another such) should protect the fee bus during fee configuration as well... rdo: block constant cycbase: integer range 0 to 3 := 1; -- tune this and delay_count to match fee data timing type dx_type is (dx_idle,dx_delay,dx_header,dx_data,dx_blank,dx_trailer); signal dx: dx_type; signal ix: integer range 0 to 255; -- CAREFUL WITH THE SIZE... constant delay_count: integer := 11; -- number of K28.5 bytes to send at start constant header_count: integer := 10; -- number of header bytes (including the C1.0 and C2.0) constant fee_count: integer := 48; -- number of FEE boards constant bytes_per_fee: integer := 8; type sc_table_type is array(natural range <>) of std_logic; type d_table_type is array(natural range <>) of std_logic_vector(7 downto 0); constant header_sc_table: sc_table_type(0 to header_count-1) := ( '1', '0', '0', '0', '0', '0', '0', '0', '0', '1'); signal header_d_table: d_table_type(0 to header_count-1); signal fod_pre: std_logic_vector(7 downto 0); signal fosc_pre,fodv_pre_data: std_logic; begin header_d_table(0) <= X"01"; header_d_table(1) <= X"00"; header_d_table(2) <= X"00"; header_d_table(3) <= X"00"; header_d_table(4) <= X"00"; header_d_table(9) <= X"02"; header_d_table(5) <= token_captured(7 downto 0); header_d_table(6) <= X"0"&token_captured(11 downto 8); header_d_table(7) <= readoutUnitId; header_d_table(8) <= X"0"&trig_cmd_captured(3 downto 0); process(clk,gsr) begin if gsr='1' then fock <= '0'; fod <= X"00"; fosc <= '0'; fodv_n <= '1'; fodv_pre_data <= '0'; dx <= dx_idle; ix <= 0; readoutFeeAddr <= "000000"; feeBusBusy <= '0'; elsif clk'event and clk='1' then fock <= cycle((cycbase+0) mod 4) or cycle((cycbase+2) mod 4); -- RS*2 output on fock if ((cycle((cycbase+1) mod 4)='1') or (cycle((cycbase+3) mod 4)='1')) then fod <= fod_pre; fosc <= fosc_pre; fodv_n <= not (bool2std(dx=dx_header) or fodv_pre_data or bool2std(dx=dx_trailer)); end if; if ((cycle((cycbase+0) mod 4)='1') or (cycle((cycbase+2) mod 4)='1')) then case dx is when dx_idle => if ((cycle((cycbase+0) mod 4)='1') and (tcd_cmd_trig='1')) then dx <= dx_delay; ix <= 0; feeBusBusy <= '1'; else feeBusBusy <= '0'; end if; when dx_delay => if ix=delay_count-1 then dx <= dx_header; ix <= 0; else ix <= ix+1; end if; when dx_header => if ix=header_count-1 then dx <= dx_data; ix <= 0; readoutFeeAddr <= "000000"; else ix <= ix+1; end if; when dx_data => if ix=bytes_per_fee-1 then if readoutFeeAddr=CONV_UNSIGNED(fee_count-1,6) then dx <= dx_blank; else ix <= 0; readoutFeeAddr <= readoutFeeAddr+"000001"; end if; else ix <= ix+1; end if; when dx_blank => -- during this cycle data last data item goes to hotlink dx <= dx_trailer; when dx_trailer => -- send trailer (C3.0) to hotlink dx <= dx_idle; end case; fodv_pre_data <= bool2std(dx=dx_data); end if; end if; end process; fosvs <= '0'; with dx select fod_pre <= header_d_table(ix) when dx_header, X"03" when dx_trailer, feed_reg_selected when others; with dx select fosc_pre <= header_sc_table(ix) when dx_header, '1' when dx_trailer, '0' when others; readoutRun <= bool2std(dx/=dx_idle); clr_busy <= bool2std(dx=dx_trailer); -- a suitable point to be un-busy... could revisit this... end block rdo; -- +++++++++++++++++++++ -- + FEE bus interface + -- +++++++++++++++++++++ -- The two FF's (equ_tcd_cmd_testPulse_and_cyc2 and equ_tcd_cmd_testPulse_and_cyc2) here are added to -- improve the timing (which otherwise would not meet constraints, on cmd_a/b and cmdv_a/b FF's which are -- clocked by negative clk edge, note). These two FF's are equivalent to tcd_cmd_trig, respectively -- tcd_cmd_testPulse, anded with cycle(2). process(clk,gsr) begin if gsr='1' then equ_tcd_cmd_trig_and_cyc2 <= '0'; equ_tcd_cmd_testPulse_and_cyc2 <= '0'; elsif clk'event and clk='1' then equ_tcd_cmd_trig_and_cyc2 <= tcd_cmd_trig and cycle(1); equ_tcd_cmd_testPulse_and_cyc2 <= tcd_cmd_testPulse and cycle(1); end if; end process; process(clk,gsr) begin if gsr='1' then cmd_a <= feeCmd_opcode(feeCmd_sync); cmd_b <= feeCmd_opcode(feeCmd_sync); cmdv_a_n <= "111111"; cmdv_b_n <= "111111"; en_a_n <= "111111"; en_b_n <= "111111"; write_a_n <= '1'; write_b_n <= '1'; feed_a_int <= X"00"; feed_b_int <= X"00"; feed_enable_n <= '1'; elsif clk'event and clk='0' then -- NOTE FALLING EDGE used here, for good timing at FEE FPGA input registers if feeConfigDone='1' then -- this is not quite right... we need to wait one or two clicks after asserting feeBusBusy before -- we actually send the feeCmd_trigger out... add a small fixed delay in responding to trigger -- command from TCD before sending to FEE modules... if equ_tcd_cmd_trig_and_cyc2='1' then cmd_a <= feeCmd_opcode(feeCmd_trigger); cmd_b <= feeCmd_opcode(feeCmd_trigger); elsif equ_tcd_cmd_testPulse_and_cyc2='1' then cmd_a <= feeCmd_opcode(feeCmd_pulse); cmd_b <= feeCmd_opcode(feeCmd_pulse); elsif send_feeCmd_addr='1' then cmd_a <= feeCmd_opcode(feeCmd_addr); cmd_b <= feeCmd_opcode(feeCmd_addr); elsif send_feeCmd_read='1' then cmd_a <= feeCmd_opcode(feeCmd_read); cmd_b <= feeCmd_opcode(feeCmd_read); elsif send_feeCmd_write='1' then cmd_a <= feeCmd_opcode(feeCmd_write); cmd_b <= feeCmd_opcode(feeCmd_write); else cmd_a <= feeCmd_opcode(feeCmd_sync); cmd_b <= feeCmd_opcode(feeCmd_sync); end if; else cmd_a <= fee_din_pre & fee_cclk_pre & '0' & fee_prog_n_pre & '0'; -- power active cmd_b <= fee_din_pre & fee_cclk_pre & '0' & fee_prog_n_pre & '0'; -- power active end if; for i in 0 to 5 loop cmdv_a_n(i) <= not (feeConfigXclkInUse -- assert 'em all when using xclk or equ_tcd_cmd_trig_and_cyc2 -- global to all modules or equ_tcd_cmd_testPulse_and_cyc2 -- global to all modules or (feeModuleSelect(i) and (send_feeCmd_addr or send_feeCmd_read or send_feeCmd_write)) or (cycle(0) -- send sync command on cycle(0) whenever possible and not (send_feeCmd_addr or send_feeCmd_read or send_feeCmd_write))); en_a_n(i) <= not feeModuleSelect(i); end loop; for i in 6 to 11 loop cmdv_b_n(i) <= not (feeConfigXclkInUse -- assert 'em all when using xclk or equ_tcd_cmd_trig_and_cyc2 -- global to all modules or equ_tcd_cmd_testPulse_and_cyc2 -- global to all modules or (feeModuleSelect(i) and (send_feeCmd_addr or send_feeCmd_read or send_feeCmd_write)) or (cycle(0) -- send sync command on cycle(0) whenever possible and not (send_feeCmd_addr or send_feeCmd_read or send_feeCmd_write))); en_b_n(i) <= not feeModuleSelect(i); end loop; -- do not write to FEE assemblies unless feeConfigDone='1' write_a_n <= not (feeConfigDone and (send_feeCmd_addr or send_feeCmd_write)); write_b_n <= not (feeConfigDone and (send_feeCmd_addr or send_feeCmd_write)); feed_a_int <= feed_pre; feed_b_int <= feed_pre; feed_enable_n <= not feed_enable_pre; end if; end process; -- feeModuleAddr selects the bus interface chips and cmdv_n signals (both for read from and for write to FEE -- assemblies); feeModuleAddr_reg applies to the registered input data (feed_a_reg/feed_b_reg) selection feeModuleAddr <= readoutFeeAddr(5 downto 2) when readoutRun='1' else UNSIGNED(sc_a_reg(12 downto 9)); with feeModuleAddr select feeModuleSelect <= X"800" when X"0", X"400" when X"1", X"200" when X"2", X"100" when X"3", X"080" when X"4", X"040" when X"5", X"020" when X"6", X"010" when X"7", X"008" when X"8", X"004" when X"9", X"002" when X"a", X"001" when X"b", X"000" when others; feed_pre <= sc_a_reg(8 downto 1) when send_feeCmd_addr='1' else sc_din_reg(7 downto 0) when send_feeCmd_write='1' else X"00"; feed_enable_pre <= send_feeCmd_addr or send_feeCmd_write; feed_a <= feed_a_int when feed_enable_n='0' else "ZZZZZZZZ"; feed_b <= feed_b_int when feed_enable_n='0' else "ZZZZZZZZ"; process(clk,gsr) begin if gsr='1' then feed_a_reg <= X"00"; feed_b_reg <= X"00"; feeModuleAddr_reg <= X"0"; elsif clk'event and clk='1' then feed_a_reg <= feed_a; feed_b_reg <= feed_b; feeModuleAddr_reg <= feeModuleAddr; end if; end process; with feeModuleAddr_reg select feed_reg_selected <= feed_a_reg when X"0" | X"1" | X"2" | X"3" | X"4" | X"5", -- could use a < comparison... will synthesize ok? feed_b_reg when others; -- +++++++++++++++++++++++++++++++++ -- + fee fpga configuration engine + -- +++++++++++++++++++++++++++++++++ -- For now, instead of checking for INIT to be released on all boards, we just wait a fixed delay that -- should be long enough, and proceed with download. If it fails, after all, it really doesn't matter why, -- that can be investigated with a scope when the time comes to fix it... Later, to be fancy, we can check -- the INIT status and go on when it is ok (but then should have a way to proceed if some FEE assemblies are -- found with INIT stuck, and others are ok and could be used!) -- -------------------------------------------------------------- -- also maybe first read some 12 bits from the CM, which can be used to allow for "permanently" -- disabling bad fee assemblies... -- -------------------------------------------------------------- -- NOTE: Should go back and check, I think maybe we are supposed to use only open-drain outputs on cmdin_n, -- cmclk, cmcs_n... (potential contention with HDLC board!) IMPORTANT/CHECK THIS!! -- -------------------------------------------------------------- -- Added to this block also is the initRegister, a shift register of arbitrary length which will be -- programmed with the last bits read (by this state machine) from the configuration memory. This is then -- used to make the boxName and readoutUnitId nonvolatile (and then they are made read-only to slow controls). feeConfig: block type fc_type is (fc_wait,fc_prog,fc_initWait,fc_shift,fc_done); signal fcx: fc_type; constant fcx_i_width: integer := 20; -- num_config_bits is the number of bits for XC2S15 configuration (197696), rounded up to the nearest -- full page (1 page = 264 bytes), plus 1 whole extra page for initRegister data. This has to be on a -- page of its own so that it can be separately programmed. constant num_config_bits: integer := 200640; constant num_bits: integer := num_config_bits+64; -- total bit count includes 64 bit cmd/dummy for CM -- four steps are used to send each bit; signal fcx_i counts down the steps constant fcx_i_initval: integer range 0 to 2**fcx_i_width-1 := 4*num_bits-1; signal fcx_i: UNSIGNED(fcx_i_width-1 downto 0); signal timer: UNSIGNED(25 downto 0); signal timer2: UNSIGNED(5 downto 0); signal timer3: UNSIGNED(15 downto 0); signal cmdin_pre: std_logic; signal initRegister: std_logic_vector(39 downto 0); begin process(clk,gsr) begin if gsr='1' then timer <= CONV_UNSIGNED(2**26-1,26); timer2 <= CONV_UNSIGNED(31,6); timer3 <= CONV_UNSIGNED(2**16-1,16); -- 1.6 ms delay to allow for INIT to be released fcx_i <= CONV_UNSIGNED(fcx_i_initval,fcx_i_width); elsif clk'event and clk='1' then timer <= timer-1; -- here (and elsewhere) there is an implied type conversion... if fcx=fc_prog then timer2 <= timer2-1; end if; if fcx=fc_initWait then timer3 <= timer3-1; end if; if fcx=fc_shift then fcx_i <= fcx_i-1; end if; end if; end process; process(clk,gsr) begin if gsr='1' then fcx <= fc_wait; xclk_a <= '0'; xclk_b <= '0'; cmcs_n <= '1'; cmclk <= '1'; cmdin_n <= '1'; fee_din_pre <= '1'; elsif clk'event and clk='1' then case fcx is when fc_wait => if timer=CONV_UNSIGNED(0,26) then fcx <= fc_prog; --fc_prog xclk_a <= '1'; -- assert fee_prog_n bit to fee fpga's xclk_b <= '1'; cmcs_n <= '0'; -- assert cs_n to config memory end if; when fc_prog => if timer2=CONV_UNSIGNED(0,6) then fcx <= fc_initWait; xclk_a <= '1'; -- deassert fee_prog_n bit to fee fpga's xclk_b <= '1'; else xclk_a <= '0'; xclk_b <= '0'; end if; when fc_initWait => xclk_a <= '0'; xclk_b <= '0'; if timer3=CONV_UNSIGNED(0,16) then fcx <= fc_shift; end if; when fc_shift => if fcx_i=CONV_UNSIGNED(0,fcx_i_width) then fcx <= fc_done; end if; cmdin_n <= not cmdin_pre; cmclk <= not fcx_i(1); if fcx_i(1 downto 0)=UNSIGNED'("01") then -- feed all the config bits also through initRegister - the last bits will rule! initRegister(0) <= cmdout; for i in 1 to initRegister'high loop initRegister(i) <= initRegister(i-1); end loop; end if; when fc_done => -- nothing to do, just wait here (future re-program maybe...) cmcs_n <= '1'; -- deassert cs_n to config memory end case; if fcx_i(1 downto 0)=2 then fee_din_pre <= cmdout; end if; end if; end process; fee_prog_n_pre <= not bool2std(fcx=fc_wait); feeConfigXclkInUse <= bool2std((fcx=fc_wait) or (fcx=fc_prog) or (fcx=fc_initWait)); feeConfigDone <= bool2std(fcx=fc_done); cmd_rom: ROM32X1 generic map(INIT => "68040000") -- CM command for continuous read starting at page 0x200 port map( A0 => fcx_i(2), A1 => fcx_i(3), A2 => fcx_i(4), A3 => fcx_i(5), A4 => fcx_i(6), O => cmdin_pre); fee_cclk_pre <= (fcx_i(1)) or (not bool2std(fcx=fc_shift)); boxName <= initRegister(39 downto 8); readoutUnitId <= initRegister(7 downto 0); sc_dout_pre <= boxName(31 downto 16) when (boxName_read and not sc_a_reg(1))='1' else "ZZZZZZZZZZZZZZZZ"; sc_dout_pre <= boxName(15 downto 0) when (boxName_read and sc_a_reg(1))='1' else "ZZZZZZZZZZZZZZZZ"; sc_dout_pre <= X"00"&readoutUnitId when readoutUnitId_read='1' else "ZZZZZZZZZZZZZZZZ"; end block feeConfig; -- control registers process(clk,gsr) begin if gsr='1' then csr <= X"0000"; shadow_lock_err <= '0'; ptLatency <= "0000000"; ltLatency <= "0000000"; elsif clk'event and clk='1' then if csr_write='1' then csr(14 downto 0) <= sc_din_reg(14 downto 0); end if; -- CSR error bits: Each error bit has a shadow error bit which is set upon the error condition. The -- shadow error bit is copied to the CSR bit whenever it is not being read. Writing a '1' to the CSR -- bit actually writes the shadow error bit (afterwards copied to CSR bit). Reading the CSR bit will -- always produce a stable value (since the shadow bit is only copied to the CSR bit when it's not -- being read). If the CSR bit is read as a '1', then the shadow bit will be cleared (and afterwards -- copied into the CSR bit, UNLESS the error condition is persisting. shadow_lock_err <= (not lock) or (csr_write and sc_din_reg(15)) or (shadow_lock_err and not (csr_read and csr(15))); csr(15) <= (shadow_lock_err and not csr_read) or (csr(15) and csr_read); if ptLatency_write='1' then ptLatency <= sc_din_reg(6 downto 0); end if; if ltLatency_write='1' then ltLatency <= sc_din_reg(6 downto 0); end if; end if; end process; sc_dout_pre <= csr when csr_read='1' else "ZZZZZZZZZZZZZZZZ"; sc_dout_pre <= "000000000"&ptLatency when ptLatency_read='1' else "ZZZZZZZZZZZZZZZZ"; sc_dout_pre <= "000000000"<Latency when ltLatency_read='1' else "ZZZZZZZZZZZZZZZZ"; -- ++++++++++++++++++++ -- + version register + -- ++++++++++++++++++++ versionRegister: block constant rev: string := "$Revision: 1.11 $"; constant date: string := "$Date: 2005/03/18 03:55:08 $"; constant vstring: string := date(8 to date'right-5)&" ["&rev(12 to rev'right-2)&", "& rev_mapmt_system(12 to rev_mapmt_system'right-2)&"] "; constant vsubstring: string := vstring(1 to 32); signal veraddr: std_logic_vector(4 downto 0); signal verbits: std_logic_vector(6 downto 0); begin process(clk,gsr) begin if gsr='1' then veraddr <= "00000"; elsif clk'event and clk='1' then if version_write='1' then veraddr <= sc_din_reg(4 downto 0); end if; end if; end process; vrom: for i in 6 downto 0 generate vr0: ROM32X1 generic map(INIT => string32_to_ascii_bitslice_hex(vsubstring,i)) port map( A0 => veraddr(0), A1 => veraddr(1), A2 => veraddr(2), A3 => veraddr(3), A4 => veraddr(4), O => verbits(i)); end generate; sc_dout_pre <= "000000000"&verbits when version_read='1' else "ZZZZZZZZZZZZZZZZ"; end block versionRegister; -- +++++++++++++++++++++++++++++++++++++ -- + fee board slow controls interface + -- +++++++++++++++++++++++++++++++++++++ -- This manages slow controls transactions targetting the FEE boards, using the FEE board commands for -- address, read, and write. It must watch the feeBusBusy signal (which may be asserted at any time, when -- the cmd and or data busses to the FEE boards are in use by the FEE board configuration engine or in -- response to a command received from the TCD interface). (feeBusBusy will be asserted at least two clk -- ticks before the bus is actually used by either of these machines... no guarantees about when, if ever, -- it is released... (sc software could retry if it sees a bus error, this might be wise...)) fee_sc: block type fee_sc_x_type is (x_idle,x_addr,x_read,x_pickoff,x_write,x_finish); signal fee_sc_x: fee_sc_x_type; signal fee_sc_data: std_logic_vector(7 downto 0); constant fee_read_pickoff_delay: integer := 2; signal fee_read_pickoff_timer: integer range 0 to fee_read_pickoff_delay; begin process(clk,gsr) begin if gsr='1' then fee_sc_x <= x_idle; fee_read_pickoff_timer <= 0; fee_sc_data <= X"00"; elsif clk'event and clk='1' then case fee_sc_x is when x_idle => if ((fee_read='1') or (fee_write='1')) then fee_sc_x <= x_addr; end if; when x_addr => if feeBusBusy='0' then if fee_write='1' then fee_sc_x <= x_write; else fee_sc_x <= x_read; end if; end if; when x_read => if feeBusBusy='0' then fee_sc_x <= x_pickoff; fee_read_pickoff_timer <= fee_read_pickoff_delay; end if; when x_pickoff => fee_read_pickoff_timer <= fee_read_pickoff_timer-1; if fee_read_pickoff_timer=0 then fee_sc_x <= x_finish; fee_sc_data <= feed_reg_selected; end if; when x_write => if feeBusBusy='0' then fee_sc_x <= x_finish; end if; when x_finish => -- wait for the request to go away (in response to dtack...) if ((fee_read='0') and (fee_write='0')) then fee_sc_x <= x_idle; end if; end case; end if; end process; -- note: send_feeCmd_xxx must only be asserted for a single clk cycle! send_feeCmd_addr <= bool2std(fee_sc_x=x_addr) and not feeBusBusy; send_feeCmd_read <= bool2std(fee_sc_x=x_read) and not feeBusBusy; send_feeCmd_write <= bool2std(fee_sc_x=x_write) and not feeBusBusy; sc_dout_pre <= X"00"&fee_sc_data when fee_read='1' else "ZZZZZZZZZZZZZZZZ"; fee_finish <= bool2std(fee_sc_x=x_finish); end block fee_sc; -- +++++++++++++++++++++++++++ -- + slow controls interface + -- +++++++++++++++++++++++++++ -- This interface is a slave to the MC68302 microprocessor on the STAR HDLC mezzanine board. Only address -- lines 16:1 are connected, lines 23:17 are not connected here. Wired for 16-bit accesses only, on even -- addresses only. sc: block signal sc_dout_int: std_logic_vector(sc_d'range); signal sc_write_n_reg,sc_cs3_n_reg,sc_ds_n_reg: std_logic; signal send_dtack: std_logic; begin process(clk,gsr) begin if gsr='1' then sc_dtack_n <= '1'; elsif clk'event and clk='1' then if (sc_ds_n_reg='1') then sc_dtack_n <= '1'; -- release dtack as soon as we see sc_ds_n is deasserted again elsif ((send_dtack='1') and (sc_ds_n_reg='0')) then sc_dtack_n <= '0'; -- set dtack when data transfer is complete, IF sc_ds_n is still -- asserted! (If not, we already had a bus error, accept this fact!) end if; end if; end process; process(clk,gsr) begin if gsr='1' then sc_a_reg <= X"0000"; sc_din_reg <= X"0000"; sc_dout_int <= X"0000"; sc_write_n_reg <= '0'; sc_cs3_n_reg <= '1'; sc_ds_n_reg <= '1'; elsif clk'event and clk='1' then sc_a_reg <= sc_a; sc_din_reg <= sc_d; if send_dtack='1' then sc_dout_int <= sc_dout_pre; end if; sc_write_n_reg <= sc_write_n; sc_cs3_n_reg <= sc_cs3_n; sc_ds_n_reg <= sc_ds_n; end if; end process; sc_d <= sc_dout_int when ((not sc_ds_n_reg) and sc_write_n_reg and (not sc_cs3_n_reg))='1' else "ZZZZZZZZZZZZZZZZ"; -- the following action signals are held asserted while sc_ds_n etc. are asserted - if this would cause -- side effects, the state machine executing the respective action must block them fee_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_fee)); fee_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_fee)); csr_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_csr)); csr_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_csr)); boxName_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_boxName)); readoutUnitId_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_readoutUnitId)); ptLatency_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_ptLatency)); ptLatency_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_ptLatency)); ltLatency_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_ltLatency)); ltLatency_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_ltLatency)); version_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_version)); version_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_version)); dly_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_delay)); mon_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_monitor)); eventLogCount_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_eventLogCount)); eventLog_clear <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_eventLogCount)); eventLog_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_eventLog)); rs_period_write <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and (not sc_write_n_reg) and bool2std(sc_target_addr_match(sc_a_reg,sc_rs_period_count)); rs_period_read <= (not sc_ds_n_reg) and (not sc_cs3_n_reg) and sc_write_n_reg and bool2std(sc_target_addr_match(sc_a_reg,sc_rs_period_count)); send_dtack <= fee_finish or csr_read or csr_write or boxName_read or readoutUnitId_read or ptLatency_read or ptLatency_write or ltLatency_read or ltLatency_write or version_read or version_write or dly_finish or mon_finish or eventLogCount_read or eventLog_clear or eventLog_read or rs_period_write or rs_period_read; end block sc; -- ++++++++++++++++++++++++++++++ -- + event logger (diagnostics) + -- ++++++++++++++++++++++++++++++ -- This is a dual port ram, port 1 is 256 x 64, and is written whenever there is an 'event'. This can be -- used for various diagnostic purposes, but in the initial application, an 'event' is whenever the 20-bit -- trigger command word (including trig command, daq command, and token) is not entirely zero. The 64 bits -- written to the memory in this case include the trigger command, daq command, token, and a 32-bit clock -- cycle counter. Another type of event that might be added in future would be a state change on the LOCK -- signal out of the Roboclock PLL chip. -- The number of events (0 to 256) recorded in the event log is available in the event log count register. -- Event logging stops when the log buffer is full, i.e., after the 256th event. This can be reset by -- writing (with any value) to the event log count register. The event buffer is read via the slow controls -- interface, through port 2 of the dual port ram, which is organized as 1024 x 16 (with the most -- significant word of the 64 bits at the lowest address). eventLog: block signal eventLogWrite,full: std_logic; signal cycle_count: UNSIGNED(31 downto 0); signal eventLogIndex: UNSIGNED(7 downto 0); signal eventLogWdata: std_logic_vector(63 downto 0); type eventLogRdata_type is array(0 to 3) of std_logic_vector(15 downto 0); signal eventLogRdata: eventLogRdata_type; begin process(clk,gsr) begin if gsr='1' then eventLogIndex <= X"00"; full <= '0'; cycle_count <= X"00000000"; elsif clk'event and clk='1' then if eventLog_clear='1' then eventLogIndex <= X"00"; full <= '0'; elsif cycle(2)='1' then if eventLogWrite='1' then eventLogIndex <= eventLogIndex+X"01"; if eventLogIndex=UNSIGNED'(X"ff") then full <= '1'; end if; end if; cycle_count <= cycle_count+X"00000001"; end if; end if; end process; eventLogWrite <= cycle(2) and bool2std(trig_word/=X"00000") -- cycle(2) is when to look and (not full) and (not eventLog_clear); eventLogWdata(63 downto 52) <= X"bec"; -- whatever, this is just a placeholder eventLogWdata(51 downto 32) <= trig_word; eventLogWdata(31 downto 0) <= std_logic_vector(cycle_count); buf: for i in 0 to 3 generate buf0: RAMB4_S16_S16 port map(WEA => eventLogWrite, ENA => '1', RSTA => '0', CLKA => clk, ADDRA => std_logic_vector(eventLogIndex), DIA => eventLogWdata(63-16*i downto 48-16*i), DOA => open, WEB => '0', ENB => '1', RSTB => '0', CLKB => clk, ADDRB => sc_a_reg(10 downto 3), DIB => X"0000", DOB => eventLogRdata(i)); sc_dout_pre <= eventLogRdata(i) when ((eventLog_read='1') and (sc_a_reg(2 downto 1)=CONV_STD_LOGIC_VECTOR(i,2))) else "ZZZZZZZZZZZZZZZZ"; end generate; sc_dout_pre <= "0000000"&full&std_logic_vector(eventLogIndex) when eventLogCount_read='1' else "ZZZZZZZZZZZZZZZZ"; end block eventLog; -- +++++++++++++++++++++++++++++++ -- + delay control DAC interface + -- +++++++++++++++++++++++++++++++ dlyDac: block type dly_x_type is (idle,setup1,low,high,finish); signal dly_x: dly_x_type; signal i: integer range 15 downto 0; signal dlycs_n_int: std_logic; type dacCmd_table_type is array(0 to 3) of std_logic_vector(3 downto 0); constant dacCmd_table: dacCmd_table_type := (X"1", X"2", X"3", X"4"); signal dacword: std_logic_vector(15 downto 0); begin process(clk,gsr) begin if gsr='1' then dly_x <= idle; dlycs_n_int <= '1'; i <= 15; elsif clk'event and clk='1' then if cycle(0)='1' then -- LTC1664 timing requirements need this case dly_x is when idle => if dly_write='1' then dly_x <= setup1; dlycs_n_int <= '0'; i <= 15; end if; when setup1 => dly_x <= low; when low => dly_x <= high; when high => if i=0 then dly_x <= finish; dlycs_n_int <= '1'; else i <= i-1; dly_x <= low; end if; when finish => if dly_write='0' then -- wait for the request to go away (in response to dtack...) dly_x <= idle; end if; end case; end if; end if; end process; dly_finish <= bool2std(dly_x=finish); -- dacword is concatenation of command (indexed by address(2 downto 1)) and 10-bit data (see the -- LTC1664 data sheet for details...) dacword <= dacCmd_table(CONV_INTEGER(UNSIGNED(sc_a_reg(2 downto 1)))) & sc_din_reg(9 downto 0) & "00"; -- all outputs constant when not in use, to avoid crosstalk into the delay generator circuits... dlydin <= dacword(i) or dlycs_n_int; dlycs_n <= dlycs_n_int; dlysclk <= bool2std(dly_x=high); end block dlyDac; -- +++++++++++++++++++++++++ -- + monitor ADC interface + -- +++++++++++++++++++++++++ mon: block type mon_x_type is (idle,sel_low,sel_high,acquire,read_high,read_low,finish); signal mon_x: mon_x_type; signal i: integer range 0 to 7; -- address bit index signal j: integer range 0 to 14; -- note don't make acq_delay too big or you'll get a bus error... constant acq_delay: integer := 2000; -- 200 us (at 10 MHz RS) acquisition delay (41.5 us should be enough) signal acq_timer: integer range 0 to acq_delay; signal mon_adcData: std_logic_vector(11 downto 0); -- 12 bit data (we drop the "REFRDY" indicator bit) begin process(clk,gsr) begin if gsr='1' then mon_x <= idle; i <= 7; acq_timer <= 0; j <= 14; mon_adcData <= "000000000000"; elsif clk'event and clk='1' then if cycle(0)='1' then case mon_x is when idle => if mon_read='1' then mon_x <= sel_low; i <= 7; end if; when sel_low => mon_x <= sel_high; when sel_high => if i=0 then mon_x <= acquire; acq_timer <= acq_delay; else mon_x <= sel_low; i <= i-1; end if; when acquire => acq_timer <= acq_timer-1; if acq_timer=0 then mon_x <= read_high; j <= 14; end if; when read_high => mon_x <= read_low; when read_low => if j=0 then mon_x <= finish; else mon_x <= read_high; j <= j-1; end if; mon_adcData(0) <= mondout; -- grab a bit for k in 11 downto 1 loop -- shift the rest mon_adcData(k) <= mon_adcData(k-1); end loop; when finish => if mon_read='0' then -- wait for the request to go away (in response to dtack...) mon_x <= idle; end if; end case; end if; end if; end process; mondin <= sc_a_reg(i+1); monsclk <= not (bool2std(mon_x=sel_low) or bool2std(mon_x=read_low)); moncs_n <= not bool2std(mon_x=acquire); sc_dout_pre <= mon_adcData(11) & mon_adcData(11) & mon_adcData(11) & mon_adcData(11) & mon_adcData when mon_read='1' else "ZZZZZZZZZZZZZZZZ"; mon_finish <= bool2std(mon_x=finish); end block mon; end architecture rdo_0;