-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.rv_pkg.all;


entity vsperips is
  generic ( CLK_FREQ    : natural := 50000000;            -- default 50M
            UART_SPEED  : natural := 1000000;             -- default 1M
            AXIDATA_W   : natural := 32;
            NR_GPIOS    : integer range 1 to 32 := 9 );
  port ( clk_i          : in  std_logic;
         xrst_i         : in  std_logic;                  -- syncr reset
         --
         --  vsbus, all inputs are retimed outside this block ! !
         pp_rd_i        : in  std_logic;
         pp_wr_i        : in  slv_03_00;
         pp_addr_i      : in  slv_31_00;
         pp_wr_data_i   : in  slv_31_00;
         pp_rd_data_o   : out slv_31_00;
         pp_irq_o       : out std_logic;                  -- perip interrupt
         --
         pll_cfg_i      : in slv_08_00;
         --
         -- perip ios to pads
         rx_i           : in  std_logic;                  -- ulite
         tx_o           : out std_logic;
         rx2_i          : in  std_logic;                  -- uart2
         tx2_o          : out std_logic;
         --
         spi_miso_i     : in  std_logic;
         spi_clk_o      : out std_logic;
         spi_cs_o       : out std_logic;
         spi_mosi_o     : out std_logic;
         --
         gpio_i         : in  slv_t(NR_GPIOS-1 downto 0);
         gpio_o         : out slv_t(NR_GPIOS-1 downto 0);
         gpio_oena_o    : out slv_t(NR_GPIOS-1 downto 0);
         gpio_pdena_o   : out slv_t(NR_GPIOS-1 downto 0);
         gpio_puena_o   : out slv_t(NR_GPIOS-1 downto 0);
         --  DMA bus
         dma_rd_o       : out std_logic;
         dma_wr_o       : out slv_03_00;
         dma_addr_o     : out slv_31_02;
         dma_wr_data_o  : out slv_31_00;
         dma_rd_data_i  : in  slv_31_00;
         dma_intr_i     : in  std_logic                   -- dma interrupt
         );
end entity vsperips;


