----------------------------------------------------------------- -- uCore 1.90 - debugger.vhd -- ----------------------------------------------------------------- -- -- Author: KLAUS SCHLEISIEK -- Last change: KS 07.12.2015 17:47:02 -- -- Do not use this file except in compliance with the License. You may -- obtain a copy of the License at http://www.microcore.org/License/ -- Software distributed under the License is distributed on an "AS IS" basis, -- WITHOUT WARRANTY OF ANY KIND, either express or implied. -- See the License for the specific language governing rights and limitations -- under the License. -- -- The Initial Developer of the Original Code is Klaus.Schleisiek AT microcore.org. -- -- Umbilical, byte oriented debug interface with full duplex handshake. -- It consists of a program memory loader and the DEBUG register, -- which can be written and read by the host asynchronously and read -- and written by the core thru DEBUG_REG. LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_signed.ALL; USE work.functions.ALL; USE work.constants.ALL; ENTITY debugger IS PORT (uBus : IN uBus_port; debugrom : OUT progmem_port; debug_res : OUT STD_LOGIC; debug_exc : OUT STD_LOGIC; prog_load : OUT STD_LOGIC; debug_flag : OUT STD_LOGIC; debug_data : OUT data_bus; -- umbilical uart_in : IN from_uart; uart_out : OUT to_uart ); END debugger; ARCHITECTURE rtl OF debugger IS ALIAS reset : STD_LOGIC IS uBus.reset; ALIAS clk : STD_LOGIC IS uBus.clk; ALIAS clk_en : STD_LOGIC IS uBus.clk_en; ALIAS rx_full : STD_LOGIC IS uart_in.full; -- data buffer full ALIAS rx_read : STD_LOGIC IS uart_out.read; -- read and clear data buffer ALIAS rx_data : byte IS uart_in.data; -- input data buffer ALIAS tx_empty : STD_LOGIC IS uart_in.empty; -- data buffer empty ALIAS tx_write : STD_LOGIC IS uart_out.write; -- write into data buffer ALIAS tx_data : byte IS uart_out.data; -- output data SIGNAL tx_wr : STD_LOGIC; SIGNAL rx_reg : STD_LOGIC_VECTOR(data_nibbles*8-1 DOWNTO 0); SIGNAL rx_ctr : STD_LOGIC_VECTOR(log2(data_nibbles)-1 DOWNTO 0); -- #nibbles to receive from host SIGNAL in_rd : STD_LOGIC; -- target reads rx_reg SIGNAL tx_acked : STD_LOGIC; -- ack has been received from host SIGNAL tx_reg : STD_LOGIC_VECTOR(data_nibbles*8-1 DOWNTO 0); SIGNAL tx_ctr : STD_LOGIC_VECTOR(log2(data_nibbles)-1 DOWNTO 0); -- #nibbles to send to host SIGNAL out_wr : STD_LOGIC; -- target writes tx_reg SIGNAL rx_acked : STD_LOGIC; -- ack has been send to host SIGNAL send_ack : STD_LOGIC; SIGNAL addr_ptr : program_addr; ALIAS addr_ctr : program_addr IS rx_reg(prog_addr_width-1 DOWNTO 0); SIGNAL write : STD_LOGIC; -- write signal for debugROM SIGNAL write_d : STD_LOGIC; -- one cycle delay for safe loading TYPE in_states IS (idle, getaddr, saveaddr, getlength, loading, receive, full, wait_ack); SIGNAL in_state : in_states; TYPE out_states IS (idle, start, mark, sending, wait_ack, acking); SIGNAL out_state : out_states; SIGNAL f_txempty : STD_LOGIC; SIGNAL f_rxfull : STD_LOGIC; BEGIN ----------------------------------------------------------------------- -- loading the program memory -- and listening to the host ----------------------------------------------------------------------- in_rd <= '1' WHEN uReg_read(uBus, DEBUG_REG) ELSE '0'; debug_data <= rx_reg(data_width-1 DOWNTO 0); tx_write <= tx_wr; -- OUT transfer data into transmitter rx_read <= rx_full; loadROM: PROCESS(clk, reset) PROCEDURE getdata (new_state : IN in_states) IS BEGIN IF rx_full = '1' THEN rx_reg <= rx_reg(data_nibbles*8-9 DOWNTO 0) & rx_data; rx_ctr <= rx_ctr-1; IF rx_ctr = 0 THEN in_state <= new_state; rx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); END IF; END IF; END getdata; BEGIN IF reset = '1' AND async_reset THEN rx_reg <= (OTHERS => '0'); rx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); addr_ptr <= (OTHERS => '0'); in_state <= idle; debug_res <= '0'; write <= '0'; write_d <= '0'; tx_acked <= '0'; prog_load <= '0'; debug_flag <= '0'; ELSIF rising_edge(clk) THEN IF reset = '1' AND NOT async_reset THEN rx_reg <= (OTHERS => '0'); rx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); addr_ptr <= (OTHERS => '0'); in_state <= idle; debug_res <= '0'; write <= '0'; write_d <= '0'; tx_acked <= '0'; prog_load <= '0'; debug_flag <= '0'; ELSIF clk_en = '1' THEN write <= '0'; write_d <= write; debug_res <= '0'; tx_acked <= '0'; IF rx_full = '1' AND out_state = wait_ack AND rx_data = mark_ack THEN tx_acked <= '1'; END IF; CASE in_state IS WHEN idle => rx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); IF rx_full = '1' THEN CASE rx_data IS WHEN mark_start => in_state <= getaddr; prog_load <= '1'; WHEN mark_reset => debug_res <= '1'; in_state <= getaddr; prog_load <= '1'; WHEN mark_debug => in_state <= receive; WHEN OTHERS => NULL; END CASE; END IF; WHEN getaddr => getdata(saveaddr); WHEN saveaddr => addr_ptr <= addr_ctr; in_state <= getlength; WHEN getlength => getdata(loading); WHEN loading => IF addr_ctr = 0 THEN in_state <= wait_ack; ELSE IF rx_full = '1' THEN write <= '1'; END IF; IF write_d = '1' THEN addr_ptr <= addr_ptr+1; addr_ctr <= addr_ctr-1; END IF; END IF; WHEN receive => getdata(full); WHEN full => debug_flag <= '1'; IF in_rd = '1' THEN debug_flag <= '0'; in_state <= wait_ack; END IF; WHEN wait_ack => IF rx_acked = '1' THEN in_state <= idle; prog_load <= '0'; END IF; END CASE; END IF; END IF; END PROCESS loadROM; debugrom.write <= write; debugrom.dout <= rx_data; debugrom.addr <= addr_ptr; ----------------------------------------------------------------------- -- talking to the host ----------------------------------------------------------------------- tx_data <= mark_ack WHEN rx_acked = '1' ELSE mark_debug WHEN out_state = mark ELSE mark_nack WHEN out_state = start ELSE tx_reg(data_nibbles*8-1 DOWNTO (data_nibbles-1)*8); out_wr <= '1' WHEN uReg_write(uBus, DEBUG_REG) ELSE '0'; send_ack <= '1' WHEN in_state = wait_ack AND rx_acked = '0' ELSE '0'; debug_out: PROCESS (clk, reset) BEGIN IF reset = '1' AND async_reset THEN tx_wr <= '0'; tx_reg <= (OTHERS => '0'); tx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); out_state <= start; rx_acked <= '0'; ELSIF rising_edge(clk) THEN IF reset = '1' AND NOT async_reset THEN tx_wr <= '0'; tx_reg <= (OTHERS => '0'); tx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); out_state <= start; rx_acked <= '0'; ELSIF clk_en = '1' THEN tx_wr <= '0'; rx_acked <= '0'; CASE out_state IS WHEN start => IF tx_empty = '1' AND tx_wr = '0' THEN tx_wr <= '1'; END IF; IF tx_wr = '1' THEN tx_ctr <= tx_ctr-1; IF tx_ctr = 0 THEN out_state <= idle; END IF; END IF; WHEN idle => IF send_ack = '1' THEN out_state <= acking; ELSIF out_wr = '1' THEN -- send a new word tx_reg <= (OTHERS => '0'); tx_reg(data_width-1 DOWNTO 0) <= uBus.dout; IF uBus.dout(signbit) = '1' THEN tx_reg(tx_reg'high DOWNTO data_width-1) <= (OTHERS => '1'); -- handling data_widths which are not a multiple of bytes END IF; tx_ctr <= to_vec(data_nibbles-1, log2(data_nibbles)); out_state <= mark; END IF; WHEN mark => IF tx_empty = '1' AND tx_wr = '0' THEN tx_wr <= '1'; END IF; IF tx_wr = '1' THEN out_state <= sending; END IF; WHEN sending => IF tx_empty = '1' AND tx_wr = '0' THEN tx_wr <= '1'; END IF; IF tx_wr = '1' THEN tx_reg <= tx_reg(((data_nibbles-1)*8)-1 DOWNTO 0) & tx_reg(7 DOWNTO 0); tx_ctr <= tx_ctr-1; IF tx_ctr = 0 THEN out_state <= wait_ack; END IF; END IF; WHEN acking => IF tx_empty = '1' THEN tx_wr <= '1'; rx_acked <= '1'; out_state <= idle; END IF; WHEN wait_ack => IF tx_acked = '1' THEN IF in_state = wait_ack THEN out_state <= acking; ELSE out_state <= idle; END IF; END IF; END CASE; END IF; END IF; END PROCESS debug_out; f_txempty <= '0' WHEN out_state /= idle -- transmitter busy OR send_ack = '1' -- acknowledge has priority OR in_state = full -- handshaking protocol: Receiver MUST be empty ELSE '1'; f_rxfull <= '1' WHEN in_state = full ELSE '0'; debug_exc <= '1' WHEN (out_wr = '1' AND f_txempty = '0') OR (in_rd = '1' AND f_rxfull = '0') ELSE '0'; END rtl;