From 47e53fff5639af37582f790df2136dcb61471afd Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Fri, 22 Apr 2016 17:20:13 +0200 Subject: [PATCH] Add AC97 output driver. --- synth/digilent_atlys/ac97out.vhdl | 180 ++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 synth/digilent_atlys/ac97out.vhdl diff --git a/synth/digilent_atlys/ac97out.vhdl b/synth/digilent_atlys/ac97out.vhdl new file mode 100644 index 0000000..fae2365 --- /dev/null +++ b/synth/digilent_atlys/ac97out.vhdl @@ -0,0 +1,180 @@ +-- +-- Simple core for AC97 audio output. +-- Only tested with LM4550 on Digilent Atlys board. +-- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ac97out is + + port ( + -- AC97 bit clock. + bitclk: in std_logic; + + -- Synchronous reset, active high. + rst: in std_logic; + + -- Input samples for left and right channel. + data_left: in signed(19 downto 0); + data_right: in signed(19 downto 0); + + -- Data handshake. + data_valid: in std_logic; + data_ready: out std_logic; + + -- AC97 interface signals. + ac97_sdo: out std_logic; + ac97_sync: out std_logic ); + +end entity; + +architecture rtl of ac97out is + + -- Initialization sequence. + type init_table_type is array(0 to 7) of std_logic_vector(23 downto 0); + constant init_table: init_table_type := ( + -- write 0x0000 to register 0x00: soft reset + x"000000", + -- write 0x0000 to register 0x02: set master volume to maximum + x"020000", + -- write 0x0000 to register 0x04: set headphone volume to maximum + x"040000", + -- write 0x0000 to register 0x06: set mono_out volume to maximum + x"060000", + -- write 0x0808 to register 0x18: set PCM out volume to 0 dB + x"180808", + -- dummy read from register 0x00 + x"800000", + x"800000", + x"800000" ); + + -- Output registers. + signal r_ready: std_logic; + signal r_sdo: std_logic; + signal r_sync: std_logic; + + -- Bit counter. + signal r_bitcnt: unsigned(7 downto 0); + signal r_lastbit: std_logic; + signal r_firstbit: std_logic; + + -- Initialization state machine. + signal r_initdone: std_logic; + signal r_initstep: unsigned(2 downto 0); + signal r_initword: std_logic_vector(23 downto 0); + + -- Data for next frame. + signal r_datavalid: std_logic; + signal r_dataleft: std_logic_vector(19 downto 0); + signal r_dataright: std_logic_vector(19 downto 0); + + -- Frame data shift register (tag + slots 1 .. 4) + signal r_sdoshift: std_logic_vector(95 downto 0); + +begin + + -- Drive outputs. + data_ready <= r_ready; + ac97_sdo <= r_sdo; + ac97_sync <= r_sync; + + -- Synchronous process. + process (bitclk) is + begin + if rising_edge(bitclk) then + + -- Drive SYNC high on first bit of frame. + r_sync <= r_firstbit; + + -- Push next data bit to output. + r_sdo <= r_sdoshift(r_sdoshift'high); + r_sdoshift <= r_sdoshift(r_sdoshift'high-1 downto 0) & "0"; + + -- Fetch data from init table. + r_initword <= init_table(to_integer(r_initstep)); + + -- Prepare next frame. + if r_lastbit = '1' then + + -- sdoshift(95:80) = TAG + -- bit 15 = master valid bit + -- bit 14 = slot 1 valid + -- bit 13 = slot 2 valid + -- bit 12 = slot 3 valid + -- bit 11 = slot 4 valid + + -- Always set frame valid. + r_sdoshift(95) <= '1'; + + -- Set slots 1 and 2 valid if we are initializing. + r_sdoshift(94) <= not r_initdone; + r_sdoshift(93) <= not r_initdone; + + -- Set slots 3 and 4 valid if we have valid data. + r_sdoshift(92) <= r_datavalid; + r_sdoshift(91) <= r_datavalid; + + -- Remaining tag bits always zero. + r_sdoshift(90 downto 80) <= (others => '0'); + + -- Slot 1: Register read/write command. + -- bit 19 = read (1) or write (0) + -- bit 18:12 = address + r_sdoshift(79 downto 72) <= r_initword(23 downto 16); + r_sdoshift(71 downto 0) <= (others => '0'); + + -- Slot 2: Register write data. + -- bit 19:4 = data + r_sdoshift(59 downto 44) <= r_initword(15 downto 0); + r_sdoshift(43 downto 40) <= (others => '0'); + + -- Update init pointer. + r_initstep <= r_initstep + 1; + if r_initstep = 7 then + r_initdone <= '1'; + end if; + + -- Slots 3 and 4: left and right sample value. + r_sdoshift(39 downto 20) <= r_dataleft; + r_sdoshift(19 downto 0) <= r_dataright; + + -- Consume sample values. + r_datavalid <= '0'; + + end if; + + -- Update bit counter. + r_bitcnt <= r_bitcnt - 1; + r_firstbit <= r_lastbit; + if r_bitcnt = 1 then + r_lastbit <= '1'; + else + r_lastbit <= '0'; + end if; + + -- Capture input data. + if r_ready = '1' and data_valid = '1' then + r_datavalid <= '1'; + r_dataleft <= std_logic_vector(data_left); + r_dataright <= std_logic_vector(data_right); + end if; + + -- Update ready flag. + r_ready <= (not r_datavalid) and (not data_valid); + + -- Synchronous reset. + if rst = '1' then + r_initdone <= '0'; + r_initstep <= (others => '0'); + r_bitcnt <= (others => '0'); + r_firstbit <= '0'; + r_datavalid <= '0'; + end if; + + end if; + end process; + +end architecture; +