architecture rtl of vsperips is

  constant PP_OP_c     : slv_31_28 := X"9";

  constant IRQ_OP_c    : slv_27_24 := X"0";               -- 0x90..
  constant TMR_OP_c    : slv_27_24 := X"1";               -- 0x91..
  constant UART_OP_c   : slv_27_24 := X"2";               -- 0x92...
  constant SPI_OP_c    : slv_27_24 := X"3";               -- 0x93...
  constant GP0_OP_c    : slv_27_24 := X"4";               -- 0x94...
  constant UART2_OP_c  : slv_27_24 := X"5";               -- 0x95...
  constant DMA_OP_c    : slv_27_24 := X"9";               -- 0x99
  constant RV_IF_OP_c  : slv_27_24 := X"B";               -- 0x9B...


  component vsirq_ctrl
    generic ( NR_IRQS : natural range 1 to 32 := 6 );
    port ( clk_i         : in  std_logic;
           xrst_i        : in  std_logic;
           --  vsbus
           pp_rd_i       : in  std_logic;
           pp_wr_i       : in  slv_03_00;                 --! 3:1 NC
           pp_addr_i     : in  slv_31_00;
           pp_data_i     : in  slv_31_00;
           pp_data_o     : out slv_31_00;
           --  interrupt requests from peripherals
           interrupt_i   : in  slv_t(NR_IRQS-1 downto 0);
           --  outputs
           intr_o        : out std_logic );               -- to cpu
  end component;


  component vstimer
    port ( clk_i       : in  std_logic;
           xrst_i      : in  std_logic;
           --  vsbus
           pp_rd_i     : in  std_logic;
           pp_wr_i     : in  slv_03_00;                   --! 3:1 NC
           pp_addr_i   : in  slv_31_00;
           pp_data_i   : in  slv_31_00;
           pp_data_o   : out slv_31_00;
           --
           mtime_o     : out slv_63_00;
           --  interrupt request
           intr_o      : out std_logic );
  end component;


  component vsuart_lite
    generic ( CLK_FREQ    : natural := 50000000;          -- default 50M
              UART_SPEED  : natural := 1000000 );         -- default 1M
    port ( clk_i     : in  std_logic;
           xrst_i    : in  std_logic;
           --  vsbus
           pp_rd_i   : in  std_logic;
           pp_wr_i   : in  slv_03_00;                     --! 3:1 NC
           pp_addr_i : in  slv_31_00;
           pp_data_i : in  slv_31_00;
           pp_data_o : out slv_31_00;
           --  interrupt request
           intr_o    : out std_logic;
           --  to pads
           rx_i      : in  std_logic;
           tx_o      : out std_logic );
  end component;


  component vsspi_lite
    port ( clk_i        : in  std_logic;
           xrst_i       : in  std_logic;
           --  vsbus
           pp_rd_i      : in  std_logic;
           pp_wr_i      : in  slv_03_00;                  --! NC [ 3:1]
           pp_addr_i    : in  slv_07_00;                  --! NC [31:8]
           pp_data_i    : in  slv_31_00;                  --! NC [30:10]
           pp_data_o    : out slv_31_00;
           --  interrupt request
           intr_o       : out std_logic;
           --  to pads
           spi_miso_i   : in  std_logic;
           spi_clk_o    : out std_logic;
           spi_cs_o     : out std_logic;
           spi_mosi_o   : out std_logic );
  end component;


  component vsgpio
    generic ( NR_GPIOS   : integer range 1 to 32 := 12 );
    port ( clk_i         : in  std_logic;
           xrst_i        : in  std_logic;
           --  vsbus
           pp_rd_i       : in  std_logic;
           pp_wr_i       : in  slv_03_00;                 --! NC [3:1]
           pp_addr_i     : in  slv_31_00;
           pp_data_i     : in  slv_31_00;
           pp_data_o     : out slv_31_00;
           --  interrupt request
           intr_o        : out std_logic;
           --  gpios to pads
           gpio_i        : in  slv_t(NR_GPIOS-1 downto 0);
           gpio_o        : out slv_t(NR_GPIOS-1 downto 0);
           gpio_oena_o   : out slv_t(NR_GPIOS-1 downto 0);
           gpio_pdena_o  : out slv_t(NR_GPIOS-1 downto 0);
           gpio_puena_o  : out slv_t(NR_GPIOS-1 downto 0) );
  end component;


  component uart16550
    port ( clk_i      : in  std_logic;
           xrst_i     : in  std_logic;
           pp_rd_i    : in  std_logic;
           pp_wr_i    : in  std_logic;
           pp_addr_i  : in  slv_t(2 downto 0);
           pp_data_i  : in  slv_t(7 downto 0);
           pp_data_o  : out slv_t(7 downto 0);
           --
           stx_pad_o  : out std_logic;    -- received from transmitter module
           srx_pad_i  : in  std_logic;
           --  interrupt request
           int_o      : out std_logic;
           -- additional signals
           -- modem status bits [3:0], ! ACTIVE LOW !
           cts_pad_i  : in  std_logic;    -- clear to send,  act low, '0' IF NC!
           dsr_pad_i  : in  std_logic;    -- data set ready        - " " -
           ri_pad_i   : in  std_logic;    -- ring indicator        - " " -
           dcd_pad_i  : in  std_logic;    -- data carrier detect   - " " -
           -- modem control outputs
           rts_pad_o  : out std_logic;    -- request to send
           dtr_pad_o  : out std_logic;    -- data terminal ready
           baud_o     : out std_logic );  -- baud rate (16X real)
  end component;


  component rv_if
    port ( clk_i       : in  std_logic;
           xrst_i      : in  std_logic;
           --  vsbus
           pp_rd_i     : in  std_logic;
           pp_wr_i     : in  slv_03_00;   -- ! 3:1 NC
           pp_addr_i   : in  slv_07_00;
           pp_data_i   : in  slv_31_00;
           pp_data_o   : out slv_31_00;
           --
           pll_cfg_i   : in  slv_08_00;   -- pll_ctrl[6:0], xtal/2, xtal/256
           uart_cfg_o  : out slv_03_00 );
  end component;


  -- inputs retimed at higher level
  type vsbus_t is
  record
    pp_access   : std_logic;
    pp_addr     : slv_31_00;
    pp_wr       : slv_03_00;
    pp_wr_data  : slv_31_00;
    pp_rd       : std_logic;
    pp_rd_data  : slv_31_00;
  end record;


  signal irq_io          : vsbus_t;
  signal timer_io        : vsbus_t;
  signal uart_io         : vsbus_t;
  signal spi_io          : vsbus_t;
  signal gp0_io          : vsbus_t;
  signal uart2_io        : vsbus_t;
  signal rv_if_io        : vsbus_t;
  signal dma_io          : vsbus_t;

  signal timer_intr_w    : std_logic;
  signal uart_intr_w     : std_logic;
  signal spi_intr_w      : std_logic;
  signal gp0_intr_w      : std_logic;
  signal uart2_intr_w    : std_logic;
  signal dma_intr_w      : std_logic;

  signal pp_access_w     : std_logic;

  signal uart_cfg_w      : slv_03_00;

