// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved

package SoC_Map;

// ================================================================
// This module defines the overall 'address map' of the SoC, showing
// the addresses serviced by each slave IP, and which addresses are
// memory vs. I/O.

// ***** WARNING! WARNING! WARNING! *****

// During system integration, this address map should be identical to
// the system interconnect settings (e.g., routing of requests between
// masters and slaves).  This map is also needed by software so that
// it knows how to address various IPs.

// This module contains no state; it just has constants, and so can be
// freely instantiated at multiple places in the SoC module hierarchy
// at no hardware cost.  It allows this map to be defined in one
// place and shared across the SoC.

// ================================================================
// This version of SoC_Map is for the DARPA SSITH GFE

// Our "Near_Mem_IO" corresponds to "CLINT" in Rocket

// ================================================================
// Exports

export  SoC_Map_Struct (..), soc_map_struct;

export  SoC_Map_IFC (..), mkSoC_Map;

export  N_External_Interrupt_Sources;
export  n_external_interrupt_sources;
export  irq_num_uart16550_0;

// ================================================================
// Bluespec library imports

import Routable :: *; // For Range

// ================================================================
// Project imports

import Fabric_Defs :: *;    // Only for type Fabric_Addr

// ================================================================
// Top-level-struct version of the SoC Map for RISCY-OOO

typedef struct {
   Bit #(64)  near_mem_io_addr_base;
   Bit #(64)  near_mem_io_addr_size;

   Bit #(64)  boot_rom_addr_base;
   Bit #(64)  boot_rom_addr_size;

   Bit #(64)  main_mem_addr_base;
   Bit #(64)  main_mem_addr_size;

   Bit #(64)  pc_reset_value;
   } SoC_Map_Struct
deriving (FShow);

SoC_Map_Struct soc_map_struct =
SoC_Map_Struct {
   near_mem_io_addr_base:  'h_1000_0000,
   near_mem_io_addr_size:  'h_0001_0000,

   boot_rom_addr_base:     'h_7000_0000,
   boot_rom_addr_size:     'h_0000_1000,

   main_mem_addr_base:     'h_C000_0000,
   main_mem_addr_size:     'h_C000_0000,

   pc_reset_value:         'h_7000_0000    // = boot_rom_addr_base
   };

// ================================================================

function Bool addr_function(Fabric_Addr base, Fabric_Addr size, Fabric_Addr addr);
   return (base <= addr) && (addr < (base + size));
endfunction

// ================================================================
// Interface and module for the address map

interface SoC_Map_IFC;
   (* always_ready *)   method  Range#(Wd_Addr)  m_plic_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_near_mem_io_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_flash_mem_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_ethernet_0_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_dma_0_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_timer_0_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_uart16550_0_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_gpio_0_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_boot_rom_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_ddr4_0_uncached_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_ddr4_0_cached_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_mem0_controller_addr_range;
   (* always_ready *)   method  Range#(Wd_Addr)  m_soc_config_addr_range;

   (* always_ready *)
   method  Bool  m_is_mem_addr (Fabric_Addr addr);

   (* always_ready *)
   method  Bool  m_is_IO_addr (Fabric_Addr addr, Bool imem_not_dmem);

   (* always_ready *)
   method  Bool  m_is_near_mem_IO_addr (Fabric_Addr addr);

   (* always_ready *)   method  Bit #(64)  m_pc_reset_value;
   (* always_ready *)   method  Bit #(64)  m_mtvec_reset_value;
   (* always_ready *)   method  Bit #(64)  m_nmivec_reset_value;
endinterface

// ================================================================

