URL
https://opencores.org/ocsvn/spacewire_light/spacewire_light/trunk
Subversion Repositories spacewire_light
[/] [spacewire_light/] [trunk/] [rtl/] [vhdl/] [spwahbmst.vhd] - Rev 12
Compare with Previous | Blame | View Log
-- -- AHB master for AMBA interface. -- -- This is a helper entity for the SpaceWire AMBA interface. -- It implements the AHB master which transfers data from/to main memory. -- -- Descriptor flag bits on input: -- bit 15:0 (RX) max nr of bytes to receive (must be a multiple of 4) -- (TX) nr of bytes to transmit -- bit 16 EN: '1' = descriptor enabled -- bit 17 WR: wrap to beginning of descriptor table -- bit 18 IE: interrupt at end of descriptor -- bit 19 '0' -- bit 20 (TX only) send EOP after end of data -- bit 21 (TX only) send EEP after end of data -- -- Descriptor flag bits after completion of frame: -- bit 15:0 (RX only) LEN: nr of bytes received -- (TX) undefined -- bit 16 '0' -- bit 18:17 undefined -- bit 19 '1' to indicate descriptor completed --- bit 20 (RX only) received EOP after end of data -- bit 21 (RX only) received EEP after end of data -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library grlib; use grlib.amba.all; use grlib.stdlib.all; use work.spwambapkg.all; entity spwahbmst is generic ( -- AHB master index. hindex: integer; -- AHB plug&play information. hconfig: ahb_config_type; -- Maximum burst length as the 2-logarithm of the number of words. maxburst: integer range 1 to 8 ); port ( -- System clock. clk: in std_logic; -- Synchronous reset (active-low). rstn: in std_logic; -- Inputs from SpaceWire core. msti: in spw_ahbmst_in_type; -- Outputs to SpaceWire core. msto: out spw_ahbmst_out_type; -- AHB master input signals. ahbi: in ahb_mst_in_type; -- AHB master output signals. ahbo: out ahb_mst_out_type ); end entity spwahbmst; architecture spwahbmst_arch of spwahbmst is -- -- Registers. -- type state_type is ( st_idle, st_rxgetdesc, st_rxgetptr, st_rxtransfer, st_rxfinal, st_rxputdesc, st_txgetdesc, st_txgetptr, st_txtransfer, st_txfinal, st_txputdesc, st_txskip ); type burst_state_type is ( bs_idle, bs_setup, bs_active, bs_end ); type regs_type is record -- dma state rxdma_act: std_ulogic; txdma_act: std_ulogic; ahberror: std_ulogic; -- main state machine mstate: state_type; firstword: std_ulogic; prefertx: std_ulogic; -- rx descriptor state rxdes_en: std_ulogic; rxdes_wr: std_ulogic; rxdes_ie: std_ulogic; rxdes_eop: std_ulogic; rxdes_eep: std_ulogic; rxdes_len: std_logic_vector(13 downto 0); -- in 32-bit words rxdes_pos: std_logic_vector(15 downto 0); -- in bytes rxaddr: std_logic_vector(31 downto 2); rxdesc_next: std_ulogic; -- tx descriptor state txdes_en: std_ulogic; txdes_wr: std_ulogic; txdes_ie: std_ulogic; txdes_eop: std_ulogic; txdes_eep: std_ulogic; txdes_len: std_logic_vector(15 downto 0); -- in bytes txaddr: std_logic_vector(31 downto 2); txdesc_next: std_ulogic; -- interrupts int_rxdesc: std_ulogic; int_txdesc: std_ulogic; int_rxpacket: std_ulogic; -- burst state burststat: burst_state_type; hbusreq: std_ulogic; hwrite: std_ulogic; haddr: std_logic_vector(31 downto 2); hwdata: std_logic_vector(31 downto 0); end record; constant regs_reset: regs_type := ( rxdma_act => '0', txdma_act => '0', ahberror => '0', mstate => st_idle, firstword => '0', prefertx => '0', rxdes_en => '0', rxdes_wr => '0', rxdes_ie => '0', rxdes_eop => '0', rxdes_eep => '0', rxdes_len => (others => '0'), rxdes_pos => (others => '0'), rxaddr => (others => '0'), rxdesc_next => '0', txdes_en => '0', txdes_wr => '0', txdes_ie => '0', txdes_eop => '0', txdes_eep => '0', txdes_len => (others => '0'), txaddr => (others => '0'), txdesc_next => '0', int_rxdesc => '0', int_txdesc => '0', int_rxpacket => '0', burststat => bs_idle, hbusreq => '0', hwrite => '0', haddr => (others => '0'), hwdata => (others => '0') ); signal r: regs_type := regs_reset; signal rin: regs_type; begin -- -- Combinatorial process -- process (r, rstn, msti, ahbi) is variable v: regs_type; variable v_hrdata: std_logic_vector(31 downto 0); variable v_burstreq: std_logic; variable v_burstack: std_logic; variable v_rxfifo_read: std_logic; variable v_txfifo_write: std_logic; variable v_txfifo_wdata: std_logic_vector(35 downto 0); begin v := r; -- Decode AHB data bus (64-bit AHB compatibility). v_hrdata := ahbreadword(ahbi.hrdata); -- Assume no burst request. v_burstreq := '0'; -- Detect request from burst state machine for next data word. v_burstack := ahbi.hready and conv_std_logic(r.burststat = bs_active or r.burststat = bs_end); -- Assume no fifo activity; take data for TX fifo from AHB bus. v_rxfifo_read := '0'; v_txfifo_write := '0'; v_txfifo_wdata(35 downto 32) := (others => '0'); v_txfifo_wdata(31 downto 0) := v_hrdata; -- Reset registers for interrupts and descriptor updates. v.int_rxdesc := '0'; v.int_txdesc := '0'; v.int_rxpacket := '0'; v.rxdesc_next := '0'; v.txdesc_next := '0'; -- Start DMA on external request. if msti.rxdma_start = '1' then v.rxdma_act := '1'; end if; if msti.txdma_start = '1' then v.txdma_act := '1'; end if; -- -- Main state machine. -- case r.mstate is when st_idle => -- Waiting for something to do. v.prefertx := '0'; v.firstword := '1'; if msti.txdma_cancel = '1' then v.txdma_act := '0'; v.txdes_en := '0'; end if; if r.rxdma_act = '1' and msti.rxfifo_empty = '0' and (r.prefertx = '0' or r.txdma_act = '0' or msti.txfifo_highw = '1') then -- Start RX transfer. if r.rxdes_en = '1' then -- Transfer RX data to current descriptor. v_burstreq := '1'; v.hwrite := '1'; v.haddr := r.rxaddr; v.mstate := st_rxtransfer; else -- Must fetch new RX descriptor. v_burstreq := '1'; v.hwrite := '0'; v.haddr := msti.rxdesc_ptr & "0"; v.mstate := st_rxgetdesc; end if; elsif r.txdma_act = '1' and msti.txdma_cancel = '0' and msti.txfifo_highw = '0' then -- Start TX transfer. if r.txdes_en = '1' then -- Transfer TX data from current descriptor. if unsigned(r.txdes_len) = 0 then -- Only send EOP/EEP and write back descriptor. v_burstreq := '1'; v.hwrite := '1'; v.haddr := msti.txdesc_ptr & "0"; v.txdesc_next := '1'; v.mstate := st_txputdesc; else -- Start burst transfer. v_burstreq := '1'; v.hwrite := '0'; v.haddr := r.txaddr; if unsigned(r.txdes_len) <= 4 then -- Transfer only one word. v.mstate := st_txfinal; else v.mstate := st_txtransfer; end if; end if; else -- Must fetch new TX descriptor. v_burstreq := '1'; v.hwrite := '0'; v.haddr := msti.txdesc_ptr & "0"; v.mstate := st_txgetdesc; end if; end if; when st_rxgetdesc => -- Read RX descriptor flags from memory. v_burstreq := '1'; v.hwrite := '0'; v.rxdes_len := v_hrdata(15 downto 2); v.rxdes_en := v_hrdata(16); v.rxdes_wr := v_hrdata(17); v.rxdes_ie := v_hrdata(18); v.rxdes_eop := '0'; v.rxdes_eep := '0'; v.rxdes_pos := (others => '0'); if v_burstack = '1' then -- Got descriptor flags. v_burstreq := '0'; v.mstate := st_rxgetptr; end if; when st_rxgetptr => -- Read RX data pointer from memory. v.rxaddr := v_hrdata(31 downto 2); v.haddr := v_hrdata(31 downto 2); v.firstword := '1'; if v_burstack = '1' then -- Got data pointer. if r.rxdes_en = '1' then -- Start transfer. v_burstreq := '1'; v.hwrite := '1'; v.mstate := st_rxtransfer; else -- Reached end of valid descriptors; stop. v.rxdma_act := '0'; v.mstate := st_idle; end if; end if; when st_rxtransfer => -- Continue an RX transfer. v_burstreq := '1'; v.hwrite := '1'; v.firstword := '0'; if v_burstack = '1' or r.firstword = '1' then -- Setup first/next data word. v.hwdata := msti.rxfifo_rdata(31 downto 0); v_rxfifo_read := '1'; -- Update pointers. v.rxdes_len := std_logic_vector(unsigned(r.rxdes_len) - 1); v.rxdes_pos := std_logic_vector(unsigned(r.rxdes_pos) + 4); v.rxaddr := std_logic_vector(unsigned(r.rxaddr) + 1); -- Detect EOP/EEP. v.rxdes_eop := (msti.rxfifo_rdata(35) and not msti.rxfifo_rdata(24)) or (msti.rxfifo_rdata(34) and not msti.rxfifo_rdata(16)) or (msti.rxfifo_rdata(33) and not msti.rxfifo_rdata(8)) or (msti.rxfifo_rdata(32) and not msti.rxfifo_rdata(0)); v.rxdes_eep := (msti.rxfifo_rdata(35) and msti.rxfifo_rdata(24)) or (msti.rxfifo_rdata(34) and msti.rxfifo_rdata(16)) or (msti.rxfifo_rdata(33) and msti.rxfifo_rdata(8)) or (msti.rxfifo_rdata(32) and msti.rxfifo_rdata(0)); -- Adjust frame length in case of EOP/EEP. if msti.rxfifo_rdata(35) = '1' then v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "00"; elsif msti.rxfifo_rdata(34) = '1' then v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "01"; elsif msti.rxfifo_rdata(33) = '1' then v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "10"; elsif msti.rxfifo_rdata(32) = '1' then v.rxdes_pos := r.rxdes_pos(r.rxdes_pos'high downto 2) & "11"; end if; -- Stop at end of requested length or end of packet or fifo empty. if msti.rxfifo_nxempty = '1' or orv(msti.rxfifo_rdata(35 downto 32)) = '1' or unsigned(r.rxdes_len) = 1 then v_burstreq := '0'; v.mstate := st_rxfinal; end if; -- Stop at max burst length boundary. if (andv(r.rxaddr(maxburst+1 downto 2)) = '1') then v_burstreq := '0'; v.mstate := st_rxfinal; end if; end if; when st_rxfinal => -- Last data cycle of an RX transfer. if v_burstack = '1' then if unsigned(r.rxdes_len) = 0 or r.rxdes_eop = '1' or r.rxdes_eep = '1' then -- End of frame; write back descriptor. v_burstreq := '1'; v.hwrite := '1'; v.haddr := msti.rxdesc_ptr & "0"; v.rxdesc_next := '1'; v.mstate := st_rxputdesc; else -- Go through st_idle to pick up more work. v.mstate := st_idle; end if; end if; -- Give preference to TX work since we just did some RX work. v.prefertx := '1'; when st_rxputdesc => -- Write back RX descriptor. v.hwdata(15 downto 0) := r.rxdes_pos; v.hwdata(16) := '0'; v.hwdata(17) := r.rxdes_wr; v.hwdata(18) := r.rxdes_ie; v.hwdata(19) := '1'; v.hwdata(20) := r.rxdes_eop; v.hwdata(21) := r.rxdes_eep; v.hwdata(31 downto 22) := (others => '0'); if v_burstack = '1' then -- Frame done. v.rxdes_en := '0'; v.int_rxdesc := r.rxdes_ie; v.int_rxpacket := r.rxdes_eop or r.rxdes_eep; -- Go to st_idle. v.mstate := st_idle; end if; when st_txgetdesc => -- Read TX descriptor flags from memory. v_burstreq := '1'; v.hwrite := '0'; v.txdes_len := v_hrdata(15 downto 0); v.txdes_en := v_hrdata(16); v.txdes_wr := v_hrdata(17); v.txdes_ie := v_hrdata(18); v.txdes_eop := v_hrdata(20); v.txdes_eep := v_hrdata(21); if v_burstack = '1' then -- Got descriptor flags. v_burstreq := '0'; v.mstate := st_txgetptr; end if; when st_txgetptr => -- Read TX data pointer from memory. v.txaddr := v_hrdata(31 downto 2); if v_burstack = '1' then -- Got data pointer. if r.txdes_en = '1' then -- Start transfer. if unsigned(r.txdes_len) = 0 then -- Only send EOP/EEP and write back descriptor. v_burstreq := '1'; v.hwrite := '1'; v.haddr := msti.txdesc_ptr & "0"; v.txdesc_next := '1'; v.mstate := st_txputdesc; else v_burstreq := '1'; v.hwrite := '0'; v.haddr := v_hrdata(31 downto 2); if unsigned(r.txdes_len) <= 4 then -- Transfer only one word. v.mstate := st_txfinal; else v.mstate := st_txtransfer; end if; end if; else -- Reached end of valid descriptors; stop. v.txdma_act := '0'; v.mstate := st_idle; end if; end if; when st_txtransfer => -- Continue an TX transfer. v_burstreq := '1'; v.hwrite := '0'; if v_burstack = '1' then -- Got next data word from memory. v_txfifo_write := '1'; -- Update pointers. v.txdes_len := std_logic_vector(unsigned(r.txdes_len) - 4); v.txaddr := std_logic_vector(unsigned(r.txaddr) + 1); -- Handle end of burst/transfer. if andv(r.txaddr(maxburst+1 downto 2)) = '1' then -- This was the last data cycle before the max burst boundary. -- Go through st_idle to pick up more work. v_burstreq := '0'; v.mstate := st_idle; elsif msti.txfifo_nxfull = '1' then -- Fifo full; stop transfer, ignore final data cycle. v_burstreq := '0'; v.mstate := st_txskip; elsif unsigned(r.txdes_len) <= 8 then -- Stop at end of requested length (one more data cycle). v_burstreq := '0'; v.mstate := st_txfinal; elsif andv(r.txaddr(maxburst+1 downto 3)) = '1' then -- Stop at max burst length boundary (one more data cycle). v_burstreq := '0'; end if; else if andv(r.txaddr(maxburst+1 downto 2)) = '1' then -- Stop at max burst length boundary (just one more data cycle). v_burstreq := '0'; end if; end if; when st_txfinal => -- Last data cycle of a TX descriptor (1 <= txdes_len <= 4). if v_burstack = '1' then -- Got last data word from memory. v_txfifo_write := '1'; v.txdes_len := std_logic_vector(unsigned(r.txdes_len) - 4); -- Insert EOP in last word if needed. -- (Or set bit 7 in the flag byte to indicate that the -- frame ends while the packet continues.) case r.txdes_len(1 downto 0) is when "01" => v_txfifo_wdata(34) := '1'; v_txfifo_wdata(23) := not (r.txdes_eop or r.txdes_eep); v_txfifo_wdata(22 downto 17) := "000000"; v_txfifo_wdata(16) := r.txdes_eep; when "10" => v_txfifo_wdata(33) := '1'; v_txfifo_wdata(15) := not (r.txdes_eop or r.txdes_eep); v_txfifo_wdata(14 downto 9) := "000000"; v_txfifo_wdata(8) := r.txdes_eep; when "11" => v_txfifo_wdata(32) := '1'; v_txfifo_wdata(7) := not (r.txdes_eop or r.txdes_eep); v_txfifo_wdata(6 downto 1) := "000000"; v_txfifo_wdata(0) := r.txdes_eep; when others => -- txdes_len = 4 -- Store 4 data bytes now; store EOP in st_txputdesc (if needed). end case; if msti.txfifo_nxfull = '1' and r.txdes_len(1 downto 0) = "00" then -- Fifo full so no room to store EOP. v.mstate := st_idle; v.haddr := msti.txdesc_ptr & "0"; else -- Prepare to write back descriptor. v_burstreq := '1'; v.hwrite := '1'; v.haddr := msti.txdesc_ptr & "0"; v.txdesc_next := '1'; v.mstate := st_txputdesc; end if; end if; when st_txputdesc => -- Write back TX descriptor. v.hwdata(15 downto 0) := (others => '0'); v.hwdata(16) := '0'; v.hwdata(17) := r.txdes_wr; v.hwdata(18) := r.txdes_ie; v.hwdata(19) := '1'; v.hwdata(20) := r.txdes_eop; v.hwdata(21) := r.txdes_eep; v.hwdata(31 downto 22) := (others => '0'); if v_burstack = '1' then if r.txdes_len(1 downto 0) = "00" and (r.txdes_eop = '1' or r.txdes_eep = '1') then -- Store EOP in TX fifo. v_txfifo_write := '1'; v_txfifo_wdata(35) := '1'; v_txfifo_wdata(31 downto 25) := "0000000"; v_txfifo_wdata(24) := r.txdes_eep; end if; -- Frame done. v.txdes_en := '0'; v.int_txdesc := r.txdes_ie; -- Go to st_idle and give preference to RX work. v.mstate := st_idle; end if; when st_txskip => -- Ignore last data cycle of burst because TX fifo is full. if v_burstack = '1' then v.mstate := st_idle; end if; end case; -- Abort DMA when an AHB error occurs. if r.ahberror = '1' then v.rxdma_act := '0'; v.txdma_act := '0'; v.mstate := st_idle; end if; -- -- Burst state machine. -- -- A transfer starts when the main state machine combinatorially pulls -- v_burstreq high and assigns v.haddr and v.hwrite (i.e. r.haddr and -- r.hwrite must be valid in the first clock cycle AFTER rising v_burstreq). -- In case of a write transfer, r.hwdata must be valid in the second -- clock cycle after rising v_burstreq. -- -- During the transfer, the burst state machine announces each word -- with a v_burstack pulse. During a read transfer, ahbi.hrdata is -- valid when v_burstack is high. During a write transfer, a next -- word must be assigned to v.hwdata on the v_burstack pulse. -- -- For a single-word transfer, v_burstreq should be high for only one -- clock cycle. For a multi-word transfer, v_burstreq should be high -- until the last-but-one v_burstack pulse. I.e. after v_burstreq is -- released combinatorially on a v_burstack pulse, one last v_burstack -- pulse will follow. -- -- The burst state machine transparently handles bus arbitration and -- retrying of transfers. In case of a non-retryable error, r.ahberror -- is set high and further transfers are blocked. The main state -- machine is responsible for ensuring that bursts do not cross a -- forbidden address boundary. -- case r.burststat is when bs_idle => -- Wait for request and bus grant. -- (htrans = HTRANS_IDLE) v.hbusreq := r.hbusreq or v_burstreq; if (r.hbusreq = '1' or v_burstreq = '1') and ahbi.hready = '1' and ahbi.hgrant(hindex) = '1' then -- Start burst. v.burststat := bs_setup; end if; -- Block new bursts after an error occurred. if r.ahberror = '1' then v.hbusreq := '0'; v.burststat := bs_idle; end if; when bs_setup => -- First address cycle. -- (htrans = HTRANS_NONSEQ) v.hbusreq := '1'; if ahbi.hready = '1' then -- Increment address and continue burst in bs_active. v.haddr(maxburst+1 downto 2) := std_logic_vector(unsigned(r.haddr(maxburst+1 downto 2)) + 1); v.burststat := bs_active; -- Stop burst when application ends the transfer. v.hbusreq := v_burstreq; if v_burstreq = '0' then v.burststat := bs_end; end if; -- Stop burst when we are kicked off the bus. if ahbi.hgrant(hindex) = '0' then v.burststat := bs_end; end if; end if; when bs_active => -- Continue burst. -- (htrans = HTRANS_SEQ) v.hbusreq := '1'; if ahbi.hresp /= HRESP_OKAY then -- Error response from slave. v.haddr(maxburst+1 downto 2) := std_logic_vector(unsigned(r.haddr(maxburst+1 downto 2)) - 1); if ahbi.hresp = HRESP_ERROR then -- Permanent error. v.ahberror := '1'; v.hbusreq := '0'; else -- Must retry request. v.hbusreq := '1'; end if; v.burststat := bs_idle; elsif ahbi.hready = '1' then -- Increment address. v.haddr(maxburst+1 downto 2) := std_logic_vector(unsigned(r.haddr(maxburst+1 downto 2)) + 1); -- Stop burst when application ends the transfer. v.hbusreq := v_burstreq; if v_burstreq = '0' then v.burststat := bs_end; end if; -- Stop burst when we are kicked off the bus. if ahbi.hgrant(hindex) = '0' then v.burststat := bs_end; end if; end if; when bs_end => -- Last data cycle of burst. -- (htrans = HTRANS_IDLE) v.hbusreq := r.hbusreq or v_burstreq; if ahbi.hresp /= HRESP_OKAY then -- Error response from slave. v.haddr(maxburst+1 downto 2) := std_logic_vector(unsigned(r.haddr(maxburst+1 downto 2)) - 1); if ahbi.hresp = HRESP_ERROR then -- Permanent error. v.ahberror := '1'; v.hbusreq := '0'; else -- Must retry request. v.hbusreq := '1'; end if; v.burststat := bs_idle; elsif ahbi.hready = '1' then -- Burst complete. if (r.hbusreq = '1' or v_burstreq = '1') and ahbi.hgrant(hindex) = '1' then -- Immediately start next burst. v.burststat := bs_setup; else v.burststat := bs_idle; end if; end if; end case; -- -- Drive output signals. -- ahbo.hbusreq <= r.hbusreq; if r.burststat = bs_setup then ahbo.htrans <= HTRANS_NONSEQ; elsif r.burststat = bs_active then ahbo.htrans <= HTRANS_SEQ; else ahbo.htrans <= HTRANS_IDLE; end if; ahbo.haddr <= r.haddr & "00"; ahbo.hwrite <= r.hwrite; ahbo.hwdata <= ahbdrivedata(r.hwdata); ahbo.hlock <= '0'; -- never lock the bus ahbo.hsize <= HSIZE_WORD; -- always 32-bit words ahbo.hburst <= HBURST_INCR; -- undetermined incremental burst ahbo.hprot <= "0011"; -- not cacheable, privileged, data ahbo.hirq <= (others => '0'); -- no interrupts via AHB bus ahbo.hconfig <= hconfig; -- AHB plug&play data ahbo.hindex <= hindex; -- index feedback msto.rxdma_act <= r.rxdma_act; msto.txdma_act <= r.txdma_act; msto.ahberror <= r.ahberror; msto.int_rxdesc <= r.int_rxdesc; msto.int_txdesc <= r.int_txdesc; msto.int_rxpacket <= r.int_rxpacket; msto.rxdesc_next <= r.rxdesc_next; msto.rxdesc_wrap <= r.rxdesc_next and r.rxdes_wr; msto.txdesc_next <= r.txdesc_next; msto.txdesc_wrap <= r.txdesc_next and r.txdes_wr; msto.rxfifo_read <= v_rxfifo_read; msto.txfifo_write <= v_txfifo_write; msto.txfifo_wdata <= v_txfifo_wdata; -- -- Reset. -- if rstn = '0' then v := regs_reset; end if; -- -- Update registers. -- rin <= v; end process; -- -- Synchronous process: update registers. -- process (clk) is begin if rising_edge(clk) then r <= rin; end if; end process; end architecture spwahbmst_arch;