-------------------------------- -- P2081A.vhd ------------------ -- For A208101B and later ------ -- Firmware version 1 ---------- -------------------------------- -------------------------------- -- (c) 2015 Richard Studley ---- -- Brandeis University --------- -- Updated 22 Sept 2015 -------- -------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity main is port ( CLK, -- 40 MHz Clock input A : in std_ulogic; -- LVDS signal in SWITCHES -- four SMT toggle switches : in std_logic_vector(3 downto 0); B, -- LVDS return signal WAKE, -- Power switches ENA_5V_REF, -- 5VREF enable ENA_0V_REF, -- 0VREF enable ENA_ADC_1, -- ADC "1" enable ENA_ADC_2, -- ADC "2" enable CLK_ENA, -- Clock enable LB, -- Loop back TP1, TP2, TP3, TP4 -- Test pads : out std_logic; -- DAC_A, DAC_B, and LED are type "buffer" to -- simplify the code where one register is changed -- and the other two retain their value DAC_A, -- DAC "A" bits DAC_B : buffer std_logic_vector(7 downto 0); LED : buffer std_logic_vector(12 downto 0); -- IO is type inout for future implementation -- of digital logical input IO -- Logical I/O pads : inout std_logic_vector(15 downto 0) ); end; architecture behavior of main is ------------------------------------ --Version: 1 (Standard I/O)--------- ------------------------------------ constant version_number : -- Firmware version stored as constant unsigned(7 downto 0) := -- for flash on board test "00000001"; ------------------------------------ -- LWDAQ Command Receiver signals:-- ------------------------------------ signal synchronized_A : std_logic; -- "A" synchronized to CLK signal synchronized_A_pipeline : -- pipeline register of SA to create DA & DDA std_logic_vector(9 downto 0); signal delayed_A : std_logic; -- "SA" positive edges delayed by 125 ns signal delayed_delayed_A : -- "SA" positive edges delayed by 250 ns std_logic; signal receiver_active : std_logic; -- shows that command receiver is active signal command_register : -- 16 bit LWDAQ command word std_logic_vector(15 downto 0) := "0000000000000000"; signal command_strobe : std_logic; -- loads data_bit_register into command_register signal address_register : -- 16 bit LWDAQ address word std_logic_vector(15 downto 0); signal address_strobe : std_logic; -- loads data_bit_register into address_register -- Command Receiver Abbreviations: alias SA : std_logic is synchronized_A; alias SA_pipeline : std_logic_vector (9 downto 0) is synchronized_A_pipeline; alias DA : std_logic is delayed_A; alias DDA : std_logic is delayed_delayed_A; ------------------------------------------- --End LWDAQ Command Receiver Declarations-- ------------------------------------------- -- NOTE: "address_register" and "address_strobe" -- do not synthesize in A2081 ----------------------------- --A2081 Signal Declarations-- ----------------------------- -- I/O Signals: signal IO_register : -- Register output for IO std_logic_vector(15 downto 0); signal LED_register : -- Register output for LED std_logic_vector(12 downto 0); -- Control Signals: signal dac_data_source_flags : -- Determines source of DAC register value std_logic_vector(2 downto 0); signal dac_clock_source_flag : -- Determines source of DAC clock std_logic; signal RAM_address_reset_low : -- Avtive low reset set high when using ram std_logic; signal DAC_reg : -- Register holding output value for DAC std_logic_vector(7 downto 0); --Clock Divider Declarations: signal divided_CLK : -- Variable frequency clock for use in std_logic; -- function generator signal delay_timer : -- Delay counter for frequency control unsigned(23 downto 0); signal delay_timer_max : -- Reset value for delay_output_timer unsigned(23 downto 0) := "000000000000000000001111"; constant delay_timer_zero : -- Delay timer reset zero unsigned(23 downto 0) := "000000000000000000000000"; -- Embedded Block RAM Signals: signal ram_input_byte : std_logic_vector(7 downto 0); -- Command Byte constant ram_byte_address_max : -- Maximum address value unsigned := "111111111"; signal ram_byte_address : -- Command Memory Address std_logic_vector(8 downto 0); signal ram_output_byte : -- Command Memory Data Out std_logic_vector(7 downto 0); signal ram_write_enable : -- Command Memory Write std_logic; -- A2081 specific aliases: alias op_code : std_logic_vector(3 downto 0) is command_register (3 downto 0); alias selector : std_logic_vector(1 downto 0) is command_register (5 downto 4); --------------------------------- --END A2081 Signal Declarations-- --------------------------------- begin ------------------------------------------ --BEGIN Embedded Block RAM Declarations:-- ------------------------------------------ Command_Memory: entity RAM_512Byte port map ( Clock => not CLK, ClockEn => '1', Reset => '0', WE => ram_write_enable, Address => ram_byte_address, Data => ram_input_byte, Q => ram_output_byte ); --------------------------------------- --END Embedded Block RAM Declarations-- --------------------------------------- -------------------------------- --BEGIN LWDAQ Command Receiver-- -------------------------------- command_receiver: process(CLK) is type command_receiver_state is ( -- FSM state type defined inactive, data_transmit, -- data_transmit currently placeholder command_receive, address_receive ); variable current_state, next_state : -- FSM state variable instantiated command_receiver_state := inactive; variable driver_bit_register : -- Pipeline register records incoming bits; std_logic_vector(16 downto 0 ) := -- The command word is then clocked into "00000000000000000"; -- command register in its entirety begin -- Generation of synchronized and delayed versions of input signal "A": -- -- For more information, see: -- http://alignment.hep.brandeis.edu/Electronics/LWDAQ/LWDAQ.html#Transmit Signals if rising_edge(CLK) then SA <= A; -- synchronizes data stream; SA_pipeline <= SA_pipeline(8 downto 0) & SA; -- shifts data pipeline register DA <= SA_pipeline(3) and not SA_pipeline(4); -- creates DA from 125 ns delay DDA <= SA_pipeline(8) and not SA_pipeline(9); -- creates DDA from 250 ns delay B <= A; -- creates loopback signal; -- will require conditional logic on any board requiring digital data return -- Generation of "Receiver Active" signal: if (SA = '0' and DDA = '1') then receiver_active <= '1'; elsif (SA = '1' and DDA = '1') then receiver_active <= '0'; else receiver_active <= receiver_active; end if; -- Begin LWDAQ Command Receiver State Machine: case current_state is when inactive => if receiver_active = '1' then if driver_bit_register(0) = '1' then -- Looks at first bit of command word; next_state := command_receive; -- if 1, word is LWDAQ command else next_state := address_receive; -- if 0, word is LWDAQ address end if; else next_state := inactive; -- Stays in inactive until receiver_active end if; command_strobe <= '0'; -- Ends strobe signals on return to inactive state address_strobe <= '0'; when command_receive => if receiver_active = '1' then -- Stays in command_receive until LWDAQ command word is complete next_state := command_receive; else command_register <= -- Loads LWDAQ command word into register driver_bit_register(16 downto 1); command_strobe <= '1'; -- Activates command strobe pulse next_state := inactive; -- Returns to idle state end if; when address_receive => if receiver_active = '1' then next_state := address_receive; -- Stays in address_receive until LWDAQ address word is complete else address_register <= -- Loads LWDAQ address word into register driver_bit_register(16 downto 1); address_strobe <= '1'; -- Activates address strobe pulse next_state := inactive; -- Returns to idle state end if; when others => next_state := inactive; command_register <= command_register; end case; -- End LWDAQ Command Receiver State Machine -- Clocking of Command Word into "driver_bit_register": if DA = '1' then driver_bit_register := driver_bit_register(15 downto 0) & SA; else driver_bit_register := driver_bit_register; end if; -- Clocking of State Machine state register: current_state := next_state; end if; end process; ------------------------------ --END LWDAQ Command Receiver-- ------------------------------ ----------------------------- --Begin A2081 Control Logic-- ----------------------------- command_update: process (command_strobe) is variable next_address : -- Variable established for incrementing std_logic_vector(8 downto 0) := -- synchronous "ram_address" register "000000000"; -- NOT CURRENTLY IMPLEMENTED begin if falling_edge(command_strobe) then ------------------------- --Begin Function Select-- ------------------------- -- The default version of the firmware uses a four-digit opcode to -- determine board behavior on command_strobe. It consists of the -- four LWDAQ command word bits DC4-DC1: case op_code is -- Output Reset: -- Asserts 0 value on all outputs, -- RAM Address, and DAC control logic: when "0000" => IO_register <= "0000000000000000"; LED_register(7 downto 0) <= "00000000"; ENA_5V_REF <= '0'; -- DG411 switches are active low ENA_0V_REF <= '0'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '0'; ram_write_enable <= '0'; ram_input_byte <= "00000000"; dac_data_source_flags <= "000"; -- Sets DAC to 0V and turns off RAM output dac_clock_source_flag <= '0'; ram_address_reset_low <= '0'; -- Sets RAM address to "00000000"; -- RAM Single Command Upload: -- Uploads a single 8-bit DAC value to RAM; -- Commands sent in sequence will upload consecutive bytes: when "0100" => dac_data_source_flags <= "000"; -- De-asserts RAM output control ram_input_byte <= -- Updates ram_input_byte with DC16-DC9 command_register(15 downto 8); ram_write_enable <= '1'; -- Activates RAM writethrough LED_register(7 downto 0) <= ram_byte_address(7 downto 0); -- Displays RAM address for diagnostic purposes ram_address_reset_low <= '1'; -- Preserves value in address register -- Establish DAC Output: -- Establishes control of DAC output: when "0110" => case selector is when "00" => dac_data_source_flags <= "000"; -- DAC output to "00000000"; DAC source is command word dac_clock_source_flag <= '0'; -- DAC clocked by command_strobe when "01" => dac_data_source_flags <= "011"; -- DAC_A output active; DAC source is ram_output_byte dac_clock_source_flag <= '1'; -- DAC clocked by divided_CLK when "10" => dac_data_source_flags <= "101"; -- DAC_B output active; DAC source is ram_output_byte dac_clock_source_flag <= '1'; -- DAC clocked by divided_CLK when "11" => dac_data_source_flags <= "111"; -- Both DAC outputs active; DAC source is ram_output_byte dac_clock_source_flag <= '1'; -- DAC clocked by divided_CLK when others => dac_data_source_flags <= "000"; -- DAC output to "00000000"; DAC source is command word dac_clock_source_flag <= '0'; -- DAC clocked by command_strobe end case; ram_address_reset_low <= '1'; -- "ram_address" value is preserved ram_write_enable <= '0'; -- RAM Output Period Select: -- Establish function generator frequency by setting -- number of delay ticks ("delay_timer_max") between DAC updates; -- Delay counter generates divided_CLK when it reaches 0; -- "delay_timer_max" is 24 bit unsigned: when "0101" => case selector is when "00" => -- Set to maximum frequency delay_timer_max <= -- (zero delay ticks) delay_timer_zero; when "01" => -- Set lowest 8 bits of delay_timer_max delay_timer_max(7 downto 0) <= unsigned( command_register(15 downto 8) ); when "10" => -- Set middle 8 bits of delay_timer_max delay_timer_max(15 downto 8) <= unsigned( command_register(15 downto 8) ); when "11" => -- Set upper 8 bits of delay_timer_max delay_timer_max(23 downto 16) <= unsigned( command_register(15 downto 8) ); when others => delay_timer_max <= delay_timer_max; end case; ram_write_enable <= '0'; --Analog Input: -- Establish analog return to driver by closing one switch on DG412: when "1000" => case selector is when "00" => -- S=00 is 0V reference ENA_5V_REF <= '0'; ENA_0V_REF <= '1'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '0'; when "01" => -- S=01 is ADC "1" ENA_5V_REF <= '0'; ENA_0V_REF <= '0'; ENA_ADC_1 <= '1'; ENA_ADC_2 <= '0'; when "10" => -- S=10 is ADC "2" ENA_5V_REF <= '0'; ENA_0V_REF <= '0'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '1'; when "11" => -- S=11 is 5V reference ENA_5V_REF <= '1'; ENA_0V_REF <= '0'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '0'; when others => -- Should not synthesize ENA_5V_REF <= '0'; ENA_0V_REF <= '0'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '0'; end case; ram_write_enable <= '0'; -- Analog Output: -- Establishes control of DAC output; -- -- dac_data_source_flags(0) asserts control -- to "Function Select" process; -- -- setting DAC "A" (S=01) does not affect DAC "B" -- and vice versa (S=10): when "1001" => case selector is when "00" => -- sets all DACs to "00000000" dac_data_source_flags <= "000"; when "01" => -- sets DAC "A" to DC16-DC9 dac_data_source_flags <= "010"; when "10" => -- sets DAC "B" to DC16-DC9 dac_data_source_flags <= "100"; when "11" => -- sets both DACs to DC16-DC9 dac_data_source_flags <= "110"; when others => dac_data_source_flags <= "000"; end case; ram_write_enable <= '0'; -- Digital Output: -- Establishes the state of LEDs and I/O pads: when "1011" => case selector is when "00" => -- Resets all outputs to "0" IO_register <= "0000000000000000"; LED_register(7 downto 0) <= "00000000"; when "01" => -- Sets lower I/O byte to DC16-DC9 IO_register(15 downto 8) <= IO_register(15 downto 8); IO_register(7 downto 0) <= command_register(15 downto 8); LED_register <= LED_register; when "10" => -- Sets higher I/O byte to DC16-DC9 IO_register(15 downto 8) <= command_register(15 downto 8); IO_register(7 downto 0) <= IO_register(7 downto 0); LED_register <= LED_register; when "11" => -- Sets LEDs to DC16-DC9 IO_register <= IO_register; LED_register(7 downto 0) <= command_register(15 downto 8); when others => IO_register <= IO_register; LED_register <= LED_register; end case; ram_write_enable <= '0'; -- Board Test: -- Displays firmware version on green LEDs when "1111" => LED_register(7 downto 0) <= std_logic_vector(version_number); ram_write_enable <= '0'; -- Output Reset: -- Erroneous opcode defauts to Output reset: when others => IO_register <= "0000000000000000"; LED_register(7 downto 0) <= "00000000"; ENA_5V_REF <= '0'; -- DG412 switches are active high ENA_0V_REF <= '0'; ENA_ADC_1 <= '0'; ENA_ADC_2 <= '0'; ram_write_enable <= '0'; ram_input_byte <= "00000000"; dac_data_source_flags <= "000"; -- Sets DAC to 0V and turns off RAM output dac_clock_source_flag <= '0'; ram_address_reset_low <= '0'; -- Sets RAM address to "00000000"; end case; end if; end process; ----------------------- --End Function Select-- ----------------------- -- delay_output establishes a delay timer that allows adjustment of -- the frequency at which the DAC signals update from RAM; -- -- If delay_timer_max is set to 0, divided clock is a 10-MHz square wave: delay_output: process (CLK) is begin if rising_edge(CLK) then if (delay_timer = delay_timer_zero) then delay_timer <= delay_timer_max; -- delay timer resets divided_CLK <= '1'; -- divided_CLK goes high for 50 ns else delay_timer <= delay_timer - 1; -- delay_timer increments down from delay_timer_max divided_CLK <= '0'; end if; end if; end process; -- DAC_mux establishes the source of the value clocked into DAC_reg -- as well as the source of the clock edges which update that value: DAC_mux: process (CLK) is begin -- LSB of dac_data_source_flags determines source of DAC output; -- LSB is determined by which opcode is used: if rising_edge(CLK) then case dac_data_source_flags(0) is when '0' => -- updates from DC16-DC9; DAC_reg <= command_register(15 downto 8); when '1' => -- updates from ram; DAC_reg <= ram_output_byte; when others => DAC_reg <= DAC_reg; end case; end if; -- Upper two bits determine which DAC outputs are active; -- upper two bits are determined by selector bits: if rising_edge(CLK) then case dac_data_source_flags(2 downto 1) is when "00" => -- "00" resets DAC values DAC_A <= "00000000"; DAC_B <= "00000000"; when "01" => DAC_A <= DAC_reg; -- "01" sets DAC_A DAC_B <= DAC_B; -- DAC_B is preserved when "10" => DAC_A <= DAC_A; -- "10" sets DAC_B DAC_B <= DAC_reg; -- DAC_A is preserved when "11" => DAC_A <= DAC_reg; -- "11" sets both DACs DAC_B <= DAC_reg; when others => DAC_A <= "00000000"; DAC_B <= "00000000"; end case; end if; -- Clock source is determined by a separate flag, -- which is also determined by opcode: if rising_edge(CLK) then if ram_address_reset_low = '1' then -- RAM_address is maintained case dac_clock_source_flag is when '0' => -- increments on command_strobe if flag = '0' if op_code = "0100" then if command_strobe = '1' then ram_byte_address <= std_logic_vector( unsigned(ram_byte_address) + 1 ); end if; end if; when '1' => -- increments on divided_CLK if flag = '1' if divided_CLK = '1' then ram_byte_address <= std_logic_vector( unsigned(ram_byte_address) + 1 ); end if; when others => ram_byte_address <= ram_byte_address; end case; else ram_byte_address <= "000000000"; -- resets ram_address on global output reset end if; end if; end process; process(CLK) is begin if rising_edge(CLK) then LED_register(11 downto 8) <= SWITCHES(3 downto 0); LED_register(12) <= command_register(7); end if; end process; -- The enable pin of the 40 MHz oscillator is permanently set to '1' -- on the A208101B. CLK_ENA <= '1'; LED <= LED_register; IO <= IO_register; WAKE <= command_register(7); -- from LWDAQ specification LB <= command_register(6); -- from LWDAQ_specification TP2 <= ram_write_enable; TP1 <= divided_clk; --------------------------- --END A2081 Control Logic-- --------------------------- end behavior;