(* synthesize *)
module mkSoC_Map (SoC_Map_IFC);

   // ----------------------------------------------------------------
   // PLIC

   let plic_addr_range = Range {
      base: 'h_0C00_0000,
      size: 'h_0040_0000    // 4M
   };

   // ----------------------------------------------------------------
   // Near_Mem_IO (CLINT)

   let near_mem_io_addr_range = Range {
      base: 'h_1000_0000,
      size: 'h_0001_0000    // 64K
   };

   // ----------------------------------------------------------------
   // Flash Mem

   let flash_mem_addr_range = Range {
      base: 'h_4000_0000,
      size: 'h_0800_0000    // 128M
   };

   // ----------------------------------------------------------------
   // Ethernet 0

   let ethernet_0_addr_range = Range {
      base: 'h_6210_0000,
      size: 'h_0004_0000    // 256K
   };

   // ----------------------------------------------------------------
   // DMA 0

   let dma_0_addr_range = Range {
      base: 'h_6220_0000,
      size: 'h_0001_0000    // 64K
   };

   // ----------------------------------------------------------------
   // Timer

   let timer_0_addr_range = Range {
      base: 'h_5000_0000,
      size: 'h_0001_0000    // 64K
   };

   // ----------------------------------------------------------------
   // UART 0

   let uart16550_0_addr_range = Range {
      base: 'h_6230_0000,
      size: 'h_0000_1000    // 4K
   };

   // ----------------------------------------------------------------
   // GPIO 0

   let gpio_0_addr_range = Range {
      base: 'h_6FFF_0000,
      size: 'h_0001_0000    // 64K
   };

   // ----------------------------------------------------------------
   // Boot ROM

   let boot_rom_addr_range = Range {
      base: 'h_7000_0000,
      size: 'h_0000_1000    // 4K
   };

   // ----------------------------------------------------------------
   // DDR memory 0 uncached

   let ddr4_0_uncached_addr_range = Range {
      base: 'h_8000_0000,
      size: 'h_4000_0000    // 1G
   };

   // ----------------------------------------------------------------
   // DDR memory 0 cached

   let ddr4_0_cached_addr_range = Range {
      base: 'h_C000_0000,
      size: 'h_C000_0000    // 3G
   };

   // ----------------------------------------------------------------
   // SoC Config addresses

   let soc_config_addr_range = Range {
      base: 'h_0000_1000,
      size: 'h_0000_1000    // 4K
   };

   // ----------------------------------------------------------------

   function fn_is_flash_regs_addr = addr_function('h6240_0000, 'h1000);
   function fn_is_uart1_addr = addr_function('h6230_0000, 'h1000);
   function fn_is_i2c_addr = addr_function('h6231_0000, 'h1000);
   function fn_is_spi_addr = addr_function('h6232_0000, 'h1000);
   function fn_is_uart2_addr = addr_function('h6236_0000, 'h1000);
   function fn_is_gpio1_addr = addr_function('h6233_0000, 'h1000);
   function fn_is_gpio2_addr = addr_function('h6237_0000, 'h1000);

   function fn_is_xdma_control = addr_function('h2000_0000, 'h1000_0000);
   function fn_is_xdma_ecam = addr_function('h3000_0000, 'h1000_0000);

   // ----------------------------------------------------------------
   // Memory address predicate
   // Identifies memory addresses in the Fabric.
   // (Caches needs this information to cache these addresses.)

   function Bool fn_is_mem_addr (Fabric_Addr addr);
      return (   inRange(ddr4_0_cached_addr_range, addr)
	      );
   endfunction

   // ----------------------------------------------------------------
   // I/O address predicate
   // Identifies I/O addresses in the Fabric.
   // (Caches needs this information to avoid cacheing these addresses.)

   function Bool fn_is_IO_addr (Fabric_Addr addr, Bool imem_not_dmem);
      return (   inRange(boot_rom_addr_range, addr)
	      || inRange(ddr4_0_uncached_addr_range, addr)
	      || inRange(flash_mem_addr_range, addr)
              || inRange(soc_config_addr_range, addr)
	      || (   (! imem_not_dmem)
		  && (   inRange(plic_addr_range, addr)
		      || inRange(near_mem_io_addr_range, addr)
		      || inRange(ethernet_0_addr_range, addr)
		      || inRange(dma_0_addr_range, addr)
		      || inRange(timer_0_addr_range, addr)
		      || inRange(uart16550_0_addr_range, addr)
		      || inRange(gpio_0_addr_range, addr)
		      || fn_is_flash_regs_addr (addr)
		      || fn_is_uart1_addr (addr)
		      || fn_is_i2c_addr (addr)
		      || fn_is_spi_addr (addr)
		      || fn_is_uart2_addr (addr)
		      || fn_is_gpio1_addr (addr)
		      || fn_is_gpio2_addr (addr)
		      || fn_is_xdma_control (addr)
		      || fn_is_xdma_ecam (addr)
		      )
		  )
	      );
   endfunction

   // ----------------------------------------------------------------
   // PC, MTVEC and NMIVEC reset values

   Bit #(64) pc_reset_value     = boot_rom_addr_range.base;
   Bit #(64) mtvec_reset_value  = 'h1000;    // TODO
   Bit #(64) nmivec_reset_value = ?;         // TODO

   // ================================================================
   // INTERFACE

   method  Range#(Wd_Addr)  m_plic_addr_range = plic_addr_range;
   method  Range#(Wd_Addr)  m_near_mem_io_addr_range = near_mem_io_addr_range;
   method  Range#(Wd_Addr)  m_flash_mem_addr_range = flash_mem_addr_range;
   method  Range#(Wd_Addr)  m_ethernet_0_addr_range = ethernet_0_addr_range;
   method  Range#(Wd_Addr)  m_dma_0_addr_range = dma_0_addr_range;
   method  Range#(Wd_Addr)  m_timer_0_addr_range = timer_0_addr_range;
   method  Range#(Wd_Addr)  m_uart16550_0_addr_range = uart16550_0_addr_range;
   method  Range#(Wd_Addr)  m_gpio_0_addr_range = gpio_0_addr_range;
   method  Range#(Wd_Addr)  m_boot_rom_addr_range = boot_rom_addr_range;
   method  Range#(Wd_Addr)  m_ddr4_0_uncached_addr_range = ddr4_0_uncached_addr_range;
   method  Range#(Wd_Addr)  m_ddr4_0_cached_addr_range = ddr4_0_cached_addr_range;
   method  Range#(Wd_Addr)  m_mem0_controller_addr_range = ddr4_0_cached_addr_range;
   method  Range#(Wd_Addr)  m_soc_config_addr_range = soc_config_addr_range;

   method  Bool  m_is_mem_addr (Fabric_Addr addr) = fn_is_mem_addr (addr);

   method  Bool  m_is_IO_addr (Fabric_Addr addr, Bool imem_not_dmem) = fn_is_IO_addr (addr, imem_not_dmem);

   method  Bool  m_is_near_mem_IO_addr (Fabric_Addr addr) = inRange (near_mem_io_addr_range, addr);

   method  Bit #(64)  m_pc_reset_value     = pc_reset_value;
   method  Bit #(64)  m_mtvec_reset_value  = mtvec_reset_value;
   method  Bit #(64)  m_nmivec_reset_value = nmivec_reset_value;
endmodule

// ================================================================
// Interrupt request numbers (== index in to vector of
// interrupt-request lines in Core)

typedef  16  N_External_Interrupt_Sources;
Integer  n_external_interrupt_sources = valueOf (N_External_Interrupt_Sources);

Integer irq_num_uart16550_0 = 0;

// ================================================================

endpackage
