---------------------------------------------------------------------------------- -- -- Copyright (c) 2021 Erwin Rol -- -- SPDX-License-Identifier: MIT -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library UNISIM; use UNISIM.VComponents.all; Library UNIMACRO; use UNIMACRO.vcomponents.all; entity dmx_tx is generic ( C_REG_ADDR_WIDTH : integer := 10; C_REG_DATA_WIDTH : integer := 32; C_MEM_ADDR_WIDTH : integer := 32; C_MEM_DATA_WIDTH : integer := 32 ); port ( clk_i : in std_logic; rst_i : in std_logic; reg_rd_adr_i : in std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0); reg_rd_dat_o : out std_logic_vector(C_REG_DATA_WIDTH-1 downto 0); reg_wr_adr_i : in std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0); reg_wr_dat_i : in std_logic_vector(C_REG_DATA_WIDTH-1 downto 0); reg_wr_we_i : in std_logic_vector((C_REG_DATA_WIDTH/8)-1 downto 0); bram_addr_o : out std_logic_vector(C_MEM_ADDR_WIDTH-1 downto 0); bram_dat_o : out std_logic_vector(C_MEM_DATA_WIDTH-1 downto 0); bram_dat_i : in std_logic_vector(C_MEM_DATA_WIDTH-1 downto 0); bram_we_o : out std_logic_vector((C_MEM_DATA_WIDTH/8)-1 downto 0); bram_en_o : out std_logic; led0_o : out std_logic; led1_o : out std_logic; pb_i : in std_logic; sw_i : in std_logic; irq_o : out std_logic; tx_o : out std_logic ); end dmx_tx; architecture behavioral of dmx_tx is constant MAGIC_VALUE : std_logic_vector(C_REG_DATA_WIDTH-1 downto 0) := x"ABCDEF10"; constant VERSION_VALUE : std_logic_vector(C_REG_DATA_WIDTH-1 downto 0) := x"01010105"; -- -- Register addresses -- constant REG_NR_MAGIC : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned( 0, C_REG_ADDR_WIDTH)); constant REG_NR_VERSION : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned( 1, C_REG_ADDR_WIDTH)); constant REG_NR_BYTE_CNTR : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned( 2, C_REG_ADDR_WIDTH)); constant REG_NR_BIT_CNTR : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned( 3, C_REG_ADDR_WIDTH)); constant REG_NR_MBB_TIME_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(10, C_REG_ADDR_WIDTH)); constant REG_NR_BREAK_TIME_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(11, C_REG_ADDR_WIDTH)); constant REG_NR_MAB_TIME_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(12, C_REG_ADDR_WIDTH)); constant REG_NR_MAD_TIME_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(13, C_REG_ADDR_WIDTH)); constant REG_NR_START_CODE_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(14, C_REG_ADDR_WIDTH)); constant REG_NR_FRAME_LEN_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(15, C_REG_ADDR_WIDTH)); constant REG_NR_MEM_PTR_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(16, C_REG_ADDR_WIDTH)); constant REG_NR_STATUS_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(17, C_REG_ADDR_WIDTH)); constant REG_NR_CONFIG_1 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(18, C_REG_ADDR_WIDTH)); constant REG_NR_MBB_TIME_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(20, C_REG_ADDR_WIDTH)); constant REG_NR_BREAK_TIME_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(21, C_REG_ADDR_WIDTH)); constant REG_NR_MAB_TIME_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(22, C_REG_ADDR_WIDTH)); constant REG_NR_MAD_TIME_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(23, C_REG_ADDR_WIDTH)); constant REG_NR_START_CODE_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(24, C_REG_ADDR_WIDTH)); constant REG_NR_FRAME_LEN_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(25, C_REG_ADDR_WIDTH)); constant REG_NR_MEM_PTR_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(26, C_REG_ADDR_WIDTH)); constant REG_NR_STATUS_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(27, C_REG_ADDR_WIDTH)); constant REG_NR_CONFIG_2 : std_logic_vector(C_REG_ADDR_WIDTH-1 downto 0) := std_logic_vector(to_unsigned(28, C_REG_ADDR_WIDTH)); function us_to_tick(t : integer) return std_logic_vector is variable res : std_logic_vector(31 downto 0); begin if (t = 0) then res := (others => '0'); else res := std_logic_vector( to_unsigned((t * 100) - 1, res'length) ); end if; return res; end function; constant BIT_TIME : unsigned(31 downto 0) := unsigned(us_to_tick(4)); type desc_type is record mbb_time : unsigned(31 downto 0); break_time : unsigned(31 downto 0); mab_time : unsigned(31 downto 0); mad_time : unsigned(31 downto 0); start_code : std_logic_vector(7 downto 0); frame_len : std_logic_vector(9 downto 0); mem_ptr : std_logic_vector(31 downto 0); status : std_logic_vector(31 downto 0); config : std_logic_vector(31 downto 0); end record; constant reset_desc : desc_type := ( mbb_time => unsigned(us_to_tick(100)), break_time => unsigned(us_to_tick(200)), mab_time => unsigned(us_to_tick(100)), mad_time => unsigned(us_to_tick(0)), start_code => x"00", frame_len => (others => '0'), mem_ptr => (others => '0'), status => (others => '0'), config => (others => '0') ); type state_type is ( st_idle, st_mbb, st_break, st_mab, st_mad, st_start_bit, st_data_bit_0, st_data_bit_1, st_data_bit_2, st_data_bit_3, st_data_bit_4, st_data_bit_5, st_data_bit_6, st_data_bit_7, st_stop_bit_0, st_stop_bit_1 ); type reg_type is record tx_o : std_logic; irq_o : std_logic; led0_o : std_logic; led1_o : std_logic; bram_addr_o : std_logic_vector(C_MEM_ADDR_WIDTH-1 downto 0); bram_dat_o : std_logic_vector(C_MEM_DATA_WIDTH-1 downto 0); bram_we_o : std_logic_vector((C_MEM_DATA_WIDTH/8)-1 downto 0); bram_en_o : std_logic; current_desc : desc_type; next_desc : desc_type; state : state_type; byte_counter : unsigned(9 downto 0); bit_counter : unsigned(31 downto 0); current_byte : std_logic_vector(7 downto 0); end record; constant reset_reg : reg_type := ( tx_o => '0', irq_o => '0', led0_o => '0', led1_o => '0', bram_addr_o => (others => '0'), bram_dat_o => (others => '0'), bram_we_o => (others => '0'), bram_en_o => '0', current_desc => reset_desc, next_desc => reset_desc, state => st_idle, byte_counter => (others => '-'), bit_counter => (others => '-'), current_byte => (others => '-') ); signal reg : reg_type; signal next_reg : reg_type; begin process(reg, rst_i, reg_wr_we_i, reg_wr_adr_i, reg_wr_dat_i, reg_rd_adr_i, bram_dat_i, pb_i, sw_i) variable v : reg_type; begin v := reg; v.bram_en_o := '1'; -- -- TX FSM -- v.bit_counter := reg.bit_counter - 1; case reg.state is when st_idle => v.tx_o := '1'; if (unsigned(reg.current_desc.frame_len) /= 0) then v.bit_counter := reg.current_desc.mbb_time; v.state := st_mbb; end if; when st_mbb => if (reg.bit_counter = 0) then v.tx_o := '0'; v.bit_counter := reg.current_desc.break_time; v.state := st_break; end if; when st_break => if (reg.bit_counter = 0) then v.tx_o := '1'; v.bit_counter := reg.current_desc.mab_time; v.state := st_mab; end if; when st_mab => if (reg.bit_counter = 0) then v.tx_o := '0'; v.bit_counter := BIT_TIME; v.state := st_start_bit; v.current_byte := reg.current_desc.start_code; v.byte_counter := (others => '0'); end if; when st_start_bit => if (reg.bit_counter = 0) then v.bram_addr_o := std_logic_vector(unsigned(reg.current_desc.mem_ptr) + reg.byte_counter); v.tx_o := reg.current_byte(0); v.bit_counter := BIT_TIME; v.state := st_data_bit_0; end if; when st_data_bit_0 => if (reg.bit_counter = 0) then v.tx_o := reg.current_byte(1); v.bit_counter := BIT_TIME; v.state := st_data_bit_1; end if; when st_data_bit_1 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(2); v.state := st_data_bit_2; end if; when st_data_bit_2 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(3); v.state := st_data_bit_3; end if; when st_data_bit_3 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(4); v.state := st_data_bit_4; end if; when st_data_bit_4 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(5); v.state := st_data_bit_5; end if; when st_data_bit_5 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(6); v.state := st_data_bit_6; end if; when st_data_bit_6 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := reg.current_byte(7); v.state := st_data_bit_7; end if; when st_data_bit_7 => if (reg.bit_counter = 0) then v.bit_counter := BIT_TIME; v.tx_o := '1'; v.state := st_stop_bit_0; end if; when st_stop_bit_0 => case reg.bram_addr_o(1 downto 0) is when "00" => v.current_byte := bram_dat_i( 7 downto 0); when "01" => v.current_byte := bram_dat_i(15 downto 8); when "10" => v.current_byte := bram_dat_i(23 downto 16); when "11" => v.current_byte := bram_dat_i(31 downto 24); when others => null; end case; if (reg.bit_counter = 0) then v.byte_counter := reg.byte_counter + 1; v.bit_counter := BIT_TIME; v.tx_o := '1'; v.state := st_stop_bit_1; end if; when st_stop_bit_1 => if (reg.bit_counter = 0) then if (reg.byte_counter = unsigned(reg.current_desc.frame_len)) then v.tx_o := '1'; if (unsigned(reg.next_desc.frame_len) /= 0) then -- get the next frame v.current_desc := reg.next_desc; v.next_desc := reset_desc; v.state := st_mbb; v.bit_counter := reg.next_desc.mbb_time; else v.current_desc := reset_desc; v.state := st_idle; --v.bit_counter := reg.current_desc.mbb_time; end if; else if (reg.current_desc.mad_time = 0) then v.tx_o := '0'; v.bit_counter := BIT_TIME; v.state := st_start_bit; else v.tx_o := '0'; v.bit_counter := reg.current_desc.mad_time; v.state := st_mad; end if; end if; end if; when st_mad => if (reg.bit_counter = 0) then v.tx_o := '0'; v.bit_counter := BIT_TIME; v.state := st_start_bit; end if; when others => v.state := st_idle; end case; v.led0_o := sw_i; v.led1_o := pb_i; -- -- handle AXI register writes -- if (reg_wr_we_i = "1111") then case reg_wr_adr_i is when REG_NR_MBB_TIME_1 => v.current_desc.mbb_time := unsigned(reg_wr_dat_i); when REG_NR_BREAK_TIME_1 => v.current_desc.break_time := unsigned(reg_wr_dat_i); when REG_NR_MAB_TIME_1 => v.current_desc.mab_time := unsigned(reg_wr_dat_i); when REG_NR_MAD_TIME_1 => v.current_desc.mad_time := unsigned(reg_wr_dat_i); when REG_NR_START_CODE_1 => v.current_desc.start_code := reg_wr_dat_i(7 downto 0); when REG_NR_FRAME_LEN_1 => v.current_desc.frame_len := reg_wr_dat_i(9 downto 0); when REG_NR_MEM_PTR_1 => v.current_desc.mem_ptr := reg_wr_dat_i; when REG_NR_CONFIG_1 => v.current_desc.config := reg_wr_dat_i; when REG_NR_MBB_TIME_2 => v.next_desc.mbb_time := unsigned(reg_wr_dat_i); when REG_NR_BREAK_TIME_2 => v.next_desc.break_time := unsigned(reg_wr_dat_i); when REG_NR_MAB_TIME_2 => v.next_desc.mab_time := unsigned(reg_wr_dat_i); when REG_NR_MAD_TIME_2 => v.next_desc.mad_time := unsigned(reg_wr_dat_i); when REG_NR_START_CODE_2 => v.next_desc.start_code := reg_wr_dat_i(7 downto 0); when REG_NR_FRAME_LEN_2 => v.next_desc.frame_len := reg_wr_dat_i(9 downto 0); when REG_NR_MEM_PTR_2 => v.next_desc.mem_ptr := reg_wr_dat_i; when REG_NR_CONFIG_2 => v.next_desc.config := reg_wr_dat_i; when others => null; end case; end if; -- -- Reset handling -- if (rst_i = '1') then v := reset_reg; end if; -- -- Assign outputs -- reg_rd_dat_o <= (others => '-'); case reg_rd_adr_i is when REG_NR_MAGIC => reg_rd_dat_o <= MAGIC_VALUE; when REG_NR_VERSION => reg_rd_dat_o <= VERSION_VALUE; when REG_NR_BYTE_CNTR => reg_rd_dat_o(9 downto 0) <= std_logic_vector(reg.byte_counter); when REG_NR_BIT_CNTR => reg_rd_dat_o <= std_logic_vector(reg.bit_counter); when REG_NR_MBB_TIME_1 => reg_rd_dat_o <= std_logic_vector(reg.current_desc.mbb_time); when REG_NR_BREAK_TIME_1 => reg_rd_dat_o <= std_logic_vector(reg.current_desc.break_time); when REG_NR_MAB_TIME_1 => reg_rd_dat_o <= std_logic_vector(reg.current_desc.mab_time); when REG_NR_MAD_TIME_1 => reg_rd_dat_o <= std_logic_vector(reg.current_desc.mad_time); when REG_NR_START_CODE_1 => reg_rd_dat_o(7 downto 0) <= reg.current_desc.start_code; when REG_NR_FRAME_LEN_1 => reg_rd_dat_o(9 downto 0) <= reg.current_desc.frame_len; when REG_NR_MEM_PTR_1 => reg_rd_dat_o <= reg.current_desc.mem_ptr; when REG_NR_STATUS_1 => reg_rd_dat_o <= reg.current_desc.status; when REG_NR_CONFIG_1 => reg_rd_dat_o <= reg.current_desc.config; when REG_NR_MBB_TIME_2 => reg_rd_dat_o <= std_logic_vector(reg.next_desc.mbb_time); when REG_NR_BREAK_TIME_2 => reg_rd_dat_o <= std_logic_vector(reg.next_desc.break_time); when REG_NR_MAB_TIME_2 => reg_rd_dat_o <= std_logic_vector(reg.next_desc.mab_time); when REG_NR_MAD_TIME_2 => reg_rd_dat_o <= std_logic_vector(reg.next_desc.mad_time); when REG_NR_START_CODE_2 => reg_rd_dat_o(7 downto 0) <= reg.next_desc.start_code; when REG_NR_FRAME_LEN_2 => reg_rd_dat_o(9 downto 0) <= reg.next_desc.frame_len; when REG_NR_MEM_PTR_2 => reg_rd_dat_o <= reg.next_desc.mem_ptr; when REG_NR_STATUS_2 => reg_rd_dat_o <= reg.next_desc.status; when REG_NR_CONFIG_2 => reg_rd_dat_o <= reg.next_desc.config; when others => reg_rd_dat_o <= (others => '-'); end case; irq_o <= reg.irq_o; tx_o <= reg.tx_o; led0_o <= reg.led0_o; led1_o <= reg.led1_o; bram_addr_o <= reg.bram_addr_o(31 downto 2) & "00"; -- allign to 32bit bram_dat_o <= reg.bram_dat_o; bram_we_o <= reg.bram_we_o; bram_en_o <= reg.bram_en_o; next_reg <= v; end process; process(clk_i) begin if (rising_edge(clk_i)) then reg <= next_reg; end if; end process; end behavioral;