library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all; use work.util_pkg.all; use work.lx_rocon_pkg.all; -- LX Master (Transmitter) entity lxmaster_transmitter is generic ( cycle_cnt_width_g : natural := 12 ); port ( clk_i : in std_logic; reset_i : in std_logic; -- Transmision clock_o : out std_logic; mosi_o : out std_logic; sync_o : out std_logic; -- Register register_i : in std_logic; register_o : out std_logic_vector(1 downto 0); register_we_i : in std_logic; -- Cycle period cycle_reg_i : in std_logic_vector(cycle_cnt_width_g-1 downto 0); cycle_reg_o : out std_logic_vector(cycle_cnt_width_g-1 downto 0); cycle_reg_we_i : in std_logic; -- Watchdog wdog_i : in std_logic; wdog_we_i : in std_logic; -- BRAM access mem_clk_i : in std_logic; mem_en_i : in std_logic; mem_we_i : in std_logic_vector(1 downto 0); mem_addr_i : in std_logic_vector(8 downto 0); mem_data_i : in std_logic_vector(15 downto 0); mem_data_o : out std_logic_vector(15 downto 0) ); end lxmaster_transmitter; architecture Behavioral of lxmaster_transmitter is -- Types type state_t is (ST_INIT, ST_READY, ST_XFER, ST_CRC, ST_END); constant frame_length_c : positive := 2500; -- 50 MHz -> 20 kHz constant wdog_length_c : positive := 2500000; -- 50 ms constant msg_max_count_c : positive := 8; -- RAM Access signal ram_en_s : std_logic; signal ram_rst_s : std_logic; signal ram_addr_s : std_logic_vector(8 downto 0); signal ram_addr_r : std_logic_vector(8 downto 0); signal ram_data_o_s : std_logic_vector(15 downto 0); -- State signal lxmaster_state_s : state_t; signal lxmaster_state_r : state_t; signal lxmaster_last_word_s : std_logic; signal lxmaster_last_word_r : std_logic; -- Data signal lxmaster_num_data_s : std_logic_vector(7 downto 0); -- If 0 then the peripheral is not active this cycle signal lxmaster_num_data_r : std_logic_vector(7 downto 0); signal lxmaster_loaded_data_s : std_logic_vector(15 downto 0); signal lxmaster_loaded_data_r : std_logic_vector(15 downto 0); signal lxmaster_data_counter_s : natural range 0 to 15; signal lxmaster_data_counter_r : natural range 0 to 15; -- Transmission signal lxmaster_sync_s : std_logic; signal lxmaster_sync_r : std_logic; signal lxmaster_sync_last_bit_s : std_logic; signal lxmaster_sync_last_bit_r : std_logic; -- Counters signal lxmaster_wdog_counter_s : natural range 0 to (wdog_length_c-1); signal lxmaster_frame_counter_s : natural range 0 to (2**cycle_cnt_width_g - 1); signal lxmaster_cycle_limit_r : std_logic_vector(cycle_cnt_width_g-1 downto 0); signal lxmaster_msg_counter_s : natural range 0 to (msg_max_count_c-1); signal lxmaster_msg_counter_r : natural range 0 to (msg_max_count_c-1); -- CRC signal lxmaster_crc_data_s : std_logic; signal lxmaster_crc_reset_s : std_logic; signal lxmaster_crc_out_s : std_logic_vector(7 downto 0); signal lxmaster_crc_reg_s : std_logic_vector(7 downto 0); signal lxmaster_crc_reg_r : std_logic_vector(7 downto 0); -- RAM reset signal lxmaster_ram_reset_s : std_logic; -- Register -- Bit 0: Transmitter - use first or second buffer (I/O) -- Bit 1: Watchdog not kicked (O) signal lxmaster_register_in_s : std_logic; signal lxmaster_register_out_s : std_logic_vector(1 downto 0); -- Output buffers signal sync_s : std_logic; signal sync_r : std_logic; signal mosi_s : std_logic; signal mosi_r : std_logic; begin -- Directly route out some signals clock_o <= clk_i; -- Cannot mix with logic sync_s <= not lxmaster_sync_r; -- Active in log. 0 mosi_s <= '1' when lxmaster_sync_r = '0' and lxmaster_sync_last_bit_r = '0' else lxmaster_crc_reg_r(0) when lxmaster_state_r = ST_CRC else lxmaster_loaded_data_r(0); sync_o <= sync_r; mosi_o <= mosi_r; -- CRC lxmaster_crc_reg_s <= '0' & lxmaster_crc_reg_r(7 downto 1) when lxmaster_state_r = ST_CRC else lxmaster_crc_out_s; lxmaster_crc_data_s <= '1' when lxmaster_state_r = ST_CRC else lxmaster_loaded_data_r(0); lxmaster_crc_reset_s <= '1' when lxmaster_state_r /= ST_XFER or reset_i = '1' else '0'; -- Registers output register_o <= lxmaster_register_out_s; cycle_reg_o <= lxmaster_cycle_limit_r; ram: xilinx_dualport_bram generic map ( we_width => 2, byte_width => 8, address_width => 9, port_a_type => WRITE_FIRST, port_b_type => READ_FIRST ) port map ( -- Internal clka => clk_i, rsta => ram_rst_s, ena => ram_en_s, wea => (others => '0'), addra => ram_addr_s, dina => (others => '0'), douta => ram_data_o_s, -- External clkb => mem_clk_i, rstb => '0', enb => mem_en_i, web => mem_we_i, addrb => mem_addr_i, dinb => mem_data_i, doutb => mem_data_o ); crc_out: crc port map ( clk_i => clk_i, reset_i => lxmaster_crc_reset_s, input_i => lxmaster_crc_data_s, crc_o => lxmaster_crc_out_s ); ram_rst_s <= reset_i or lxmaster_ram_reset_s; -- Update -- TODO: Maybe some exception handling (overflows etc.) transmitter_update: process (ram_data_o_s, ram_addr_r, lxmaster_state_r, lxmaster_loaded_data_r, lxmaster_num_data_r, lxmaster_msg_counter_r, lxmaster_last_word_r, lxmaster_data_counter_r, reset_i, lxmaster_frame_counter_s, lxmaster_register_in_s, lxmaster_wdog_counter_s) variable ram_addr_v : std_logic_vector(8 downto 0); begin -- Defaults lxmaster_ram_reset_s <= '0'; lxmaster_sync_last_bit_s <= '0'; -- Defaults of state variables (no change) ram_addr_s <= ram_addr_r; lxmaster_state_s <= lxmaster_state_r; lxmaster_loaded_data_s <= lxmaster_loaded_data_r; lxmaster_data_counter_s <= lxmaster_data_counter_r; lxmaster_num_data_s <= lxmaster_num_data_r; lxmaster_msg_counter_s <= lxmaster_msg_counter_r; lxmaster_last_word_s <= lxmaster_last_word_r; if reset_i = '1' then lxmaster_num_data_s <= x"00"; lxmaster_loaded_data_s <= x"0000"; lxmaster_state_s <= ST_END; lxmaster_sync_s <= '0'; lxmaster_last_word_s <= '0'; -- ram_addr_s <= '0' & x"00"; ram_en_s <= '0'; -- lxmaster_ram_reset_s <= '1'; else -- OK, we are enabled, default values lxmaster_sync_s <= '0'; -- Not transferring ram_en_s <= '0'; -- Not reading case lxmaster_state_r is when ST_INIT => -- We just read number of commands if ram_data_o_s(7 downto 0) = x"00" then lxmaster_state_s <= ST_END; --Nothing else lxmaster_state_s <= ST_READY; -- Make next read init the transfer ram_addr_v := ram_addr_r(8) & ram_data_o_s(15 downto 8); -- Update address ram_addr_s <= ram_addr_v; lxmaster_num_data_s <= std_logic_vector(unsigned(ram_addr_v(7 downto 0)) + unsigned(ram_data_o_s(7 downto 0))); ram_en_s <= '1'; -- Read end if; -- Prepare message counter lxmaster_msg_counter_s <= 0; when ST_READY => -- We are ready to begin transferring lxmaster_loaded_data_s <= ram_data_o_s; lxmaster_sync_s <= '1'; -- Transferring data next cycle lxmaster_data_counter_s <= 0; -- Reset counter lxmaster_state_s <= ST_XFER; --Go to transfer loop when ST_XFER => lxmaster_loaded_data_s <= '0' & lxmaster_loaded_data_r(15 downto 1); -- Shift it lxmaster_sync_s <= '1'; -- Transferring data next cycle if lxmaster_data_counter_r = 14 then lxmaster_data_counter_s <= lxmaster_data_counter_r + 1; -- Increment if (ram_addr_r(7 downto 0) + 1) /= lxmaster_num_data_r then -- At 14th bit, we need to read from RAM if more words are to come ram_addr_s <= ram_addr_r(8) & (ram_addr_r(7 downto 0) + 1); -- Update address ram_en_s <= '1'; -- Read lxmaster_last_word_s <= '0'; else lxmaster_last_word_s <= '1'; end if; elsif lxmaster_data_counter_r = 15 then -- At 15th bit, we either stop if ram_addr_r equals lxmaster_num_data_r if lxmaster_last_word_r = '1' then lxmaster_state_s <= ST_CRC; lxmaster_data_counter_s <= 0; else lxmaster_loaded_data_s <= ram_data_o_s; lxmaster_data_counter_s <= 0; end if; else -- Increment lxmaster_data_counter_s <= lxmaster_data_counter_r + 1; end if; when ST_CRC => -- Check if this is last command, first read one more if lxmaster_data_counter_r = 0 then if lxmaster_msg_counter_r = (msg_max_count_c - 1) then lxmaster_ram_reset_s <= '1'; -- Make sure we read 0 if we are on the last message else lxmaster_msg_counter_s <= lxmaster_msg_counter_r + 1; end if; ram_addr_s <= ram_addr_r(8) & std_logic_vector(to_unsigned(lxmaster_msg_counter_r + 1, ram_addr_r'length - 1)); -- Update address ram_en_s <= '1'; -- Read elsif lxmaster_data_counter_r = 1 then if ram_data_o_s(15 downto 8) >= msg_max_count_c then -- Need to read first command, make sure it's valid ram_addr_v := ram_addr_r(8) & ram_data_o_s(15 downto 8); -- Update address ram_addr_s <= ram_addr_v; lxmaster_num_data_s <= std_logic_vector(unsigned(ram_addr_v(7 downto 0)) + unsigned(ram_data_o_s(7 downto 0))); ram_en_s <= '1'; -- Read else lxmaster_num_data_s <= x"00"; -- Signalize termination end if; end if; if lxmaster_data_counter_r = 7 then -- Ending lxmaster_data_counter_s <= 0; if lxmaster_num_data_r = x"00" then lxmaster_state_s <= ST_END; -- Last command else -- Begin transmission of next data lxmaster_loaded_data_s <= ram_data_o_s; lxmaster_sync_s <= '1'; -- Transferring data next cycle lxmaster_data_counter_s <= 0; -- Reset counter lxmaster_state_s <= ST_XFER; --Go to transfer loop end if; else -- Sync goes inactive to signalize termination if we're on last message if lxmaster_data_counter_r /= 6 or lxmaster_num_data_r /= x"00" then lxmaster_sync_s <= '1'; else lxmaster_sync_last_bit_s <= '1'; end if; lxmaster_data_counter_s <= lxmaster_data_counter_r + 1; -- Increment end if; when ST_END => if lxmaster_frame_counter_s < 2 then -- Initialize first step lxmaster_state_s <= ST_INIT; ram_addr_s <= lxmaster_register_in_s & x"00"; ram_en_s <= '1'; -- Watchdog - make sure we read 0 if not kicked if lxmaster_wdog_counter_s = (wdog_length_c - 1) then lxmaster_ram_reset_s <= '1'; end if; end if; end case; end if; end process; -- This function toggles outputs directly, make sure the outputs -- are updated with clock to minimize phase. state: process begin wait until clk_i'event and clk_i = '1'; -- State update ram_addr_r <= ram_addr_s; lxmaster_state_r <= lxmaster_state_s; lxmaster_loaded_data_r <= lxmaster_loaded_data_s; lxmaster_data_counter_r <= lxmaster_data_counter_s; lxmaster_num_data_r <= lxmaster_num_data_s; lxmaster_crc_reg_r <= lxmaster_crc_reg_s; lxmaster_msg_counter_r <= lxmaster_msg_counter_s; lxmaster_last_word_r <= lxmaster_last_word_s; -- Increment counter if reset_i = '1' then lxmaster_frame_counter_s <= 1; lxmaster_wdog_counter_s <= 0; lxmaster_register_in_s <= '0'; lxmaster_register_out_s <= (others => '0'); lxmaster_last_word_r <= '1'; lxmaster_cycle_limit_r <= std_logic_vector(to_unsigned(frame_length_c, lxmaster_cycle_limit_r'length)); else if register_we_i = '1' then lxmaster_register_in_s <= register_i; end if; if lxmaster_frame_counter_s < 2 then lxmaster_register_out_s(0) <= lxmaster_register_in_s; lxmaster_frame_counter_s <= to_integer(unsigned(lxmaster_cycle_limit_r)); else lxmaster_frame_counter_s <= lxmaster_frame_counter_s - 1; end if; if wdog_we_i = '1' and wdog_i = '1' then lxmaster_wdog_counter_s <= 0; lxmaster_register_out_s(1) <= '0'; elsif lxmaster_wdog_counter_s /= (wdog_length_c - 1) then lxmaster_wdog_counter_s <= lxmaster_wdog_counter_s + 1; lxmaster_register_out_s(1) <= '0'; else lxmaster_register_out_s(1) <= '1'; end if; if cycle_reg_we_i = '1' then lxmaster_cycle_limit_r <= cycle_reg_i; end if; end if; lxmaster_sync_r <= lxmaster_sync_s; lxmaster_sync_last_bit_r <= lxmaster_sync_last_bit_s; end process; update_outputs: process begin wait until clk_i'event and clk_i = '0'; sync_r <= sync_s; mosi_r <= mosi_s; end process; end Behavioral;