--------------------------------------------------------------------------------
-- Copyright (c) 2015 David Banks
--
--------------------------------------------------------------------------------
--   ____  ____ 
--  /   /\/   / 
-- /___/  \  /    
-- \   \   \/    
--  \   \         
--  /   /         Filename  : BusMonCore.vhd
-- /___/   /\     Timestamp : 30/05/2015
-- \   \  /  \ 
--  \___\/\___\ 
--
--Design Name: AtomBusMon
--Device: XC3S250E

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use work.OhoPack.all ;


entity BusMonCore is
    generic (
        num_comparators   : integer := 8;
        reg_width         : integer := 46;
        fifo_width        : integer := 72;
        avr_data_mem_size : integer := 1024 * 2; -- 2K is the mimimum
        avr_prog_mem_size : integer := 1024 * 8  -- Default is 8K, 6809 amd Z80 need 9K
    );
    port (
        clock_avr        : in    std_logic;

        busmon_clk       : in    std_logic;
        busmon_clken     : in    std_logic;
        cpu_clk          : in    std_logic;
        cpu_clken        : in    std_logic;

        -- CPU Signals
        Addr             : in    std_logic_vector(15 downto 0);
        Data             : in    std_logic_vector(7 downto 0);
        Rd_n             : in    std_logic;
        Wr_n             : in    std_logic;
        RdIO_n           : in    std_logic;
        WrIO_n           : in    std_logic;
        Sync             : in    std_logic;
        Rdy              : out   std_logic;
        nRSTin           : in    std_logic;
        nRSTout          : out   std_logic;

        CountCycle       : in    std_logic;
        
        -- CPU Registers
        -- unused in pure bus monitor mode
        Regs             : in    std_logic_vector(255 downto 0);

        -- CPU Memory Read/Write
        -- unused in pure bus monitor mode
        RdMemOut         : out std_logic;
        WrMemOut         : out std_logic;
        RdIOOut          : out std_logic;
        WrIOOut          : out std_logic;
        AddrOut          : out std_logic_vector(15 downto 0);
        DataOut          : out std_logic_vector(7 downto 0);
        DataIn           : in std_logic_vector(7 downto 0);
        Done             : in std_logic;
        
        -- Single Step interface
        SS_Single        : out std_logic;
        SS_Step          : out std_logic;
        
        -- External trigger inputs
        trig             : in    std_logic_vector(1 downto 0);
                    
        -- HD44780 LCD
        lcd_rs           : out   std_logic;
        lcd_rw           : out   std_logic;
        lcd_e            : out   std_logic;
        lcd_db           : inout std_logic_vector(7 downto 4);

        -- AVR Serial Port
        avr_RxD          : in    std_logic;
        avr_TxD          : out   std_logic;

        -- GODIL Switches
        sw1              : in    std_logic;
        nsw2             : in    std_logic;

        -- GODIL LEDs
        led3             : out   std_logic;
        led6             : out   std_logic;
        led8             : out   std_logic;

        -- OHO_DY1 connected to test connector
        tmosi            : out   std_logic;
        tdin             : out   std_logic;
        tcclk            : out   std_logic
    );
end BusMonCore;

