-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- + Indiana University Cyclotron Facility + -- + STAR Endcap Electromagnetic Calorimeter - MAPMT Readout + -- + Front-End Electronics FPGA + -- + G. Visser - gvisser@iucf.indiana.edu - 812 855 7880 + -- + file created 9/16/2002 + -- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- $Id: mapmt_fee.vhd,v 1.5 2005/03/18 04:22:10 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 mapmt_fee is Port ( -- clock and bus interface clk: in std_logic; bid: in std_logic_vector(1 downto 0); cmd: in std_logic_vector(4 downto 0); cmdv_n: in std_logic; d: inout std_logic_vector(7 downto 0); -- fee stuff clka,clkb: out std_logic; a,b: in std_logic_vector(11 downto 0); qdac: out std_logic; qt: out std_logic_vector(3 downto 0); -- miscellaneous tempen: out std_logic; -- gsr: in std_logic -- FOR SIMULATION ONLY reset_n: in std_logic ); attribute period: string; attribute iostandard: string; attribute loc: string; attribute fast: string; attribute pullup: string; attribute nodelay: string; attribute period of clk: signal is "25 ns"; attribute iostandard of clk,bid,cmd,cmdv_n,d, clka,clkb,a,b,qdac,qt,tempen,reset_n: signal is "LVCMOS2"; attribute loc of clk: signal is "P39"; attribute loc of bid: signal is "P41 P40"; attribute loc of cmd: signal is "P73 P47 P46 P45 P44"; attribute nodelay of cmd: signal is "true"; attribute loc of cmdv_n: signal is "P43"; attribute nodelay of cmdv_n: signal is "true"; attribute loc of d: signal is "P59 P58 P57 P56 P55 P54 P53 P52"; attribute pullup of d: signal is "yes"; attribute fast of d: signal is "true"; attribute nodelay of d: signal is "true"; attribute loc of clka: signal is "P34"; attribute loc of clkb: signal is "P10"; attribute fast of clka: signal is "true"; attribute loc of a: signal is "P32 P31 P30 P22 P21 P20 P19 P18 P17 P16 P15 P13"; attribute loc of b: signal is "P9 P8 P7 P6 P5 P4 P3 P98 P97 P96 P95 P93"; attribute loc of qdac: signal is "P87"; attribute loc of qt: signal is "P71 P70 P69 P68"; attribute loc of tempen: signal is "P82"; attribute loc of reset_n: signal is "P80"; end mapmt_fee; architecture mapmt_fee_0 of mapmt_fee is signal gsr: std_logic; attribute iob: string; attribute keep: string; type cyclex_type is (idle,zero,one,two,three); signal cyclex: cyclex_type; signal cycle: std_logic_vector(3 downto 0); signal cyc0,cyc1,cyc2,cyc3: boolean; signal set_csrErrSync: std_logic; signal cmdv_n_reg,boardSelected: std_logic; signal addr_reg: std_logic_vector(5 downto 0); signal writeRtLatency,readRtLatency,writePcr,readPcr,writeCsr,readCsr, writeAssyIdReg,readAssyIdReg,version_write,version_read: std_logic; signal cmd_reg: std_logic_vector(4 downto 0); type cmdStrobe_type is record sync,addr,rd,wr,trig,pulse: std_logic; end record; signal cmdStrobe: cmdStrobe_type; signal d_in_reg: std_logic_vector(7 downto 0); signal d_out_pre,d_out: std_logic_vector(7 downto 0); signal putDataOut_n,putDataOut_pre: std_logic; signal rtLatency: std_logic_vector(6 downto 0); signal pcr0,pcr1,csr: std_logic_vector(7 downto 0); signal pcr_q,next_pcr_q: std_logic_vector(11 downto 0); signal incdec: std_logic; signal assyIdReg: std_logic_vector(3 downto 0); signal verbits: std_logic_vector(6 downto 0); component ROM32X1 is generic(INIT: string); port(A0,A1,A2,A3,A4: in std_logic; O: out std_logic); end component; signal readBufReadAddr: UNSIGNED(8 downto 0); signal readBufReadData: std_logic_vector(7 downto 0); type readx_type is (readx_wait,readx_seqDelay,readx_go); signal readx: readx_type; constant seqDelay: integer := 18; -- this value has to match what the RDO FPGA is expecting... signal seqDelayCounter: UNSIGNED(8 downto 0); begin -- temporary kluges and patches gsr <= not reset_n; -- chained block read from readBuf to readout board process(clk,gsr) begin if gsr='1' then readx <= readx_wait; seqDelayCounter <= "000000000"; readBufReadAddr <= "000000000"; putDataOut_pre <= '0'; elsif clk'event and clk='1' then if (cyc2 or cyc0) then case readx is when readx_wait => readBufReadAddr <= "000000000"; if cmdStrobe.trig='1' then -- NOTE this only happens on cyc2 seqDelayCounter <= CONV_UNSIGNED(seqDelay,9) +UNSIGNED(assyIdReg&bid&"000"); -- 8 bytes per board... readx <= readx_seqDelay; end if; when readx_seqDelay => seqDelayCounter <= seqDelayCounter-"000000001"; if seqDelayCounter=UNSIGNED'("000000000") then readx <= readx_go; end if; when readx_go => readBufReadAddr <= readBufReadAddr+"000000001"; if readBufReadAddr=UNSIGNED'("000000111") then readx <= readx_wait; end if; end case; end if; putDataOut_pre <= bool2std(readx=readx_go); -- on all cycles, just like the memory! end if; end process; ----------------------------------------------------------------------------------------------------------- -- Clock Enable Generator ----------------------------------------------------------------------------------------------------------- -- Four phases of clock enable signals are generated here. If sync = '1' then a suitable number of dead -- cycles are introduced between the next cycle type 3 and the cycle type 0 after that, such that four -- cycles after the sync cycle will be a cycle type 0. (Note that the incoming command which -- generated the sync signal then came in 4 cycles before the next cycle type 3.) -- If extra cycles (dead cycles) have been inserted by this mechanism, an error bit is set in the csr. -- Note that a circular shift register is NOT used for the (normal) generation of the cycle clock enable -- pulses, a different scheme is used, in the interests of glitch/SEU recovery. clk_cntrl: block signal sync: std_logic_vector(2 downto 1); begin process(clk,gsr) begin if gsr = '1' then sync <= "00"; cyclex <= idle; elsif clk'event and clk = '1' then sync(1) <= cmdStrobe.sync; sync(2) <= sync(1); case cyclex is when zero => cyclex <= one; when one => cyclex <= two; when two => cyclex <= three; when three | idle => if (cmdStrobe.sync or sync(1) or sync(2))='1' then cyclex <= idle; else cyclex <= zero; end if; end case; end if; end process; -- set an error bit if we found that we inserted any dead cycles in response to the sync command -- actually, the FIRST time it's not really an error, should fix this up (define a new state 'init'?) set_csrErrSync <= (cmdStrobe.sync or sync(1) or sync(2)) and bool2std(cyclex=three); -- eliminate the following in favor of direct use of the state signal 'cyclex' cyc0 <= (cyclex=zero); cyc1 <= (cyclex=one); cyc2 <= (cyclex=two); cyc3 <= (cyclex=three); cycle(0) <= bool2std(cyc0); cycle(1) <= bool2std(cyc1); cycle(2) <= bool2std(cyc2); cycle(3) <= bool2std(cyc3); end block clk_cntrl; -- address register and boardSelected register process(clk,gsr) begin if gsr = '1' then addr_reg <= "000000"; boardSelected <= '0'; elsif clk'event and clk = '1' then if (cmdStrobe.addr = '1') then addr_reg <= d_in_reg(5 downto 0); boardSelected <= bool2std(bid = d_in_reg(7 downto 6)); end if; end if; end process; -- command decode cmdStrobe.sync <= bool2std((cmdv_n_reg = '0') and (cmd_reg = feeCmd_opcode(feeCmd_sync))); cmdStrobe.addr <= bool2std((cmdv_n_reg = '0') and (cmd_reg = feeCmd_opcode(feeCmd_addr))); cmdStrobe.rd <= bool2std((cmdv_n_reg = '0') and (cmd_reg = feeCmd_opcode(feeCmd_read))); cmdStrobe.wr <= bool2std((cmdv_n_reg = '0') and (cmd_reg = feeCmd_opcode(feeCmd_write))); cmdStrobe.trig <= bool2std((cmdv_n_reg = '0') and cyc2 -- NOTE ONLY CYCLE(2)!!!! and (cmd_reg = feeCmd_opcode(feeCmd_trigger))); cmdStrobe.pulse <= bool2std((cmdv_n_reg = '0') and cyc2 -- NOTE ONLY CYCLE(2)!!!! and (cmd_reg = feeCmd_opcode(feeCmd_pulse))); version_write <= cmdStrobe.wr and boardSelected and bool2std(addr_reg="111010"); version_read <= cmdStrobe.rd and boardSelected and bool2std(addr_reg="111010"); writeRtLatency <= cmdStrobe.wr and boardSelected and bool2std(addr_reg="111011"); readRtLatency <= cmdStrobe.rd and boardSelected and bool2std(addr_reg="111011"); writePcr <= cmdStrobe.wr and boardSelected and bool2std(addr_reg(5 downto 1)="11110"); readPcr <= cmdStrobe.rd and boardSelected and bool2std(addr_reg(5 downto 1)="11110"); writeCsr <= cmdStrobe.wr and boardSelected and bool2std(addr_reg="111110"); readCsr <= cmdStrobe.rd and boardSelected and bool2std(addr_reg="111110"); writeAssyIdReg <= cmdStrobe.wr and boardSelected and bool2std(addr_reg="111111"); readAssyIdReg <= cmdStrobe.rd and boardSelected and bool2std(addr_reg="111111"); -- control and status registers -- includes also the ramping-pulser stuff because it fits here nicer than in pulser block below process(clk,gsr) begin if gsr = '1' then rtLatency <= "0000010"; -- readout-trigger latency register, default value is 2 pcr0 <= "00000000"; -- pulser control register (high byte) pcr1 <= "00000000"; -- pulser control register (low byte) csr <= "00000000"; -- control/status register assyIdReg <= "0000"; -- FEE assembly identification register elsif clk'event and clk = '1' then if writeRtLatency = '1' then rtLatency <= d_in_reg(6 downto 0); end if; if csr(4)='0' then if writePcr = '1' then if addr_reg(0)='0' then pcr0 <= d_in_reg; else pcr1 <= d_in_reg; end if; end if; else if ((writePcr = '1') and (addr_reg(0)='0')) then pcr0(7 downto 4) <= d_in_reg(7 downto 4); end if; if (cyc2 and (cmdStrobe.pulse='1')) then pcr0(3 downto 0) <= next_pcr_q(11 downto 8); pcr1 <= next_pcr_q(7 downto 0); if pcr_q=X"001" then incdec <= '1'; elsif pcr_q=X"ffe" then incdec <= '0'; end if; end if; end if; if writeCsr = '1' then csr <= d_in_reg; end if; -- we should change the following, to not accept assyId > 11 -- (but, there seems to be something funny with < operator??) if (writeAssyIdReg = '1') then assyIdReg <= d_in_reg(5 downto 2); end if; end if; end process; pcr_q <= pcr0(3 downto 0)&pcr1; next_pcr_q <= pcr_q+X"001" when incdec='1' else pcr_q-X"001"; tempen <= csr(0); -- ++++++++++++++++++++ -- + version register + -- ++++++++++++++++++++ versionRegister: block constant rev: string := "$Revision: 1.5 $"; constant date: string := "$Date: 2005/03/18 04:22:10 $"; 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); begin process(clk,gsr) begin if gsr='1' then veraddr <= "00000"; elsif clk'event and clk='1' then if version_write='1' then veraddr <= d_in_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; end block versionRegister; -- command bus from, and data bus to/from, the readout board process(clk,gsr) begin if gsr = '1' then cmdv_n_reg <= '1'; cmd_reg <= "00000"; d_out <= X"00"; d_in_reg <= X"00"; putDataOut_n <= '1'; elsif clk'event and clk = '1' then cmdv_n_reg <= cmdv_n; cmd_reg <= cmd; d_out <= d_out_pre; d_in_reg <= d; putDataOut_n <= not (putDataOut_pre or readRtLatency or readPcr or readCsr or readAssyIdReg or version_read); end if; end process; d_out_pre <= '0'&rtLatency when readRtLatency = '1' else pcr0 when ((readPcr='1') and (addr_reg(0)='0')) else pcr1 when ((readPcr='1') and (addr_reg(0)='1')) else csr when readCsr = '1' else "00"&assyIdReg&bid when readAssyIdReg = '1' else '0'&verbits when version_read = '1' else readBufReadData; d <= d_out when putDataOut_n='0' else "ZZZZZZZZ"; -- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- + FEE: ADC interface, acquisition buffer, readout machine, and readout buffer + -- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fee: block signal a_reg,a_reg_reg,b_reg,b_reg_reg: std_logic_vector(11 downto 0); signal ix,ix_trig: UNSIGNED(7 downto 0); -- index for writing to acquisition buffer signal acqBufWE: std_logic; signal acqBufWriteData: std_logic_vector(15 downto 0); signal base,iy: UNSIGNED(8 downto 0); -- index for reading from acquisition buffer signal acqBufReadData,readBufDataIn: std_logic_vector(7 downto 0); type gx_type is (gx_wait,gx_calcBase,gx_grab); signal gx: gx_type; constant num_bytes: integer := 8; -- number of bytes to read (8 normal, will be 24 w/ pre/post) signal k,k_reg: integer range 0 to num_bytes-1; signal k_reg_vec: std_logic_vector(8 downto 0); type integer_array is array(natural range <>) of integer; type byte_map_type is (byte,msn,lsn); type byte_map_type_array is array(natural range <>) of byte_map_type; constant byte_offset: integer_array(0 to num_bytes-1) := (5,3,1,3,4,2,0,2); constant byte_map: byte_map_type_array(0 to num_bytes-1) := (byte,lsn,byte,msn,byte,lsn,byte,msn); signal offset: UNSIGNED(7 downto 0); -- NEED TO CONSIDER ALLOWED MINIMUM??? MAY BE > 0 (or even > 1)!! signal dataReadyForReadBuf,readBufWE: std_logic; constant enableDataIntoReadBuf: std_logic := '1'; -- here is where to turn off real data, make it csr bit begin offset <= UNSIGNED(rtLatency&'0') + UNSIGNED('0'&rtLatency); -- i.e., offset <= 3*rtLatency process(clk,gsr) begin if gsr='1' then clka <= '1'; clkb <= '1'; a_reg <= X"000"; a_reg_reg <= X"000"; b_reg <= X"000"; b_reg_reg <= X"000"; ix <= X"00"; elsif clk'event and clk = '1' then if (cyc0 or cyc2) then -- changed from cycle(0) to cycle(2) to fix up ADCCLK timing w.r.t. acq buffer control (unchanged) clka <= cycle(2); clkb <= cycle(2); end if; if (cyc0 or cyc2) then a_reg <= a; b_reg <= b; end if; if (cyc1 or cyc3) then a_reg_reg <= a_reg; b_reg_reg <= b_reg; end if; if acqBufWE='1' then ix <= ix+X"01"; end if; end if; end process; acqBufWE <= bool2std((cyclex=two) or (cyclex=three) or (cyclex=zero)); with cyclex select acqBufWriteData <= a_reg_reg(11 downto 8) & a_reg(11 downto 8) & b_reg_reg(11 downto 8) & b_reg(11 downto 8) when three, a_reg(7 downto 0) & b_reg(7 downto 0) when others; process(clk,gsr) begin if gsr='1' then gx <= gx_wait; dataReadyForReadBuf <= '0'; k <= 0; k_reg <= 0; ix_trig <= X"00"; base(8 downto 1) <= "00000000"; elsif clk'event and clk='1' then case gx is when gx_wait => dataReadyForReadBuf <= '0'; if (cmdStrobe.trig='1') then -- NOTE: this ONLY happens on cyc2! ix_trig <= ix; gx <= gx_calcBase; end if; when gx_calcBase => base(8 downto 1) <= ix_trig-offset; k <= 0; gx <= gx_grab; when gx_grab => k <= k+1; dataReadyForReadBuf <= '1'; if k=(num_bytes-1) then gx <= gx_wait; end if; end case; k_reg <= k; end if; end process; base(0) <= '0'; iy <= base + CONV_UNSIGNED(byte_offset(k),9); with byte_map(k_reg) select readBufDataIn <= acqBufReadData when byte, X"0"&acqBufReadData(7 downto 4) when msn, X"0"&acqBufReadData(3 downto 0) when lsn; -- Data format in acquisition buffer (as viewed from the 16-bit port B): -- This is the data of one RHIC cycle... Directly followed by the next. And so on... -- MSB LSB -- --------------------------------------------------------- -- @base | ch1[7:0] | ch3[7:0] | written on cycle two -- --------------------------------------------------------- -- @base+1 | ch1[11:8] | ch0[11:8] | ch3[11:8] | ch2[11:8] | written on cycle three -- --------------------------------------------------------- -- @base+2 | ch0[7:0] | ch2[7:0] | written on cycle zero -- --------------------------------------------------------- acqBuf: RAMB4_S8_S16 port map(WEA => '0', ENA => '1', RSTA => '0', CLKA => clk, ADDRA => std_logic_vector(iy), DIA => X"00", DOA => acqBufReadData, WEB => '1', ENB => acqBufWE, RSTB => '0', CLKB => clk, ADDRB => std_logic_vector(ix), DIB => acqBufWriteData, DOB => open); -- Data format in readout buffer: (Single-slice readout; pre-post mode is similar, but not yet defined) -- ----------------------------- -- @0 | ch0[7:0] | -- ----------------------------- -- @1 | 0000 | ch0[11:8] | -- ----------------------------- -- @2 | ch1[7:0] | -- ----------------------------- -- @3 | 0000 | ch1[11:8] | -- ----------------------------- -- @4 | ch2[7:0] | -- ----------------------------- -- @5 | 0000 | ch2[11:8] | -- ----------------------------- -- @6 | ch3[7:0] | -- ----------------------------- -- @7 | 0000 | ch3[11:8] | -- ----------------------------- k_reg_vec <= CONV_STD_LOGIC_VECTOR(k_reg,9); readBufWE <= dataReadyForReadBuf and enableDataIntoReadBuf; readBuf: RAMB4_S8_S8 port map(WEA => readBufWE, ENA => '1', RSTA => '0', CLKA => clk, ADDRA => k_reg_vec, DIA => readBufDataIn, DOA => open, WEB => '0', ENB => '1', RSTB => '0', CLKB => clk, ADDRB => std_logic_vector(readBufReadAddr), DIB => X"00", DOB => readBufReadData); end block fee; -- ++++++++++++++++++++++++ -- + test pulse generator + -- ++++++++++++++++++++++++ -- A simple 12-bit DAC is made by feeding a 1-bit pseudorandom output into an RC filter on the pcb. The bit -- rate into filter is 5 MHz (RS/2), pattern repeats @ 1.22 kHz rate. The DAC output feeds an independent -- charge injection circuits on each FEE input. -- PCR register bit assignments: -- ----------------------------------------- -- PCR0 | Q[7:0] | -- ----------------------------------------- -- PCR1 | E3 | E2 | E1 | E0 | Q[11:8] | -- ----------------------------------------- -- USAGE NOTES: -- 1. The dac is enabled if (E3='1' or E2='1' or E1='1' or E0='1'). -- 2. On each channel, switch is opened (pulser precharges) if Ek = '1'. -- 3. Timing of actual firing of the pulser (reclosing the switches), is controlled by TCD command input. -- 4. Switches re-open (if Ek = '1') 16 cycles later, precharging for next event. pulser: block signal prescaler: integer range 0 to 1; signal pulser_enable: std_logic; signal x: std_logic_vector(11 downto 0); signal qdac_int,force_a,force_b: std_logic; signal fire_pre,fire,undrive: std_logic; signal hold_timer: integer range 0 to 15; begin process(clk,gsr) begin if gsr='1' then prescaler <= 0; x <= "000000000000"; qdac_int <= '0'; force_a <= '0'; elsif clk'event and clk='1' then if cyc0 then prescaler <= prescaler-1; end if; if ((cyc0) and (pulser_enable='1') and (prescaler=0)) then for i in 1 to 11 loop x(i) <= x(i-1); end loop; x(0) <= x(11) xor x(5) xor x(3) xor x(0) xor bool2std(x(10 downto 0)="00000000000"); -- I am concerned why the following line (among others) works, shouldn't it need UNSIGNED type? qdac_int <= bool2std( (pcr0(3 downto 0) & pcr1) > x ); end if; force_a <= bool2std((cyc3) and (prescaler=0)); end if; end process; process(clk,gsr) begin if gsr='1' then force_b <= '0'; elsif clk'event and clk='0' then force_b <= bool2std((cyc0) and (prescaler=0)); end if; end process; process(clk,gsr) begin if gsr='1' then fire_pre <= '0'; hold_timer <= 0; elsif clk'event and clk='1' then if cyc2 then if cmdStrobe.pulse='1' then fire_pre <= '1'; hold_timer <= 15; elsif hold_timer=0 then fire_pre <= '0'; else hold_timer <= hold_timer-1; end if; end if; end if; end process; process(clk,gsr) begin if gsr='1' then fire <= '0'; elsif clk'event and clk='1' then if cyc0 then fire <= fire_pre; end if; end if; end process; process(clk,gsr) begin if gsr='1' then undrive <= '0'; elsif clk'event and clk='0' then if cyc1 then undrive <= fire_pre; end if; end if; end process; pulser_enable <= pcr0(7) or pcr0(6) or pcr0(5) or pcr0(4); -- qt, qdac outputs use external +5V pullup; they are actively driven low when a '0' is desired, then -- actively driven high for only 1/8 RS followed by high-z state, when a '1' is desired; qdac is alway -- driven low for last 1/8 RS of a bit time to improve linearity; qt outputs must always be on ('Z') -- during normal operation (to avoid charge injection circuit stealing charge from MAPMT pulse) qdac <= '0' when (((force_a='1') and (force_b='1')) or (qdac_int='0') or (pulser_enable='0')) else '1' when (force_b='1') else 'Z'; qt_bits: for i in 0 to 3 generate qt(i) <= 'Z' when ((pcr0(i+4)='0') or ((fire='1') and (undrive='1'))) else '1' when ((fire='1') and (undrive='0')) else '0'; end generate; end block pulser; end architecture mapmt_fee_0;