begin

  pp_access_w          <= eq(pp_addr_i(PP_OP_c'range), PP_OP_c);

  --
  -- IRQ_CTRL vsbus
  --
  irq_io.pp_access     <= pp_access_w and
                          eq(pp_addr_i(IRQ_OP_c'range), IRQ_OP_c);

  irq_io.pp_rd         <= pp_rd_i and irq_io.pp_access;
  irq_io.pp_addr       <= pp_addr_i;
  irq_io.pp_wr         <= pp_wr_i and (irq_io.pp_access  &     -- ! 3:1 NC
                                       irq_io.pp_access  &
                                       irq_io.pp_access  &
                                       irq_io.pp_access);
  irq_io.pp_wr_data    <= pp_wr_data_i;



  --
  --  TIMER vsbus
  --
  timer_io.pp_access   <= pp_access_w and
                          eq(pp_addr_i(TMR_OP_c'range), TMR_OP_c);

  timer_io.pp_rd       <= pp_rd_i and timer_io.pp_access;
  timer_io.pp_addr     <= pp_addr_i;
  timer_io.pp_wr       <= pp_wr_i and (timer_io.pp_access  &   -- ! 3:1 NC
                                       timer_io.pp_access  &
                                       timer_io.pp_access  &
                                       timer_io.pp_access);
  timer_io.pp_wr_data  <= pp_wr_data_i;

  --
  --  UART vsbus
  --
  uart_io.pp_access    <= pp_access_w and
                          eq(pp_addr_i(UART_OP_c'range), UART_OP_c);

  uart_io.pp_rd        <= pp_rd_i and uart_io.pp_access;
  uart_io.pp_addr      <= pp_addr_i;
  uart_io.pp_wr        <= pp_wr_i and (uart_io.pp_access  &    -- ! 3:1 NC
                                       uart_io.pp_access  &
                                       uart_io.pp_access  &
                                       uart_io.pp_access);
  uart_io.pp_wr_data   <= pp_wr_data_i;



  --
  --  SPI vsbus
  --
  spi_io.pp_access     <= pp_access_w and
                          eq(pp_addr_i(SPI_OP_c'range), SPI_OP_c);

  spi_io.pp_rd         <= pp_rd_i and spi_io.pp_access;
  spi_io.pp_addr       <= pp_addr_i;
  spi_io.pp_wr         <= pp_wr_i and (spi_io.pp_access  &     -- ! 3:1 NC
                                       spi_io.pp_access  &
                                       spi_io.pp_access  &
                                       spi_io.pp_access);
  spi_io.pp_wr_data    <= pp_wr_data_i;



  --
  --  GPIO vsbus
  --
  gp0_io.pp_access     <= pp_access_w and
                          eq(pp_addr_i(GP0_OP_c'range), GP0_OP_c);

  gp0_io.pp_rd         <= pp_rd_i and gp0_io.pp_access;
  gp0_io.pp_addr       <= pp_addr_i;
  gp0_io.pp_wr         <= pp_wr_i and (gp0_io.pp_access  &     -- ! 3:1 NC
                                       gp0_io.pp_access  &
                                       gp0_io.pp_access  &
                                       gp0_io.pp_access);
  gp0_io.pp_wr_data    <= pp_wr_data_i;



  --
  --  UART2 vsbus
  --
  uart2_io.pp_access   <= pp_access_w and
                          eq(pp_addr_i(UART2_OP_c'range), UART2_OP_c);

  uart2_io.pp_rd       <= pp_rd_i and uart2_io.pp_access;
  uart2_io.pp_addr     <= pp_addr_i;
  uart2_io.pp_wr       <= pp_wr_i and (uart2_io.pp_access  &   -- ! 3:1 NC
                                       uart2_io.pp_access  &
                                       uart2_io.pp_access  &
                                       uart2_io.pp_access);
  uart2_io.pp_wr_data  <= pp_wr_data_i;

 

  --
  -- dma
  --
  dma_io.pp_access   <= pp_access_w and
                        eq(pp_addr_i(DMA_OP_c'range), DMA_OP_c);
  dma_io.pp_rd       <= pp_rd_i and dma_io.pp_access;
  dma_io.pp_addr     <= pp_addr_i;
  dma_io.pp_wr       <= pp_wr_i and (dma_io.pp_access  &       -- ! 3:1 NC
                                     dma_io.pp_access  &
                                     dma_io.pp_access  &
                                     dma_io.pp_access);
  dma_io.pp_wr_data  <= pp_wr_data_i;


  --
  --  RISCV register interface
  --
  rv_if_io.pp_access   <= pp_access_w and
                          eq(pp_addr_i(RV_IF_OP_c'range), RV_IF_OP_c);
  rv_if_io.pp_rd       <= pp_rd_i and rv_if_io.pp_access;
  rv_if_io.pp_addr     <= pp_addr_i;
  rv_if_io.pp_wr       <= pp_wr_i and (rv_if_io.pp_access  &   -- ! 3:1 NC
                                       rv_if_io.pp_access  &
                                       rv_if_io.pp_access  &
                                       rv_if_io.pp_access);
  rv_if_io.pp_wr_data  <= pp_wr_data_i;


  pp_rd_data_o         <= irq_io.pp_rd_data   or -- when (irq_io.pp_rd   = '1')
                          timer_io.pp_rd_data or -- when (timer_io.pp_rd = '1')
                          uart_io.pp_rd_data  or -- when (uart_io.pp_rd  = '1')
                          spi_io.pp_rd_data   or -- when (spi_io.pp_rd   = '1')
                          uart2_io.pp_rd_data or -- when (uart2_io.pp_rd = '1')
                          dma_io.pp_rd_data   or
                          rv_if_io.pp_rd_data    -- when (rv_if_io.pp_rd = '1')
                          ;


  dma_rd_o          <= dma_io.pp_rd;
  dma_wr_o          <= dma_io.pp_wr;
  dma_addr_o        <= dma_io.pp_addr(31 downto 2);
  dma_wr_data_o     <= dma_io.pp_wr_data(31 downto 0);
  --input to vsperip and then output as pp_rd_data_o
  dma_io.pp_rd_data <= dma_rd_data_i;
  dma_intr_w        <= dma_intr_i;    --input and then to vsirq




  u_vsirq_ctrl : vsirq_ctrl
    generic map ( NR_IRQS  => 6 )
    port map ( clk_i            => clk_i,
               xrst_i           => xrst_i,
               --  vsbus
               pp_rd_i          => irq_io.pp_rd,
               pp_wr_i          => irq_io.pp_wr,               -- ! 3:1 NC
               pp_addr_i        => irq_io.pp_addr,
               pp_data_i        => irq_io.pp_wr_data,
               pp_data_o        => irq_io.pp_rd_data,
               --  interrupt requests from peripherals
               interrupt_i(0)   => timer_intr_w,
               interrupt_i(1)   => uart_intr_w,
               interrupt_i(2)   => spi_intr_w,
               interrupt_i(3)   => gp0_intr_w,
               interrupt_i(4)   => uart2_intr_w,
               interrupt_i(5)   => dma_intr_w,
               --  outputs
               intr_o           => pp_irq_o );                 -- to cpu


  u_vstimer : vstimer
    port map ( clk_i      => clk_i,
               xrst_i     => xrst_i,
               --  vsbus
               pp_rd_i    => timer_io.pp_rd,
               pp_wr_i    => timer_io.pp_wr,                   -- ! 3:1 NC
               pp_addr_i  => timer_io.pp_addr,
               pp_data_i  => timer_io.pp_wr_data,
               pp_data_o  => timer_io.pp_rd_data,
               --  interrupt request
               intr_o     => timer_intr_w );


  u_vsuart_lite : vsuart_lite
    generic map ( CLK_FREQ    => CLK_FREQ,                     -- default 50M
                  UART_SPEED  => UART_SPEED )                  -- default 1M
    port map ( clk_i      => clk_i,
               xrst_i     => xrst_i,
               --  vsbus
               pp_rd_i    => uart_io.pp_rd,
               pp_wr_i    => uart_io.pp_wr,                    -- ! 3:1 NC
               pp_addr_i  => uart_io.pp_addr,
               pp_data_i  => uart_io.pp_wr_data,
               pp_data_o  => uart_io.pp_rd_data,
               --  interrupt request
               intr_o     => uart_intr_w,
               --  to pads
               rx_i       => rx_i,
               tx_o       => tx_o );



  u_vsspi_lite : vsspi_lite
    port map ( clk_i       => clk_i,
               xrst_i      => xrst_i,
               --  vsbus
               pp_rd_i     => spi_io.pp_rd,
               pp_wr_i     => spi_io.pp_wr,                    -- ! NC [ 3:1]
               pp_addr_i   => spi_io.pp_addr(7 downto 0),      -- ! NC [31:8]
               pp_data_i   => spi_io.pp_wr_data,               -- ! NC [30:10]
               pp_data_o   => spi_io.pp_rd_data,
               --  interrupt request
               intr_o      => spi_intr_w,
               --  to pads
               spi_miso_i  => spi_miso_i,
               spi_clk_o   => spi_clk_o,
               spi_cs_o    => spi_cs_o,
               spi_mosi_o  => spi_mosi_o );



  u_vsgpio : vsgpio
    generic map ( NR_GPIOS  => NR_GPIOS )
    port map ( clk_i         => clk_i,
               xrst_i        => xrst_i,
               --  vsbus
               pp_rd_i       => gp0_io.pp_rd,
               pp_wr_i       => gp0_io.pp_wr,                  -- ! 3:1 NC
               pp_addr_i     => gp0_io.pp_addr,
               pp_data_i     => gp0_io.pp_wr_data,
               pp_data_o     => gp0_io.pp_rd_data,
               --  interrupt request
               intr_o        => gp0_intr_w,
               --  gpios to pads
               gpio_i        => gpio_i,
               gpio_o        => gpio_o,
               gpio_oena_o   => gpio_oena_o,
               gpio_pdena_o  => gpio_pdena_o,
               gpio_puena_o  => gpio_puena_o );



  u_uart16550 : uart16550 -- to vsdsp
    port map ( clk_i         => clk_i,
               xrst_i        => xrst_i,
               pp_rd_i       => uart2_io.pp_rd,
               pp_wr_i       => uart2_io.pp_wr(0),             -- ! 3:1 NC
               pp_addr_i     => uart2_io.pp_addr(4 downto 2),  -- 3 bits, <<2 !
               pp_data_i     => uart2_io.pp_wr_data(7 downto 0),
               pp_data_o     => uart2_io.pp_rd_data(7 downto 0),
               --
               stx_pad_o     => tx2_o,                         -- external
               srx_pad_i     => rx2_i,
               --
               int_o         => uart2_intr_w,
               --  additional signals
               --  modem status bits [3:0]
               cts_pad_i     => '0',     -- clear to send, act low, '0' IF NC
               dsr_pad_i     => '0',     -- data set ready       - " " -
               ri_pad_i      => '0',     -- ring indicator       - " " -
               dcd_pad_i     => '0',     -- data carrier detect  - " " -
               --  modem control outputs
               rts_pad_o     => OPEN,    -- request to send
               dtr_pad_o     => OPEN,    -- data terminal ready
               baud_o        => OPEN );  -- baud rte (16X real)

  uart2_io.pp_rd_data(31 downto 8) <= (others => '0');         -- NC



  u2_rv_if : rv_if
    port map ( clk_i       => clk_i,
               xrst_i      => xrst_i,
               --  vsbus
               pp_rd_i     => rv_if_io.pp_rd,
               pp_wr_i     => rv_if_io.pp_wr,                  -- ! 3:1 NC
               pp_addr_i   => rv_if_io.pp_addr(7 downto 0),
               pp_data_i   => rv_if_io.pp_wr_data,
               pp_data_o   => rv_if_io.pp_rd_data,
               --
               pll_cfg_i   => pll_cfg_i,
               uart_cfg_o  => uart_cfg_w );


end rtl;