architecture behavioral of BusMonCore is

    signal nrst_avr        : std_logic;
    
    signal lcd_rw_int      : std_logic;
    signal lcd_db_in       : std_logic_vector(7 downto 4);
    signal lcd_db_out      : std_logic_vector(7 downto 4);
    signal dy_counter      : std_logic_vector(31 downto 0);
    signal dy_data         : y2d_type ;

    signal mux             : std_logic_vector(7 downto 0);
    signal muxsel          : std_logic_vector(5 downto 0);
    signal cmd_edge        : std_logic;
    signal cmd_edge1       : std_logic;
    signal cmd_edge2       : std_logic;
    signal cmd             : std_logic_vector(4 downto 0);

    signal addr_sync       : std_logic_vector(15 downto 0);
    signal addr_inst       : std_logic_vector(15 downto 0);
    signal Addr1           : std_logic_vector(15 downto 0);
    signal Data1           : std_logic_vector(7 downto 0);

    signal cycleCount      : std_logic_vector(23 downto 0);
    signal cycleCount_inst : std_logic_vector(23 downto 0);

    signal single          : std_logic;
    signal reset           : std_logic;
    signal step            : std_logic;

    signal bw_status       : std_logic_vector(3 downto 0);
    signal bw_status1      : std_logic_vector(3 downto 0);
    
    signal auto_inc        : std_logic;
    
    signal brkpt_reg       : std_logic_vector(num_comparators * reg_width - 1 downto 0);
    signal brkpt_enable    : std_logic;
    signal brkpt_active    : std_logic;
    signal brkpt_active1   : std_logic;
    signal watch_active    : std_logic;
    
    signal fifo_din        : std_logic_vector(fifo_width - 1 downto 0);
    signal fifo_dout       : std_logic_vector(fifo_width - 1 downto 0);
    signal fifo_empty      : std_logic;
    signal fifo_empty_n    : std_logic;
    signal fifo_rd         : std_logic;
    signal fifo_rd_en      : std_logic;
    signal fifo_wr         : std_logic;
    signal fifo_wr_en      : std_logic;
    signal fifo_rst        : std_logic;

    signal memory_rd       : std_logic;
    signal memory_wr       : std_logic;
    signal io_rd           : std_logic;
    signal io_wr           : std_logic;
    signal addr_dout_reg   : std_logic_vector(23 downto 0);
    signal din_reg         : std_logic_vector(7 downto 0);

    signal Rdy_int         : std_logic;

    signal unused_a3       : std_logic;
    signal unused_b6       : std_logic;
    signal unused_b7       : std_logic;
    signal unused_d6       : std_logic;
    signal unused_d7       : std_logic;
    
begin

    inst_oho_dy1 : entity work.Oho_Dy1 port map (
        dy_clock       => clock_avr,
        dy_rst_n       => '1',
        dy_data        => dy_data,
        dy_update      => '1',
        dy_frame       => open,
        dy_frameend    => open,
        dy_frameend_c  => open,
        dy_pwm         => "1010",
        dy_counter     => dy_counter,
        dy_sclk        => tdin,
        dy_ser         => tcclk,
        dy_rclk        => tmosi
    );
    
    Inst_AVR8: entity work.AVR8
    generic map(
        CDATAMEMSIZE         => avr_data_mem_size,
        CPROGMEMSIZE         => avr_prog_mem_size
    )
    port map(
        clk16M               => clock_avr,
        nrst                 => nrst_avr,

        portain(0)           => '0',
        portain(1)           => '0',
        portain(2)           => '0',
        portain(3)           => '0',
        portain(4)           => lcd_db_in(4),
        portain(5)           => lcd_db_in(5),
        portain(6)           => lcd_db_in(6),
        portain(7)           => lcd_db_in(7),

        portaout(0)          => lcd_rs,
        portaout(1)          => lcd_rw_int,
        portaout(2)          => lcd_e,
        portaout(3)          => unused_a3,
        portaout(4)          => lcd_db_out(4),
        portaout(5)          => lcd_db_out(5),
        portaout(6)          => lcd_db_out(6),
        portaout(7)          => lcd_db_out(7),

        -- Command Port
        portbin(0)           => '0',
        portbin(1)           => '0',
        portbin(2)           => '0',
        portbin(3)           => '0',
        portbin(4)           => '0',
        portbin(5)           => '0',
        portbin(6)           => '0',
        portbin(7)           => '0',
        portbout(0)          => cmd(0),
        portbout(1)          => cmd(1),
        portbout(2)          => cmd(2),
        portbout(3)          => cmd(3),
        portbout(4)          => cmd(4),
        portbout(5)          => cmd_edge,
        portbout(6)          => unused_b6,
        portbout(7)          => unused_b7,
        
        -- Status Port
        portdin(0)           => '0',
        portdin(1)           => '0',
        portdin(2)           => '0',
        portdin(3)           => '0',
        portdin(4)           => '0',
        portdin(5)           => '0',
        portdin(6)           => sw1,
        portdin(7)           => fifo_empty_n,
        
        portdout(0)           => muxsel(0),
        portdout(1)           => muxsel(1),
        portdout(2)           => muxsel(2),
        portdout(3)           => muxsel(3),
        portdout(4)           => muxsel(4),
        portdout(5)           => muxsel(5),
        portdout(6)           => unused_d6,
        portdout(7)           => unused_d7,

        -- Mux Port
        portein              => mux,
        porteout             => open,
                
        spi_mosio            => open,
        spi_scko             => open,
        spi_misoi            => '0',
     
        rxd                  => avr_RxD,
        txd                  => avr_TxD
    );
    fifo_empty_n <= not fifo_empty;

    WatchEvents_inst : entity work.WatchEvents port map(
        clk    => busmon_clk,
        srst   => fifo_rst,
        din    => fifo_din,
        wr_en  => fifo_wr_en,
        rd_en  => fifo_rd_en,
        dout   => fifo_dout,
        full   => open,
        empty  => fifo_empty
    );
    fifo_wr_en <= fifo_wr and busmon_clken;
    fifo_rd_en <= fifo_rd and busmon_clken;
  
    -- The fifo is writen the cycle after the break point
    -- Addr1 is the address bus delayed by 1 cycle
    -- DataWr1 is the data being written delayed by 1 cycle
    -- DataRd is the data being read, that is already one cycle late
    -- bw_state1(1) is 1 for writes, and 0 for reads
    fifo_din <= cycleCount_inst & "0000" & bw_status1 & Data1 & Addr1 & addr_inst;

    lcd_rw    <= lcd_rw_int;
    lcd_db    <= lcd_db_out when lcd_rw_int = '0' else (others => 'Z');
    lcd_db_in <= lcd_db;

    led3 <= not trig(0);       -- red
    led6 <= not trig(1);       -- red
    led8 <= not brkpt_active;  -- green

    nrst_avr <= nsw2;
    
    -- OHO DY1 Display for Testing
    dy_data(0) <= hex & "0000" & Addr(3 downto 0);
    dy_data(1) <= hex & "0000" & Addr(7 downto 4);
    dy_data(2) <= hex & "0000" & "00" & (not nsw2) & sw1;

    mux <= addr_inst(7 downto 0)            when muxsel = 0 else
           addr_inst(15 downto 8)           when muxsel = 1 else
           din_reg                          when muxsel = 2 else
           cycleCount(23 downto 16)         when muxsel = 3 else
           cycleCount(7 downto 0)           when muxsel = 4 else
           cycleCount(15 downto 8)          when muxsel = 5 else

           fifo_dout(7 downto 0)            when muxsel = 6 else
           fifo_dout(15 downto 8)           when muxsel = 7 else
           fifo_dout(23 downto 16)          when muxsel = 8 else
           fifo_dout(31 downto 24)          when muxsel = 9 else          
           fifo_dout(39 downto 32)          when muxsel = 10 else
           fifo_dout(47 downto 40)          when muxsel = 11 else
           fifo_dout(55 downto 48)          when muxsel = 12 else
           fifo_dout(63 downto 56)          when muxsel = 13 else
           fifo_dout(71 downto 64)          when muxsel = 14 else
           
           Regs(8 * to_integer(unsigned(muxsel(4 downto 0))) + 7 downto 8 * to_integer(unsigned(muxsel(4 downto 0))));

    -- Combinatorial set of comparators to decode breakpoint/watch addresses
    brkpt_active_process: process (brkpt_reg, brkpt_enable, Addr, Sync, Rd_n, Wr_n, RdIO_n, WrIO_n, trig)
        variable i            : integer;
        variable reg_addr     : std_logic_vector(15 downto 0);
        variable reg_mask     : std_logic_vector(15 downto 0);
        variable reg_mode_bmr : std_logic;
        variable reg_mode_bmw : std_logic;
        variable reg_mode_bir : std_logic;
        variable reg_mode_biw : std_logic;
        variable reg_mode_bx  : std_logic;
        variable reg_mode_wmr : std_logic;
        variable reg_mode_wmw : std_logic;        
        variable reg_mode_wir : std_logic;
        variable reg_mode_wiw : std_logic;
        variable reg_mode_wx  : std_logic;
        variable reg_mode_all : std_logic_vector(9 downto 0);
        variable bactive      : std_logic;
        variable wactive      : std_logic;
        variable status       : std_logic_vector(3 downto 0);
        variable trigval      : std_logic;
    begin
        bactive := '0';
        wactive := '0';
        status  := (others => '0');
        if (brkpt_enable = '1') then
            for i in 0 to num_comparators - 1 loop
                reg_addr     := brkpt_reg(i * reg_width + 15 downto i * reg_width);
                reg_mask     := brkpt_reg(i * reg_width + 31 downto i * reg_width + 16);
                reg_mode_bmr := brkpt_reg(i * reg_width + 32);
                reg_mode_wmr := brkpt_reg(i * reg_width + 33);
                reg_mode_bmw := brkpt_reg(i * reg_width + 34);
                reg_mode_wmw := brkpt_reg(i * reg_width + 35);
                reg_mode_bir := brkpt_reg(i * reg_width + 36);
                reg_mode_wir := brkpt_reg(i * reg_width + 37);
                reg_mode_biw := brkpt_reg(i * reg_width + 38);
                reg_mode_wiw := brkpt_reg(i * reg_width + 39);
                reg_mode_bx  := brkpt_reg(i * reg_width + 40);
                reg_mode_wx  := brkpt_reg(i * reg_width + 41);
                reg_mode_all := brkpt_reg(i * reg_width + 41 downto i * reg_width + 32);
                trigval      := brkpt_reg(i * reg_width + 42 + to_integer(unsigned(trig)));
                if (trigval = '1' and ((Addr and reg_mask) = reg_addr or (reg_mode_all = "0000000000"))) then
                    if (Sync = '1') then
                        if (reg_mode_bx = '1') then
                            bactive := '1';
                            status  := "1000";
                        elsif (reg_mode_wx = '1') then
                            wactive := '1';
                            status  := "1001";
                        end if;
                    elsif (Rd_n = '0') then
                        if (reg_mode_bmr = '1') then
                            bactive := '1';
                            status  := "0000";
                        elsif (reg_mode_wmr = '1') then
                            wactive := '1';
                            status  := "0001";
                        end if;
                    elsif (Wr_n = '0') then
                        if (reg_mode_bmw = '1') then
                            bactive := '1';
                            status  := "0010";
                        elsif (reg_mode_wmw = '1') then
                            wactive := '1';
                            status  := "0011";
                        end if;
                    elsif (RdIO_n = '0') then
                        if (reg_mode_bir = '1') then
                            bactive := '1';
                            status  := "0100";
                        elsif (reg_mode_wir = '1') then
                            wactive := '1';
                            status  := "0101";
                        end if;
                    elsif (WrIO_n = '0') then
                        if (reg_mode_biw = '1') then
                            bactive := '1';
                            status  := "0110";
                        elsif (reg_mode_wiw = '1') then
                            wactive := '1';
                            status  := "0111";
                        end if;
                    end if;
                end if;
            end loop;
        end if;
        watch_active <= wactive;
        brkpt_active <= bactive;
        bw_status    <= status;
    end process;
   
    -- CPU Control Commands
    -- 0000x Enable/Disable single strpping
    -- 0001x Enable/Disable breakpoints / watches
    -- 0010x Load breakpoint / watch register
    -- 0011x Reset CPU
    -- 01000 Singe Step CPU
    -- 01001 Read FIFO
    -- 01010 Reset FIFO
    -- 01011 Unused
    -- 0110x Load address/data register
    -- 0111x Unused
    -- 10000 Read Memory
    -- 10001 Read Memory and Auto Inc Address
    -- 10010 Write Memory
    -- 10011 Write Memory and Auto Inc Address
    -- 10000 Read Memory
    -- 10001 Read Memory and Auto Inc Address
    -- 10010 Write Memory
    -- 10011 Write Memory and Auto Inc Address
    -- 1x1xx Unused
    -- 11xxx Unused

    cpuProcess: process (busmon_clk)
    begin
        if rising_edge(busmon_clk) then
            if busmon_clken = '1' then
                -- Cycle counter, wraps every 16s at 1MHz
                if (nRSTin = '0') then
                    cycleCount <= (others => '0');
                elsif (CountCycle = '1') then
                    cycleCount <= cycleCount + 1;
                end if;
            
                -- Command processing
                cmd_edge1 <= cmd_edge;
                cmd_edge2 <= cmd_edge1;
                fifo_rd   <= '0';
                fifo_wr   <= '0';
                fifo_rst  <= '0';
                memory_rd <= '0';
                memory_wr <= '0';
                io_rd     <= '0';
                io_wr     <= '0';
                SS_Step   <= '0';
                if (cmd_edge2 = '0' and cmd_edge1 = '1') then
                    if (cmd(4 downto 1) = "0000") then
                        single <= cmd(0);
                    end if;
                    
                    if (cmd(4 downto 1) = "0001") then
                        brkpt_enable <= cmd(0);
                    end if;
                    
                    if (cmd(4 downto 1) = "0010") then
                        brkpt_reg <= cmd(0) & brkpt_reg(brkpt_reg'length - 1 downto 1);
                    end if;

                    if (cmd(4 downto 1) = "0110") then
                        addr_dout_reg <= cmd(0) & addr_dout_reg(addr_dout_reg'length - 1 downto 1);
                    end if;
                    
                    if (cmd(4 downto 1) = "0011") then
                        reset <= cmd(0);
                    end if;

                    if (cmd(4 downto 0) = "01001") then
                        fifo_rd <= '1';
                    end if;                

                    if (cmd(4 downto 0) = "01010") then
                        fifo_rst <= '1';
                    end if;

                    if (cmd(4 downto 1) = "1000") then
                        memory_rd <= '1';
                        auto_inc  <= cmd(0); 
                    end if;

                    if (cmd(4 downto 1) = "1001") then
                        memory_wr <= '1';
                        auto_inc  <= cmd(0); 
                    end if;

                    if (cmd(4 downto 1) = "1010") then
                        io_rd <= '1';
                        auto_inc  <= cmd(0); 
                    end if;

                    if (cmd(4 downto 1) = "1011") then
                        io_wr <= '1';
                        auto_inc  <= cmd(0); 
                    end if;
                    
                end if;
                
                -- Auto increment the memory address reg the cycle after a rd/wr
                if (auto_inc = '1' and Done = '1') then
                    addr_dout_reg(23 downto 8) <= addr_dout_reg(23 downto 8) + 1;
                end if;

                -- Single Stepping
                if (brkpt_active = '1') then
                    single <= '1';
                end if;
                
                if ((single = '0') or (cmd_edge2 = '0' and cmd_edge1 = '1' and cmd = "01000")) then
                    Rdy_int <= (not brkpt_active);
                    SS_Step <= (not brkpt_active);            
                else
                    Rdy_int <= (not Sync);
                end if;
                
                nRSTout <= not reset;
                
                -- Latch instruction address for the whole cycle
                if (Sync = '1') then
                    addr_inst <= Addr;
                    cycleCount_inst <= cycleCount;
                end if;
                
                -- Breakpoints and Watches written to the FIFO
                brkpt_active1 <= brkpt_active;
                bw_status1    <= bw_status;
                if watch_active = '1' or (brkpt_active = '1' and brkpt_active1 = '0') then
                    fifo_wr <= '1';
                    Addr1 <= Addr;
                end if;
            end if;
        end if;
    end process;

    dataProcess: process (cpu_clk)
    begin
        if rising_edge(cpu_clk) then
            if cpu_clken = '1' then
                -- Latch the data bus for use in watches
                Data1 <= Data;
                -- Latch memory read in response to a read command
                if (Done = '1') then
                    din_reg <= DataIn;
                end if;
            end if;
        end if;
    end process;
    
    Rdy <= Rdy_int;
    RdMemOut <= memory_rd;
    WrMemOut <= memory_wr;
    RdIOOut <= io_rd;
    WrIOOut <= io_wr;
    AddrOut <= addr_dout_reg(23 downto 8);
    DataOut <= addr_dout_reg(7 downto 0);
    SS_Single <= single;

end behavioral;


