#! /usr/bin/env perl
#
# SPDX-License-Identifier: Apache-2.0
# Copyright 2019 Western Digital Corporation or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# 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.

#use strict;
use Data::Dumper;
use Getopt::Long;
use lib "$ENV{RV_ROOT}/tools";
use JSON;

my ($self) = $0 =~ m/.*\/(\w+)/o;
my @argv_orig = @ARGV;


# Master configuration file
#
# Configuration is perl hash
# Output are define files for various flows
#   Verilog (`defines common to RTL/TB)
#   Software (#defines)
#   Whisper (JSON/#defines)
#
#   Default values and valid ranges should be specified
#   Can be overridden via the cmd line (-set=name=value-string)
#
#  Format of the hash is
#    name => VALUE | LIST | HASH
#
#    Special name "inside" followed by list .. values must be one of provided list
#    Special name "derive" followed by equation to derive
#

# Dump verilog/assembly macros in upper case
my $defines_case = "U";

# Include these macros in verilog (pattern matched)
my @verilog_vars = qw (xlen reset_vec numiregs nmi_vec protection.* icg target testbench.* dccm.* retstack core.* iccm.* btb.* bht.* icache.* pic.* regwidth memmap bus.*);

# Include these macros in assembly (pattern matched)
my @asm_vars = qw (xlen reset_vec nmi_vec target dccm.* iccm.* pic.* memmap bus.* testbench.* protection.*);
my @asm_overridable = qw (reset_vec nmi_vec) ;

# Include these macros in PD (pattern matched)
my @pd_vars = qw (physical retstack target btb.* bht.* dccm.* iccm.* icache.* pic.* reset_vec nmi_vec build_ahb_lite datawidth bus.*);

# Dump non-derived/settable vars/values for these vars in stdout :
my @dvars = qw(retstack btb bht core dccm iccm icache pic bus.* reset_vec nmi_vec memmap bus);


# Prefix all macros with
my $prefix = "RV_";
# No prefix if keyword has
my $no_prefix = 'RV|TOP|^tec|regwidth|clock_period|assert_on|^datawidth|^physical|verilator|SDVT_AHB';

my $vlog_use__wh = 1;

my %regions_used = ();

# Cmd Line options#{{{
our %sets;
our %unsets;
my $help;
my @sets = ();
my @unsets = ();

#Configurations may be changed via the -set option
#
#  -set=name=value        : Change the default config parameter value (lowercase)\n";
#  -unset=name            : Remove the default config parameter (lowercase)\n";
#                         : Do not prepend RV_ prefex to -set/-unset variables\n";
#                         : multiple -set/-unset options accepted\n\n";
#

my $helpusage = "

Main configuration database for VEER

This script documents, and generates the {`#} define/include files for verilog/assembly/backend flows


User options:

     -target = { default, default_ahb, default_pd, high_perf}
        use default settings for one of the targets

     -set=var=value
        set arbitrary variable to a value
     -unset=var
        unset any definitions for var
     -snapshot=name
        name the configuration (only if no -target specified)

Direct options for the following parameters exist:

     -ret_stack_size =      {2, 3, 4, ... 8}
        size of return stack
     -btb_size =      { 32, 48, 64, 128, 256, 512 }
        size of branch target buffer
     -dccm_region =   { 0x0, 0x1, ... 0xf }
        number of 256Mb memory region containig DCCM
     -dccm_offset =   hexadecimal
        offset (in bytes) of DCCM witin dccm_region
        dccm address will be: 256M * dccm_region + dccm_offset\", and that must be aligned
          to the dccm size or the next larger power of 2 if size is not a power of 2
     -dccm_size   =   { 4, 8, 16, 32, 48, 64, 128, 256, 512 } kB
        size of DCCM
     -iccm_enable =   { 0, 1 }
        whether or not ICCM is enabled
     -icache_enable = { 0, 1 }
        whether or not icache is enabled
     -icache_ecc = { 0, 1 }
        whether or not icache has ecc - EXPENSIVE 30% sram growth
        default: icache_ecc==0 (parity)
     -iccm_region =   { 0x0, 0x1, ... 0xf  }
        number of 256Mb memory region containing ICCM
     -iccm_offset =   hexadecimal
        offcet (in bytes) of ICCM within iccm_region
        iccm address will be: \"256M * iccm_region + iccm_offset\", and that must be aligned
          to the iccm size or the next larger power of 2 if size is not a power of 2
     -iccm_size   =   { 4 , 8 , 16 , 32, 64, 128, 256, 512 } kB
        size of ICCM
     -icache_size =   { 16, 32, 64, 128, 256 } kB
        size of icache
     -pic_2cycle  =   { 0, 1 }
        whether or not 2-cycle PIC is enabled (2 cycle pic may result
        in an overall smaller cycle time)
     -pic_region =    { 0x0, 0x1, ... 0xf  }
         nuber of 256Mb memory region containing PIC memory-mapped registers
     -pic_offset =    hexadecial
         offset (in bytes) of PIC within pic_region
         pic address will be: \"256M * pic_region + pic_offset\", and that must be aligned
          to the pic size or the next larger power of 2 if size is not a power of 2
     -pic_size   =    { 32, 64, 128, 256 } kB
         size of PIC
     -pic_total_int = { 1, 2, 3, ..., 255 }
         number of interrupt sources in PIC
     -ahb_lite
         build with AHB-lite bus interface
         default is AXI4
     -fpga_optimize =   { 0, 1 }
        if 1, minimize clock-gating to facilitate FPGA builds
";


my $ret_stack_size;
my $btb_size;
my $bht_size;
my $dccm_region;
my $dccm_offset;
my $dccm_size;
my $iccm_enable;
my $icache_enable;
my $icache_ecc;
my $iccm_region;
my $iccm_offset;
my $iccm_size;
my $icache_size;
my $pic_2cycle;
my $pic_region;
my $pic_offset;
my $pic_size;
my $pic_total_int;
my $ahb_lite;
my $fpga_optimize;

my $top_align_iccm = 0;

my $target = "default";
my $snapshot ;
my $build_path ;

GetOptions(
    "help"              => \$help,
    "target=s"          => \$target,
    "snapshot=s"        => \$snapshot,
    "verbose"           => \$verbose,
    "ret_stack_size=s"  => \$ret_stack_size,
    "btb_size=s"        => \$btb_size,
    "bht_size=s"        => \$bht_size,
    "dccm_enable=s"     => \$dccm_enable,
    "dccm_region=s"     => \$dccm_region,
    "dccm_offset=s"     => \$dccm_offset,
    "dccm_size=s"       => \$dccm_size,
    "iccm_enable=s"     => \$iccm_enable,
    "icache_enable=s"   => \$icache_enable,
    "icache_ecc=s"      => \$icache_ecc,
    "iccm_region=s"     => \$iccm_region,
    "iccm_offset=s"     => \$iccm_offset,
    "iccm_size=s"       => \$iccm_size,
    "pic_2cycle=s"      => \$pic_2cycle,
    "pic_region=s"      => \$pic_region,
    "pic_offset=s"      => \$pic_offset,
    "pic_size=s"        => \$pic_size,
    "pic_total_int=s"   => \$pic_total_int,
    "icache_size=s"     => \$icache_size,
    "ahb_lite"          => \$ahb_lite,
    "fpga_optimize"     => \$fpga_optimize,
    "set=s@"            => \@sets,
    "unset=s@"          => \@unsets,
) || die("$helpusage");

if ($help) {
   print "$helpusage\n";
   exit;
}

if (!defined $snapshot ) {
    $snapshot = $target;
}

if (!defined $ENV{BUILD_PATH}) {
    $build_path = "$ENV{PWD}/snapshots/$snapshot" ;
} else {
    $build_path = $ENV{BUILD_PATH};
}

if (! -d "$build_path") {
    system ("mkdir -p $build_path");
}

# Verilog defines file path
my $vlogfile = "$build_path/common_defines.vh";

# Assembly defines file path
my $asmfile = "$build_path/defines.h";

# PD defines file path
my $pdfile = "$build_path/pd_defines.vh";

# Whisper config file path
my $whisperfile = "$build_path/whisper.json";

# Default linker file
my $linkerfile = "$build_path/link.ld";

# Perl defines file path
my $perlfile = "$build_path/perl_configs.pl";

my $no_secondary_alu=0;

if ($target eq "default") {
    if (!defined($ret_stack_size))    { $ret_stack_size=4; }
    if (!defined($btb_size))          { $btb_size=32; }
    if (!defined($bht_size))          { $bht_size=128; }
    if (!defined($dccm_enable))       { $dccm_enable=1; }
    if (!defined($dccm_region))       { $dccm_region="0xf"; }
    if (!defined($dccm_offset))       { $dccm_offset="0x40000"; } #1*256*1024
    if (!defined($dccm_size))         { $dccm_size=64; }
    if (!defined($dccm_num_banks))    { $dccm_num_banks=8; }
    if (!defined($iccm_enable))       { $iccm_enable=0; }
    if (!defined($iccm_region))       { $iccm_region="0xe"; }
    if (!defined($iccm_offset))       { $iccm_offset="0xe000000"; } #0x380*256*1024
    if (!defined($iccm_size))         { $iccm_size=512; }
    if (!defined($iccm_num_banks))    { $iccm_num_banks=8; }
    if (!defined($icache_enable))     { $icache_enable=1; }
    if (!defined($icache_ecc))        { $icache_ecc=0; }
    if (!defined($icache_size))       { $icache_size=16; }
    if (!defined($pic_2cycle))        { $pic_2cycle=0; }
    if (!defined($pic_region))        { $pic_region="0xf"; }
    if (!defined($pic_offset))        { $pic_offset="0xc0000"; } # 3*256*1024
    if (!defined($pic_size))          { $pic_size=32; }
    if (!defined($pic_total_int))     { $pic_total_int=8; }
    if (!defined($fpga_optimize))     { $fpga_optimize=1; }

    # default is AXI bus
}
elsif ($target eq "default_ahb") {
    if (!defined($ret_stack_size))    { $ret_stack_size=4; }
    if (!defined($btb_size))          { $btb_size=32; }
    if (!defined($bht_size))          { $bht_size=128; }
    if (!defined($dccm_enable))       { $dccm_enable=1; }
    if (!defined($dccm_region))       { $dccm_region="0xf"; }
    if (!defined($dccm_offset))       { $dccm_offset="0x40000"; } #1*256*1024
    if (!defined($dccm_size))         { $dccm_size=64; }
    if (!defined($dccm_num_banks))    { $dccm_num_banks=8; }
    if (!defined($iccm_enable))       { $iccm_enable=0; }
    if (!defined($iccm_region))       { $iccm_region="0xe"; }
    if (!defined($iccm_offset))       { $iccm_offset="0xe000000"; } #0x380*256*1024
    if (!defined($iccm_size))         { $iccm_size=512; }
    if (!defined($iccm_num_banks))    { $iccm_num_banks=8; }
    if (!defined($icache_enable))     { $icache_enable=1; }
    if (!defined($icache_ecc))        { $icache_ecc=0; }
    if (!defined($icache_size))       { $icache_size=16; }
    if (!defined($pic_2cycle))        { $pic_2cycle=0; }
    if (!defined($pic_region))        { $pic_region="0xf"; }
    if (!defined($pic_offset))        { $pic_offset="0xc0000"; } # 3*256*1024
    if (!defined($pic_size))          { $pic_size=32; }
    if (!defined($pic_total_int))     { $pic_total_int=8; }
    if (!defined($fpga_optimize))     { $fpga_optimize=1; }
    $ahb_lite = 1;

} elsif ($target eq "default_pd") {
    if (!defined($ret_stack_size))    { $ret_stack_size=4; }
    if (!defined($btb_size))          { $btb_size=32; }
    if (!defined($bht_size))          { $bht_size=128; }
    if (!defined($dccm_enable))       { $dccm_enable=1; }
    if (!defined($dccm_region))       { $dccm_region="0xf"; }
    if (!defined($dccm_offset))       { $dccm_offset="0x40000"; } #1*256*1024
    if (!defined($dccm_size))         { $dccm_size=32; }
    if (!defined($dccm_num_banks))    { $dccm_num_banks=8; }
    if (!defined($iccm_enable))       { $iccm_enable=0; }
    if (!defined($iccm_region))       { $iccm_region="0xe"; }
    if (!defined($iccm_offset))       { $iccm_offset="0xe000000"; } #0x380*256*1024
    if (!defined($iccm_size))         { $iccm_size=512; }
    if (!defined($iccm_num_banks))    { $iccm_num_banks=8; }
    if (!defined($icache_enable))     { $icache_enable=1; }
    if (!defined($icache_ecc))        { $icache_ecc=0; }
    if (!defined($icache_size))       { $icache_size=16; }
    if (!defined($pic_2cycle))        { $pic_2cycle=0; }
    if (!defined($pic_region))        { $pic_region="0xf"; }
    if (!defined($pic_offset))        { $pic_offset="0xc0000"; } # 3*256*1024
    if (!defined($pic_size))          { $pic_size=32; }
    if (!defined($pic_total_int))     { $pic_total_int=8; }
    if (!defined($fpga_optimize))     { $fpga_optimize=0; }

} elsif ($target eq "high_perf") {
    if (!defined($ret_stack_size))    { $ret_stack_size=4; }
    if (!defined($btb_size))          { $btb_size=512; }
    if (!defined($bht_size))          { $bht_size=2048; }
    if (!defined($dccm_enable))       { $dccm_enable=1; }
    if (!defined($dccm_region))       { $dccm_region="0xf"; }
    if (!defined($dccm_offset))       { $dccm_offset="0x40000"; } #1*256*1024
    if (!defined($dccm_size))         { $dccm_size=64; }
    if (!defined($dccm_num_banks))    { $dccm_num_banks=8; }
    if (!defined($iccm_enable))       { $iccm_enable=0; }
    if (!defined($iccm_region))       { $iccm_region="0xe"; }
    if (!defined($iccm_offset))       { $iccm_offset="0xe000000"; } #0x380*256*1024
    if (!defined($iccm_size))         { $iccm_size=512; }
    if (!defined($iccm_num_banks))    { $iccm_num_banks=8; }
    if (!defined($icache_enable))     { $icache_enable=1; }
    if (!defined($icache_ecc))        { $icache_ecc=0; }
    if (!defined($icache_size))       { $icache_size=32; }
    if (!defined($pic_2cycle))        { $pic_2cycle=0; }
    if (!defined($pic_region))        { $pic_region="0xf"; }
    if (!defined($pic_offset))        { $pic_offset="0xc0000"; } # 3*256*1024
    if (!defined($pic_size))          { $pic_size=32; }
    if (!defined($pic_total_int))     { $pic_total_int=8; }
    if (!defined($fpga_optimize))     { $fpga_optimize=1; }

} else {
    die "$self: ERROR! Unsupported target \"$target\". Supported targets are: \"default, default_ahb, default_pd, high_perf\"!\n";
}

# general stuff - can't set from command line other than -set

if (!defined($lsu_stbuf_depth))   { $lsu_stbuf_depth=8;   }
if (!defined($dma_buf_depth))     { $dma_buf_depth=4;     }
if (!defined($lsu_num_nbload))    { $lsu_num_nbload=8;   }
if (!defined($dec_instbuf_depth)) { $dec_instbuf_depth=4; }


# Configure triggers
our @triggers = (#{{{
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081810c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081810c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081818c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081818c7", "0xffffffff", "0x00000000"]
    },
    {
        "reset"         => ["0x23e00000", "0x00000000", "0x00000000"],
        "mask"          => ["0x081810c7", "0xffffffff", "0x00000000"],
        "poke_mask"     => ["0x081810c7", "0xffffffff", "0x00000000"]
    },
 );#}}}


# Configure CSRs
our %csr = (#{{{
    "mstatus" => {
       "reset"         => "0x1800", # MPP bits hard wired to binrary 11.
       "mask"          => "0x88",   # Only mpie(7) & mie(3) bits writeable
       "exists"        => "true",
    },
    "mie" => {
        "reset"         => "0x0",
        # Only external, timer, local, and software writeable
        "mask"          => "0x70000888",
        "exists"        => "true",
    },
    "mip" => {
        "reset"         => "0x0",
        # None of the bits are writeable using CSR instructions
        "mask"          => "0x0",
        # Bits corresponding to error overflow, external, timer and stoftware
        # interrupts are modifiable
        "poke_mask"     => "0x70000888",
        "exists"        => "true",
    },
   "mvendorid" => {
       "reset"         => "0x45",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "marchid" => {
       "reset"         => "0x0000000b",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "mimpid" => {
       "reset"         => "0x6",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "misa" => {
       "reset"         => "0x40001104",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "tselect" => {
       "reset"         => "0x0",
       "mask"          => "0x3",    # Four triggers
       "exists"        => "true",
   },
   "dcsr" => {
       "reset"         => "0x40000003",
       "mask"          => "0x00008c04",
       "poke_mask"     => "0x00008dcc",  # cause field modifiable, nmip modifiable
       "exists"        => "true",
    },
    "cycle" => {
        "exists"       => "false",
    },
    "time" => {
        "exists"       => "false",
    },
    "instret" => {
        "exists"       => "false",
    },
    "mcountinhibit" => {
       "exists"        => "false",
    },
    "mcounteren" => {
       "exists"        => "false",
    },
    "mhpmcounter3" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter4" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter5" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter6" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter3h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter4h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter5h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmcounter6h" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent3" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent4" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent5" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mhpmevent6" => {
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
# Remaining CSRs are non-standard. These are specific to VEER
    "dicawics" => {
       "number"        => "0x7c8",
       "reset"         => "0x0",
       "mask"          => "0x0130fffc",
       "exists"        => "true",
    },
    "dicad0" => {
       "number"        => "0x7c9",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "dicad1" => {
       "number"        => "0x7ca",
       "reset"         => "0x0",
       "mask"          => "0x3",
       "exists"        => "true",
    },
    "dicago" => {
       "number"        => "0x7cb",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "mgpmc" => {
       "number"        => "0x7d0",
       "reset"         => "0x1",
       "mask"          => "0x1",
       "exists"        => "true",
    },
    "mitcnt0" => {
       "number"        => "0x7d2",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitbnd0" => {
       "number"        => "0x7d3",
       "reset"         => "0xffffffff",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitctl0" => {
       "number"        => "0x7d4",
       "reset"         => "0x1",
       "mask"          => "0x00000007",
       "exists"        => "true",
    },
    "mitcnt1" => {
       "number"        => "0x7d5",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitbnd1" => {
       "number"        => "0x7d6",
       "reset"         => "0xffffffff",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mitctl1" => {
       "number"        => "0x7d7",
       "reset"         => "0x1",
       "mask"          => "0x00000007",
       "exists"        => "true",
    },
    "mcpc" => {
       "number"        => "0x7c2",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "mpmc" => {
       "comment"       => "FWHALT",
       "number"        => "0x7c6",
       "reset"         => "0x2",
       "mask"          => "0x2",
       "poke_mask"     => "0x2",
       "exists"        => "true",
    },
    "micect" => {
       "number"        => "0x7f0",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "miccmect" => {
       "number"        => "0x7f1",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mdccmect" => {
       "number"        => "0x7f2",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
    },
    "mcgc" => {
       "number"        => "0x7f8",
       "reset"         => "0x0",
       "mask"          => "0x000001ff",
       "poke_mask"     => "0x000001ff",
       "exists"        => "true",
    },
    "mfdc" => {
       "number"        => "0x7f9",
       "reset"         => "0x00070000",
       "mask"          => "0x000727ff",
       "exists"        => "true",
    },
    "dmst" => {
       "comment"       => "Memory synch trigger: Flush caches in debug mode.",
       "number"        => "0x7c4",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicawics" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7c8",
       "reset"         => "0x0",
       "mask"          => "0x0130fffc",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicad0" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7c9",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicad1" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7ca",
       "reset"         => "0x0",
       "mask"          => "0x3",
       "exists"        => "true",
       "debug"         => "true",
    },
    "dicago" => {
       "comment"       => "Cache diagnostics.",
       "number"        => "0x7cb",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
       "debug"         => "true",
    },
    "meipt" => {
       "comment"       => "External interrupt priority threshold.",
       "number"        => "0xbc9",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },
    "meicpct" => {
       "comment"       => "External claim id/priority capture.",
       "number"        => "0xbca",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "meicidpl" => {
       "comment"       => "External interrupt claim id priority level.",
       "number"        => "0xbcb",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },
    "meicurpl" => {
       "comment"       => "External interrupt current priority level.",
       "number"        => "0xbcc",
       "reset"         => "0x0",
       "mask"          => "0xf",
       "exists"        => "true",
    },

);#}}}


foreach my $i (0 .. 3) {
    $csr{"pmpcfg$i"} = { "exists" => "false" };
}

foreach my $i (0 .. 15) {
    $csr{"pmpaddr$i"} = { "exists" => "false" };
}



# }}}
# Main config hash, with default values
#
# Hash can be hierarchical with arbitrary levels
# Hexadecimal values are prefixed with 0x
#
# For verilog, if bit width is expected, add to %width hash below
our %config = (#{{{
    "harts"                 => "1",
    "xlen"                  => "32",
    "numiregs"              => "32",
    "regwidth"              => "32",
    "reset_vec"             => "0x80000000",
    "nmi_vec"               => "0x11110000",
    "physical"              => "1",
    "num_mmode_perf_regs"   => "4",   # Whisper only
    "max_mmode_perf_event"  => "50",  # Whisper only: performance counters event ids will be clamped to this
    "target"                => $target,
    "tec_rv_icg"            => "clockhdr",
        "verilator"          => "$verilator",

    "retstack" => {
            "ret_stack_size"    => "$ret_stack_size",
    },

    "btb"  => {
         "btb_size"         => "$btb_size",
         "btb_index1_hi"    => "derived",
         "btb_index1_lo"    => "4",
         "btb_index2_hi"    => "derived",
         "btb_index2_lo"    => "derived",
         "btb_index3_hi"    => "derived",
         "btb_index3_lo"    => "derived",
         "btb_addr_hi"      => "derived",
         "btb_array_depth"  => "derived",
         "btb_addr_lo"      => "4",
         "btb_btag_size"    => "derived",
    },
    "bht"  => {
         "bht_size"         => "$bht_size",
         "bht_addr_hi"      => "derived",
         "bht_addr_lo"      => "4",
         "bht_array_depth"  => "derived",
         "bht_ghr_size"     => "derived",
         "bht_ghr_range"    => "derived",
         "bht_ghr_pad"      => "derived",
         "bht_ghr_pad2"     => "derived",
         "bht_hash_string"  => "derived",

    },

    "core" => {
        "dec_instbuf_depth" => "$dec_instbuf_depth",
        "lsu_stbuf_depth"   => "$lsu_stbuf_depth",
        "dma_buf_depth"     => "$dma_buf_depth",
        "lsu_num_nbload"    => "$lsu_num_nbload",
        "no_secondary_alu"  => "$no_secondary_alu",
        "fpga_optimize"     => "$fpga_optimize"
    },

    "dccm" => {
        "dccm_enable"       => "$dccm_enable",                          # Comment this out if no DCCM
        "dccm_region"       => "$dccm_region",                       # 256M region number
        "dccm_offset"       => "$dccm_offset",                         # 256K offset number
        "dccm_size"         => "$dccm_size",                       # Size in Kbytes
        "dccm_num_banks"    => "$dccm_num_banks",
        "dccm_sadr"         => 'derived',
        "dccm_eadr"         => 'derived',
         "dccm_bits"         => 'derived',
        "dccm_bank_bits"         => 'derived',
        "dccm_data_width"         => 'derived',
        "dccm_fdata_width"         => 'derived',
        "dccm_byte_width"         => 'derived',
        "dccm_width_bits"         => 'derived',
        "dccm_index_bits"         => 'derived',
        "dccm_ecc_width"         => 'derived',
        "lsu_sb_bits"         => 'derived',
        "dccm_data_cell"     => 'derived',
        "dccm_rows"          => 'derived',
        "dccm_reserved"     => 'derived',                                  # reserve dccm space for SW/stack - no random r/w
    },


    "iccm" => {
        "iccm_enable"       => "$iccm_enable",                          # Comment this out if no ICCM
        "iccm_region"       => "$iccm_region",                       # 256M region number
        "iccm_offset"       => "$iccm_offset",                     # 256K offset number
        "iccm_size"         => "$iccm_size",                       # Size in Kbytes
        "iccm_num_banks"    => "$iccm_num_banks",
        "iccm_bank_bits"    => 'derived',
        "iccm_index_bits"   => 'derived',
        "iccm_rows"         => 'derived',
        "iccm_data_cell"    => 'derived',
        "iccm_sadr"         => 'derived',
        "iccm_eadr"         => 'derived',
        "iccm_reserved"     => 'derived',                                  # reserve iccm space for SW/handlers - no random r/w
    },
    "icache" => {
        "icache_enable"     => "$icache_enable",
        "icache_size"       => "$icache_size",
        "icache_data_cell"  => 'derived',
        "icache_tag_cell"   => 'derived',
        "icache_taddr_high" => 'derived',
        "icache_tag_high"   => 'derived',
        "icache_tag_depth"  => 'derived',
        "icache_ic_depth"   => 'derived',
        "icache_ic_rows"    => 'derived',
        "icache_ic_index"    => 'derived',
        "icache_tag_low"    => '6',
        "icache_ecc"        => "$icache_ecc",
    },
    "pic" => {
        "pic_2cycle"         => "$pic_2cycle",   # two cycle PIC for timing reasons
        "pic_region"         => "$pic_region",   # 256M region number
        "pic_offset"         => "$pic_offset",   # 256K offset number
        "pic_size"           => "$pic_size",     # Size in Kbytes
        "pic_base_addr"      => 'derived', # base_addr = pic_region + offset
        "pic_total_int_plus1"      => 'derived', # pic_total_int + 1
        "pic_total_int"      => "$pic_total_int",# number of interrupt pins (Smax)
        "pic_int_words"      => 'derived', # number of 32 bit words for packed registers (Xmax)
        "pic_bits"           => 'derived', # of bits needs to address the PICM
        "pic_meipl_offset"   => '0x0000',        # Offset of meipl relative to pic_base_addr
        "pic_meip_offset"    => '0x1000',        # Offset of meip relative to pic_base_addr
        "pic_meie_offset"    => '0x2000',        # Offset of meie relative to pic_base_addr
        "pic_mpiccfg_offset" => '0x3000',        # Offset of mpiccfg relative to pic_base_addr
        "pic_meipt_offset"   => '0x3004',        # Offset of meipt relative to pic_base_addr -- deprecated
        "pic_meigwctrl_offset" => '0x4000',      # gateway control regs relative to pic_base_addr
        "pic_meigwclr_offset"  => '0x5000',       # gateway clear regs relative to pic_base_addr
  "pic_meipl_mask"     => '0xf',
        "pic_meip_mask"      => '0x0',
        "pic_meie_mask"      => '0x1',
        "pic_mpiccfg_mask"   => '0x1',
        "pic_meipt_mask"     => '0x0',
        "pic_meigwctrl_mask" => '0x3',
        "pic_meigwclr_mask"  => '0x0',

        "pic_meipl_count"     => $pic_total_int,
        "pic_meip_count"      => 4,
        "pic_meie_count"      => $pic_total_int,
        "pic_mpiccfg_count"   => 1,
        "pic_meipt_count"     => $pic_total_int,
        "pic_meigwctrl_count" => $pic_total_int,
        "pic_meigwclr_count"  => $pic_total_int
    },
    "testbench" => {
        "TOP"               => "tb_top",
        "RV_TOP"            => "`TOP.rvtop",
        "CPU_TOP"            => "`RV_TOP.veer",
        "clock_period"      => "100",
        "build_ahb_lite"    => "$ahb_lite",       # one and only one bus build arg will ever be defined
        "build_axi4"        => "1",
        "assert_on"         => "",
        "datawidth"         => "64",                        # deprecate this !! FIXME
        "ext_datawidth"     => "64",
        "ext_addrwidth"     => "32",
        "sterr_rollback"    => "0",
        "lderr_rollback"    => "1",
        "SDVT_AHB"          => "1",
    },
   "protection" => {
        "inst_access_enable0" => "0x0",
        "inst_access_addr0"   => "0x00000000",
        "inst_access_mask0"   => "0xffffffff",
        "inst_access_enable1" => "0x0",
        "inst_access_addr1"   => "0x00000000",
        "inst_access_mask1"   => "0xffffffff",
        "inst_access_enable2" => "0x0",
        "inst_access_addr2"   => "0x00000000",
        "inst_access_mask2"   => "0xffffffff",
        "inst_access_enable3" => "0x0",
        "inst_access_addr3"   => "0x00000000",
        "inst_access_mask3"   => "0xffffffff",
        "inst_access_enable4" => "0x0",
        "inst_access_addr4"   => "0x00000000",
        "inst_access_mask4"   => "0xffffffff",
        "inst_access_enable5" => "0x0",
        "inst_access_addr5"   => "0x00000000",
        "inst_access_mask5"   => "0xffffffff",
        "inst_access_enable6" => "0x0",
        "inst_access_addr6"   => "0x00000000",
        "inst_access_mask6"   => "0xffffffff",
        "inst_access_enable7" => "0x0",
        "inst_access_addr7"   => "0x00000000",
        "inst_access_mask7"   => "0xffffffff",
        "data_access_enable0" => "0x0",
        "data_access_addr0"   => "0x00000000",
        "data_access_mask0"   => "0xffffffff",
        "data_access_enable1" => "0x0",
        "data_access_addr1"   => "0x00000000",
        "data_access_mask1"   => "0xffffffff",
        "data_access_enable2" => "0x0",
        "data_access_addr2"   => "0x00000000",
        "data_access_mask2"   => "0xffffffff",
        "data_access_enable3" => "0x0",
        "data_access_addr3"   => "0x00000000",
        "data_access_mask3"   => "0xffffffff",
        "data_access_enable4" => "0x0",
        "data_access_addr4"   => "0x00000000",
        "data_access_mask4"   => "0xffffffff",
        "data_access_enable5" => "0x0",
        "data_access_addr5"   => "0x00000000",
        "data_access_mask5"   => "0xffffffff",
        "data_access_enable6" => "0x0",
        "data_access_addr6"   => "0x00000000",
        "data_access_mask6"   => "0xffffffff",
        "data_access_enable7" => "0x0",
        "data_access_addr7"   => "0x00000000",
        "data_access_mask7"   => "0xffffffff",
     },
    "memmap" => {                                                       # Testbench only
        "serialio"          => 'derived, overridable',                  # Testbench only
        "external_data"     => 'derived, overridable',                  # Testbench only
        "external_prog"     => 'derived, overridable',                  # Testbench only
        "debug_sb_mem"      => 'derived, overridable',                  # Testbench only
        "external_data_1"   => 'derived, overridable',                  # Testbench only
        "external_mem_hole"   => 'default disabled',                    # Testbench only
#       "consoleio"         => 'derived',   # Part of serial io.
    },
    "bus" => {
        "lsu_bus_tag"      => 'derived',
        "dma_bus_tag"      => '1',
        "sb_bus_tag"       => '1',
        "ifu_bus_tag"      => '3',
    },
    "triggers" => \@triggers,
    "csr" => \%csr,
    "even_odd_trigger_chains" => "true",
);

if (($fpga_optimize==0) && !grep(/fpga_optimize/, @sets)) { delete $config{"core"}{"fpga_optimize"}; }
if (($iccm_enable==0) && !grep(/iccm_enable/, @sets)) { delete $config{"iccm"}{"iccm_enable"}; }
if (($dccm_enable==0) && !grep(/dccm_enable/, @sets)) { delete $config{"dccm"}{"dccm_enable"}; }
if (($icache_enable==0) && !grep(/icache_enable/, @sets)) { delete $config{"icache"}{"icache_enable"}; }
if (($verilator==0) && !grep(/verilator/, @sets))     { delete $config{"core"}{"verilator"}; }
if (($no_secondary_alu==0) && !grep(/no_secondary_alu/, @sets))           { delete $config{"core"}{"no_secondary_alu"}; }
if (($pic_2cycle==0) && !grep(/pic_2cycle/, @sets)) { delete $config{"pic"}{"pic_2cycle"}; }
if (($icache_ecc==0) && !grep(/icache_ecc/, @sets)) { delete $config{"icache"}{"icache_ecc"}; }



# Perform any overrides first before derived values
map_set_unset();
gen_define("","", \%config,[]);
print "\nVeeR configuration for target=$target\n\n";
dump_define("","", \%config,[]);


# perform final checks
my $c;
$c=$config{retstack}{ret_stack_size}; if (!($c >=2 && $c <=8))                                 { die("$helpusage\n\nFAIL: ret_stack_size == $c;   ILLEGAL !!!\n\n"); }
$c=$config{btb}{btb_size};            if (!($c==32||$c==48||$c==64||$c==128||$c==256||$c==512)){ die("$helpusage\n\nFAIL: btb_size == $c;         ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_region};        if (!($c>=0 && $c<16))                                   { die("$helpusage\n\nFAIL: iccm_region == $c       ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_offset};        if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: iccm_offset == $c       ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_size};          if (!($c==4||$c==8||$c==16||$c==32||$c==64||$c==128||$c==256||$c==512)) { die("$helpusage\n\nFAIL: iccm_size == $c         ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_num_banks};     if (!($c==4 || $c==8  || ($c==16 && $config{iccm}{iccm_size} != 4)))    { die("$helpusage\n\nFAIL: iccm_num_banks == $c    ILLEGAL !!!\n\n"); }
$c=$config{iccm}{iccm_enable};        if (!($c==0  || $c==1))                                  { die("$helpusage\n\nFAIL: iccm_enable == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_region};        if (!($c>=0  && $c<16))                                  { die("$helpusage\n\nFAIL: iccm_region == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_num_banks};     if (!($c==4 || $c==8 || ($c==16 && $config{dccm}{dccm_size} != 4) ))                                   { die("$helpusage\n\nFAIL: dccm_num_banks == $c    ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_offset};        if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: iccm_offset == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_size};          if (!($c==4||$c==8||$c==16||$c==32||$c==48||$c==64||$c==128||$c==256||$c==512))        { die("$helpusage\n\nFAIL: iccm_size == $c         ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_2cycle};          if (!($c==0  || $c==1))                                  { die("$helpusage\n\nFAIL: pic_2cycle == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_region};          if (!($c>=0 && $c<16))                                   { die("$helpusage\n\nFAIL: pic_region == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_offset};          if (!($c>=0  && $c<256*1024*1024 && ($c&0xfff)==0))      { die("$helpusage\n\nFAIL: pic_offset == $c        ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_size};            if (!($c==32 || $c==64 || $c==128 || $c==256))           { die("$helpusage\n\nFAIL: pic_size == $c          ILLEGAL !!!\n\n"); }
$c=$config{pic}{pic_total_int};       if (  $c<1 || $c>255)                                    { die("$helpusage\n\nFAIL: pic_total_int == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_enable};    if (!($c==0 || $c==1))                                   { die("$helpusage\n\nFAIL: icache_enable == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_size};      if (!($c==16 || $c==32 || $c==64 || $c==128 || $c==256)) { die("$helpusage\n\nFAIL: icache_size == $c       ILLEGAL !!!\n\n"); }
$c=$config{core}{dec_instbuf_depth};  if (!($c==2 || $c==4))                                   { die("$helpusage\n\nFAIL: dec_instbuf_depth == $c ILLEGAL !!!\n\n"); }
$c=$config{core}{lsu_stbuf_depth};    if (!($c==2 || $c==4 || $c==8))                          { die("$helpusage\n\nFAIL: lsu_stbuf_depth == $c   ILLEGAL !!!\n\n"); }
$c=$config{core}{dma_buf_depth};      if (!($c==2 || $c==4))                                   { die("$helpusage\n\nFAIL: dma_buf_depth == $c     ILLEGAL !!!\n\n"); }
$c=$config{core}{lsu_num_nbload};     if (!($c==2 || $c==4 || $c==8))                          { die("$helpusage\n\nFAIL: lsu_num_nbload == $c   ILLEGAL !!!\n\n"); }

$c=$config{protection}{inst_access_addr0}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr0 lower 6b must be 0s $c !!!\n\n"); }
$c=$config{protection}{inst_access_addr1}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr1 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr2}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr2 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr3}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr3 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr4}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr4 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr5}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr5 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr6}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr6 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_addr7}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: inst_access_addr7 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{inst_access_mask0}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask0 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask1}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask1 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask2}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask2 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask3}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask3 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask4}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask4 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask5}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask5 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask6}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask6 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{inst_access_mask7}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: inst_access_mask7 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_addr0}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr0 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr1}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr1 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr2}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr2 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr3}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr3 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr4}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr4 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr5}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr5 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr6}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr6 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_addr7}; if ((hex($c)&0x3f) != 0)                                { die("$helpusage\n\nFAIL: data_access_addr7 lower 6b must be 0s !!!\n\n"); }
$c=$config{protection}{data_access_mask0}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask0 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask1}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask1 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask2}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask2 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask3}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask3 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask4}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask4 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask5}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask5 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask6}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask6 lower 6b must be 1s !!!\n\n"); }
$c=$config{protection}{data_access_mask7}; if ((hex($c)&0x3f) != 63)                               { die("$helpusage\n\nFAIL: data_access_mask7 lower 6b must be 1s !!!\n\n"); }



if (($config{testbench}{build_ahb_lite} ne "")) {
    delete $config{testbench}{build_axi4};
}
else {  # default is AXI bus
    delete $config{testbench}{build_ahb_lite};
}


# Over-ride MFDC reset value for AXI.
if (exists($config{testbench}{build_axi4}) ) {
    $config{csr}{mfdc}{reset} = "0x00070040" if exists $config{csr}{mfdc};
}



# Fill in derived configuration entries.

if($config{btb}{btb_size}==512){
    $config{btb}{btb_index1_hi} = 9;
    $config{btb}{btb_index2_hi} = 15;
    $config{btb}{btb_index3_hi} = 21;
    $config{btb}{btb_array_depth}= 64;
    $config{btb}{btb_btag_size} = 5;
} elsif($config{btb}{btb_size}==256){
    $config{btb}{btb_index1_hi} = 8;
    $config{btb}{btb_index2_hi} = 13;
    $config{btb}{btb_index3_hi} = 18;
    $config{btb}{btb_array_depth}= 32;
    $config{btb}{btb_btag_size} = 6;
} elsif($config{btb}{btb_size}==128){
    $config{btb}{btb_index1_hi} = 7;
    $config{btb}{btb_index2_hi} = 11;
    $config{btb}{btb_index3_hi} = 15;
    $config{btb}{btb_array_depth}= 16;
    $config{btb}{btb_btag_size} = 7;
} elsif($config{btb}{btb_size}==64){
    $config{btb}{btb_index1_hi} = 6;
    $config{btb}{btb_index2_hi} = 9;
    $config{btb}{btb_index3_hi} = 12;
    $config{btb}{btb_array_depth}= 8;
    $config{btb}{btb_btag_size} = 8;
} elsif($config{btb}{btb_size}==48){
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 7;
    $config{btb}{btb_index3_hi} = 9;
    $config{btb}{btb_array_depth}= 4;
    $config{btb}{btb_48}= 1;
    $config{btb}{btb_fold2_index_hash} = 1;
    $config{btb}{btb_btag_size} = 9;
    $config{btb}{btb_btag_fold} = 1;
} elsif($config{btb}{btb_size}==32){
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 7;
    $config{btb}{btb_index3_hi} = 9;
    $config{btb}{btb_array_depth}= 4;
    $config{btb}{btb_btag_size} = 9;
    $config{btb}{btb_btag_fold} = 1;
}

$config{btb}{btb_index2_lo} = $config{btb}{btb_index1_hi}+1;
$config{btb}{btb_index3_lo} = $config{btb}{btb_index2_hi}+1;
$config{btb}{btb_addr_hi}   = $config{btb}{btb_index1_hi};

# BHT index is a hash of the GHR and PC_HASH
sub ghrhash{
    my($btb_index_hi,$ghr_size) = @_;

    $btb_addr_width = $btb_index_hi - 3;

    $ghr_hi = $ghr_size - 1;
    $ghr_lo = $btb_addr_width;

    $ghr_start = "{";
    if($ghr_size > $btb_addr_width){
        if($ghr_size-1 == $btb_addr_width){
            $string= "{ghr[$ghr_hi:$ghr_lo] ^ ghr[$ghr_hi+1],hashin[$config{btb}{btb_index1_hi}:4]^ghr[$ghr_lo-1:0]}";
        }
        else{
            $string = "{ghr[$ghr_hi:$ghr_lo] ^ {ghr[$ghr_hi+1], {$ghr_size-1-$btb_addr_width\{1'b0} } },hashin[$config{btb}{btb_index1_hi}:4]^ghr[$ghr_lo-1:0]}";
        }
    }
    elsif($ghr_size < $btb_addr_width){
        $string = "{hashin[$ghr_size+3:4]^ghr[$ghr_size-1:0]^{ghr[$ghr_hi+1], {$ghr_hi\{1'b0} } }}";
    }
    else{ $string = "{hashin[$config{btb}{btb_index1_hi}:4]^ghr[$ghr_lo-1:0]^{ghr[$ghr_hi+1], {$btb_addr_width-1\{1'b0} } } }"}
    return $string;

}


if($config{bht}{bht_size}==2048){
    $config{bht}{bht_ghr_size}= 9;
    $config{bht}{bht_ghr_range}= "8:0";
    $config{bht}{bht_ghr_pad}= "fghr[8:4],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[8:3],2'b0";
    $config{bht}{bht_array_depth}= 256;
    $config{bht}{bht_addr_hi}= 11;
} elsif($config{bht}{bht_size}==1024){
    $config{bht}{bht_ghr_size}= 8;
    $config{bht}{bht_ghr_range}= "7:0";
    $config{bht}{bht_ghr_pad}= "fghr[7:4],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[7:3],2'b0";
    $config{bht}{bht_array_depth}= 128;
    $config{bht}{bht_addr_hi}= 10;
} elsif($config{bht}{bht_size}==512){
    $config{bht}{bht_ghr_size}= 7;
    $config{bht}{bht_ghr_range}= "6:0";
    $config{bht}{bht_ghr_pad}= "fghr[6:4],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[6:3],2'b0";
    $config{bht}{bht_array_depth}= 64;
    $config{bht}{bht_addr_hi}= 9;
} elsif($config{bht}{bht_size}==256){
    $config{bht}{bht_ghr_size}= 6;
    $config{bht}{bht_ghr_range}= "5:0";
    $config{bht}{bht_ghr_pad}= "fghr[5:4],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[5:3],2'b0";
    $config{bht}{bht_addr_hi} = 8;
    $config{bht}{bht_array_depth}= 32;
} elsif($config{bht}{bht_size}==128){
    $config{bht}{bht_ghr_size}= 5;
    $config{bht}{bht_ghr_range}= "4:0";
    $config{bht}{bht_ghr_pad}= "fghr[4],3'b0";
    $config{bht}{bht_ghr_pad2}= "fghr[4:3],2'b0";
    $config{bht}{bht_addr_hi} = 7;
    $config{bht}{bht_array_depth}= 16;
} elsif($config{bht}{bht_size}==64){
    $config{bht}{bht_ghr_size}= 4;
    $config{bht}{bht_ghr_range}= "3:0";
    $config{bht}{bht_ghr_pad}= "3'b0 ";
    $config{bht}{bht_ghr_pad2}= "fghr[3],2'b0";
    $config{bht}{bht_addr_hi} = 6;
    $config{bht}{bht_array_depth}= 8;
} elsif($config{bht}{bht_size}==32){
    $config{bht}{bht_ghr_size}= 3;
    $config{bht}{bht_ghr_range}= "2:0";
    $config{bht}{bht_ghr_pad}= "2'b0 ";
    $config{bht}{bht_ghr_pad2}= "2'b0";
    $config{bht}{bht_addr_hi} = 5;
    $config{bht}{bht_array_depth}= 4;
}

$config{bht}{bht_hash_string} = &ghrhash($config{btb}{btb_index1_hi}, $config{bht}{bht_ghr_size}-1);

$config{pic}{pic_base_addr} = (hex($config{pic}{pic_region})<<28) +
    (hex($config{pic}{pic_offset}));
$config{pic}{pic_base_addr} = sprintf("0x%x", $config{pic}{pic_base_addr});

$config{pic}{pic_int_words} = int($config{pic}{pic_total_int}/32 +0.9);
$config{pic}{pic_bits} = 10 + log2($config{pic}{pic_size});

$config{core}{lsu_num_nbload_width} = log2($config{core}{lsu_num_nbload});

$config{bus}{lsu_bus_tag} = log2($config{core}{lsu_num_nbload}) + 1;

$config{dccm}{dccm_sadr} = (hex($config{dccm}{dccm_region})<<28) +
    (hex($config{dccm}{dccm_offset}));
$config{dccm}{dccm_sadr} = sprintf("0x%x", $config{dccm}{dccm_sadr});

$config{dccm}{dccm_eadr} = (hex($config{dccm}{dccm_region})<<28) +
    (hex($config{dccm}{dccm_offset})) + size($config{dccm}{dccm_size})-1;
$config{dccm}{dccm_eadr} = sprintf("0x%x", $config{dccm}{dccm_eadr});

$config{dccm}{dccm_reserved} = sprintf("0x%x", ($config{dccm}{dccm_size}>30)? 4096 : ($config{dccm}{dccm_size}*1024)/4);

$config{dccm}{dccm_bits} =  ($config{dccm}{dccm_size}==48 ) ? 16 : 10 + log2($config{dccm}{dccm_size});

$config{dccm}{dccm_bank_bits} = log2($config{dccm}{dccm_num_banks});
$config{dccm}{dccm_data_width} = 32;
$config{dccm}{dccm_fdata_width} = $config{dccm}{dccm_data_width} + log2($config{dccm}{dccm_data_width}) + 2;
$config{dccm}{dccm_byte_width} = $config{dccm}{dccm_data_width}/8;

$config{dccm}{dccm_width_bits} = log2($config{dccm}{dccm_byte_width});
$config{dccm}{dccm_index_bits} = $config{dccm}{dccm_bits} - $config{dccm}{dccm_bank_bits} - $config{dccm}{dccm_width_bits};

$config{dccm}{dccm_ecc_width} = log2($config{dccm}{dccm_data_width}) + 2;
$config{dccm}{lsu_sb_bits}    = (($config{dccm}{dccm_bits}) > ($config{pic}{pic_bits})) ? ($config{dccm}{dccm_bits}) : ($config{pic}{pic_bits});
$config{dccm}{dccm_rows}      = ($config{dccm}{dccm_size}==48 ) ? (2**($config{dccm}{dccm_index_bits}-1) +  2**$config{dccm}{dccm_index_bits})/2   : 2**$config{dccm}{dccm_index_bits};
$config{dccm}{dccm_data_cell} = "ram_$config{dccm}{dccm_rows}x39";


$config{icache}{icache_tag_high} = (($config{icache}{icache_size}==256) ? 16 :
                                    ($config{icache}{icache_size}==128) ? 15 :
                                    ($config{icache}{icache_size}==64)  ? 14 :
                                    ($config{icache}{icache_size}==32)  ? 13 : 12);

$config{icache}{icache_tag_depth} = (($config{icache}{icache_size}==256) ? 1024 :
                                    ($config{icache}{icache_size}==128) ? 512 :
                                    ($config{icache}{icache_size}==64)  ? 256 :
                                    ($config{icache}{icache_size}==32)  ? 128 : 64);


$config{icache}{icache_ic_depth} =  log2($config{icache}{icache_size}) + 4;
$config{icache}{icache_ic_index} =  log2($config{icache}{icache_size}) + 4;
$config{icache}{icache_ic_rows}  =  2**$config{icache}{icache_ic_depth};


$config{icache}{icache_taddr_high} = log2($config{icache}{icache_tag_depth}) - 1;

if (defined($config{icache}{icache_ecc})) {
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_ic_rows}x42";
$config{icache}{icache_tag_cell}    = "ram_$config{icache}{icache_tag_depth}x25";

}
else {
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_ic_rows}x34";
$config{icache}{icache_tag_cell}    = "ram_$config{icache}{icache_tag_depth}x21";
}
$config{pic}{pic_total_int_plus1} = $config{pic}{pic_total_int} + 1;
# Defines with explicit values in the macro name
$config{dccm}{"dccm_num_banks_$config{dccm}{dccm_num_banks}"} = "";
$config{dccm}{"dccm_size_$config{dccm}{dccm_size}"} = "";

# If ICCM offset not explicitly provided, align to TOP of the region
if ($top_align_iccm && ($config{iccm}{iccm_offset} eq $iccm_offset) && ($config{iccm}{iccm_size} < 32)) {
    $config{iccm}{iccm_region} = "0xa";
    print "$self: Setting default iccm region to region $config{iccm}{iccm_region}\n";
    $config{iccm}{iccm_offset} = sprintf("0x%08x",256*1024*1024-size($config{iccm}{iccm_size}));
    print "$self: Aligning default iccm offset to top of region @ $config{iccm}{iccm_offset}\n";
}
$config{iccm}{iccm_sadr} = (hex($config{iccm}{iccm_region})<<28) +
                           (hex($config{iccm}{iccm_offset}));
$config{iccm}{iccm_sadr} = sprintf("0x%08x", $config{iccm}{iccm_sadr});

$config{iccm}{iccm_eadr} = (hex($config{iccm}{iccm_region})<<28) +
                           (hex($config{iccm}{iccm_offset})) + size($config{iccm}{iccm_size})-1;
$config{iccm}{iccm_eadr} = sprintf("0x%08x", $config{iccm}{iccm_eadr});

$config{iccm}{iccm_reserved} = sprintf("0x%x", ($config{iccm}{iccm_size}>30)? 4096 : ($config{iccm}{iccm_size}*1024)/4);

$config{iccm}{iccm_bits}      = 10 + log2($config{iccm}{iccm_size});
$config{iccm}{iccm_bank_bits} = log2($config{iccm}{iccm_num_banks});  //-1;
$config{iccm}{iccm_index_bits} = $config{iccm}{iccm_bits} - $config{iccm}{iccm_bank_bits} - 2;   # always 4 bytes
$config{iccm}{iccm_rows}      = 2**$config{iccm}{iccm_index_bits};
$config{iccm}{iccm_data_cell} = "ram_$config{iccm}{iccm_rows}x39";
# Defines with explicit values in the macro name
$config{iccm}{"iccm_num_banks_$config{iccm}{iccm_num_banks}"} = "";
$config{iccm}{"iccm_size_$config{iccm}{iccm_size}"} = "";

delete $config{core}{fpga_optimize} if ($config{core}{fpga_optimize} == 0);

# Track used regions

$regions_used{hex($config{iccm}{iccm_region})} = 1;
$regions_used{hex($config{dccm}{dccm_region})} = 1;
$regions_used{hex($config{pic}{pic_region})} = 1;
$regions_used{hex($config{reset_vec})>>28} = 1;

# Find an unused region for serial IO
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{serialio} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}

$config{memmap}{serialio} = sprintf("0x%08x", $config{memmap}{serialio});

# Find an unused region for external data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{external_data} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_data} = sprintf("0x%08x", $config{memmap}{external_data});
#
# Find an unused region for external prog
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{external_prog} = ($rgn << 28);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_prog} = sprintf("0x%08x", $config{memmap}{external_prog});

# Unused region for second data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{memmap}{external_prog})>>28)  &&
        ($rgn != (hex($config{pic}{pic_region})))
        )) {
        $config{memmap}{external_data_1} = ($rgn << 28);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{external_data_1} = sprintf("0x%08x", $config{memmap}{data_1});


#$config{memmap}{consoleio} = hex($config{memmap}{serialio}) + 0x100;
#$config{memmap}{consoleio} = sprintf("0x%x", $config{memmap}{consoleio});

# Find an unused region for debug_sb_memory data
for (my $rgn = 15;$rgn >= 0; $rgn--) {
    if (($rgn != hex($config{iccm}{iccm_region})) &&
        ($rgn != hex($config{dccm}{dccm_region})) &&
        ($rgn != (hex($config{memmap}{serialio})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data})>>28)) &&
        ($rgn != (hex($config{memmap}{external_data_1})>>28)) &&
        ($rgn != (hex($config{pic}{pic_region})))) {
        $config{memmap}{debug_sb_mem} = ($rgn << 28) + (22<<18);
        $regions_used{$rgn} = 1;
        last;
    }
}
$config{memmap}{debug_sb_mem} = sprintf("0x%08x", $config{memmap}{debug_sb_mem});


# Create the memory map hole for random testing
# Only do this if masks are not enabled already
if (hex($config{protection}{data_access_enable0}) > 0 ||
    hex($config{protection}{data_access_enable1}) > 0 ||
    hex($config{protection}{data_access_enable2}) > 0 ||
    hex($config{protection}{data_access_enable3}) > 0 ||
    hex($config{protection}{data_access_enable4}) > 0 ||
    hex($config{protection}{data_access_enable5}) > 0 ||
    hex($config{protection}{data_access_enable6}) > 0 ||
    hex($config{protection}{data_access_enable7}) > 0 ||
    hex($config{protection}{inst_access_enable0}) > 0 ||
    hex($config{protection}{inst_access_enable1}) > 0 ||
    hex($config{protection}{inst_access_enable2}) > 0 ||
    hex($config{protection}{inst_access_enable3}) > 0 ||
    hex($config{protection}{inst_access_enable4}) > 0 ||
    hex($config{protection}{inst_access_enable5}) > 0 ||
    hex($config{protection}{inst_access_enable6}) > 0 ||
    hex($config{protection}{inst_access_enable7}) > 0 ||
    $config{memmap}{external_mem_hole} eq "default disabled"){
    delete($config{memmap}{external_mem_hole}) ;
} else {
    # Unused region to create a memory map hole, if not already specified
    if ($config{memmap}{external_mem_hole} eq "derived, overridable") {
        for (my $rgn = 15;$rgn >= 0; $rgn--) {
            if (!defined($regions_used{$rgn})) {
                $config{memmap}{external_mem_hole} = ($rgn << 28);
                $regions_used{$rgn} = 1;
                last;
            }
        }
    } else {
        my $rgn = hex($config{memmap}{external_mem_hole})>>28;
        $config{memmap}{external_mem_hole} = ($rgn << 28);
        $regions_used{$rgn} =1;
    }
    my $hreg = $config{memmap}{external_mem_hole}>>28;
    $config{protection}{data_access_addr0} = sprintf("0x%x", (($hreg^8)&8)<<28);
    $config{protection}{data_access_mask0} = "0x7fffffff";
    $config{protection}{data_access_addr1} = sprintf("0x%x", ($hreg&8) << 28 |(($hreg^4)&4)<<28);
    $config{protection}{data_access_mask1} = "0x3fffffff";
    $config{protection}{data_access_addr2} = sprintf("0x%x", ($hreg&12) <<28 | (($hreg^2)&2) <<28);
    $config{protection}{data_access_mask2} = "0x1fffffff";
    $config{protection}{data_access_addr3} = sprintf("0x%x", ($hreg&14) << 28 |(($hreg^1)&1)<<28);
    $config{protection}{data_access_mask3} = "0x0fffffff";
    $config{protection}{data_access_enable0} = "1";
    $config{protection}{data_access_enable1} = "1";
    $config{protection}{data_access_enable2} = "1";
    $config{protection}{data_access_enable3} = "1";
    $config{protection}{inst_access_addr0} = sprintf("0x%x", (($hreg^8)&8)<<28);
    $config{protection}{inst_access_mask0} = "0x7fffffff";
    $config{protection}{inst_access_addr1} = sprintf("0x%x", ($hreg&8) << 28 |(($hreg^4)&4)<<28);
    $config{protection}{inst_access_mask1} = "0x3fffffff";
    $config{protection}{inst_access_addr2} = sprintf("0x%x", ($hreg&12) <<28 | (($hreg^2)&2) <<28);
    $config{protection}{inst_access_mask2} = "0x1fffffff";
    $config{protection}{inst_access_addr3} = sprintf("0x%x", ($hreg&14) << 28 |(($hreg^1)&1)<<28);
    $config{protection}{inst_access_mask3} = "0x0fffffff";
    $config{protection}{inst_access_enable0} = "1";
    $config{protection}{inst_access_enable1} = "1";
    $config{protection}{inst_access_enable2} = "1";
    $config{protection}{inst_access_enable3} = "1";

    $config{memmap}{external_mem_hole} = sprintf("0x%08x", $config{memmap}{external_mem_hole});
}


#Define 5 unused regions for used in TG

foreach my $unr (reverse(0 .. 15)) {
    if (!defined($regions_used{$unr})) {
        $config{memmap}{"unused_region$unr"} = sprintf("0x%08x",($unr << 28));
        $regions_used{$unr} = 1;
    }
}

if ($target eq "baseline") {
    $config{reset_vec} = $config{iccm}{iccm_sadr};
    $config{testbench}{magellan} = 1;
    print "$self: Setting reset_vec = ICCM start address for Baseline\n";
}


# Output bit-width specifiers for these variables
our %widths = (
        "dccm_region"   => "4",
        "dccm_offset"   => "28",
        "dccm_sadr"     => "32",
        "dccm_eadr"     => "32",
        "pic_region"    => "4",
        "pic_offset"    => "10",
        "pic_base_addr" => "32",
        "iccm_region"   => "4",
        "iccm_offset"   => "10",
        "iccm_sadr"     => "32",
        "iccm_eadr"     => "32",
        "bus_prty_default" => "2",
        "inst_access_enable0" => "1",
        "inst_access_enable1" => "1",
        "inst_access_enable2" => "1",
        "inst_access_enable3" => "1",
        "inst_access_enable4" => "1",
        "inst_access_enable5" => "1",
        "inst_access_enable6" => "1",
        "inst_access_enable7" => "1",
        "data_access_enable0" => "1",
        "data_access_enable1" => "1",
        "data_access_enable2" => "1",
        "data_access_enable3" => "1",
        "data_access_enable4" => "1",
        "data_access_enable5" => "1",
        "data_access_enable6" => "1",
        "data_access_enable7" => "1",
);
#}}}


#-----------------------Reset Vector MPU check-----------------------#
$flag_pass=0;
if(!(hex($config{reset_vec}) >= ((hex($config{iccm}{iccm_region})<<28) + (hex($config{iccm}{iccm_offset}))) && (hex($config{reset_vec}) < ((hex($config{iccm}{iccm_region})<<28) + (hex($config{iccm}{iccm_offset})) + size($config{iccm}{iccm_size})-1))))
{
    for(my $i=0; $i<8; $i++)
    {
        $inst_access_enable = "inst_access_enable$i";
        $inst_access_addr = "inst_access_addr$i";
        $inst_access_mask = "inst_access_mask$i";
        if(hex($config{protection}{$inst_access_enable}))
        {
                if((hex($config{reset_vec})>= hex($config{protection}{$inst_access_addr})) && (hex($config{reset_vec})< (hex($config{protection}{$inst_access_addr}) | hex($config{protection}{$inst_access_mask}))))
                {
                        $flag_pass++;
                        last;
                }
        }
        else    {$enable_check++;}
    }

if($flag_pass == 0 & $enable_check < 8)  {      die("$helpusage\n\nFAIL: RESET_VECTOR not in any of MPU enabled instruction access windows or ICCM !!!\n\n");}
}
#-----------------------Reset Vector MPU check-----------------------#

#print Dumper(\%config);
#print Dumper(\%width);

#print Dumper(\%sets);
#print Dumper(\%unsets);

# Sanity checks
check_addr_align("dccm", hex($config{dccm}{dccm_sadr}), $config{dccm}{dccm_size}*1024);
check_addr_align("iccm", hex($config{iccm}{iccm_sadr}), $config{iccm}{iccm_size}*1024);
check_addr_align("pic", hex($config{pic}{pic_base_addr}), $config{pic}{pic_size}*1024);

#   Prevent overlap of internal memories
if ((hex($config{pic}{pic_region}) == hex($config{iccm}{iccm_region})) && (hex($config{pic}{pic_offset}) == hex($config{iccm}{iccm_offset}))) {
    die "$self: ERROR! PIC and ICCM blocks collide (region $config{iccm}{iccm_region}, offset $config{pic}{pic_offset})!\n";
}
if ((hex($config{pic}{pic_region}) == hex($config{dccm}{dccm_region})) && (hex($config{pic}{pic_offset}) == hex($config{dccm}{dccm_offset}))) {
    die "$self: ERROR! PIC and DCCM blocks collide (region $config{dccm}{dccm_region}, offset $config{pic}{pic_offset})!\n";
}
if ((hex($config{iccm}{iccm_region}) == hex($config{dccm}{dccm_region})) && (hex($config{iccm}{iccm_offset}) == hex($config{dccm}{dccm_offset}))) {
    die "$self: ERROR! ICCM and DCCM blocks collide (region $config{iccm}{iccm_region}, offset $config{dccm}{dccm_offset})!\n";
}

##################### Add dumper routines here ##########################
#
# Dump Verilog $RV_ROOT/configs/common_defines.vh
print "$self: Writing $vlogfile\n";
open (FILE, ">$vlogfile") || die "Cannot open $vlogfile for writing $!\n";
print_header("//");
gen_define("","`", \%config, \@verilog_vars);
close FILE;

print "$self: Writing $asmfile\n";
open (FILE, ">$asmfile") || die "Cannot open $asmfile for writing $!\n";
# Dump ASM/C   $RV_ROOT/diags/env/defines.h
print_header("//");
gen_define("","#", \%config, \@asm_vars, \@asm_overridable);
close FILE;

# add `define PHYSICAL 1
# remove `undef RV_ICCM_ENABLE

my $pddata='
`include "common_defines.vh"
`undef ASSERT_ON
`undef TEC_RV_ICG
`define TEC_RV_ICG CKLNQD12BWP35P140
`define PHYSICAL 1
';


print "$self: Writing $pdfile\n";
open (FILE, ">$pdfile") || die "Cannot open $pdfile for writing $!\n";
# Dump PD   $RV_ROOT/$RV_ROOT/configs/pd_defines.vh
print_header("//");
printf (FILE "$pddata");
close FILE;

print "$self: Writing $whisperfile\n";
dump_whisper_config(\%config, $whisperfile);


# change this to use config version
#`$ENV{RV_ROOT}/tools/picmap -t $config{pic}{pic_total_int} > $ENV{RV_ROOT}/design/include/pic_map_auto.h`;
`$ENV{RV_ROOT}/tools/picmap -t $config{pic}{pic_total_int} > $build_path/pic_map_auto.h`;
#`$ENV{RV_ROOT}/tools/unrollforverilator  $config{pic}{pic_total_int_plus1} > $ENV{RV_ROOT}/design/include/pic_ctrl_verilator_unroll.sv`;
`$ENV{RV_ROOT}/tools/unrollforverilator  $config{pic}{pic_total_int_plus1} > $build_path/pic_ctrl_verilator_unroll.sv`;

# Perl vars for use by scripts
print "$self: Writing $perlfile\n";
open (FILE, ">$perlfile") || die "Cannot open $perlfile for writing $!\n";
print_header("# ");
print FILE "# To use this in a perf script, use 'require \$RV_ROOT/configs/config.pl'\n";
print FILE "# Reference the hash via \$config{name}..\n\n\n";
print FILE Data::Dumper->Dump([\%config], [ qw(*config) ]);
print FILE "1;\n";
close FILE;

# Default linker script
gen_default_linker_script();
# Done ##################################################################
#
exit(0);

# ######################   Helper subroutines ##########################{{{
# Convert size in kilobytes to real value

sub size {#{{{
    my $ksize = shift;
    my $size = sprintf("%d",$ksize*1024);
    return $size;
}#}}}

# Print the defines with prefix
sub print_define {#{{{
    my ($sym, $key,$value, $override) = @_;
    my $lprefix = $prefix if ($key !~ /$no_prefix/);
    if ($sym eq "`") {
        if (defined($widths{$key})) {
            $value =~ s/^(0x)*/$widths{$key}'h/;
        } else {
            $value =~ s/^0x/'h/;
        }
    }
    if ($defines_case eq "U") {
        print FILE "${sym}ifndef \U$lprefix$key\E\n" if ($override);
        print FILE "${sym}define \U$lprefix$key\E $value\n";
        print FILE "${sym}endif\n" if ($override);
    } else {
        print FILE "${sym}ifndef $lprefix$key\n" if ($override);
        print FILE "${sym}define $lprefix$key $value\n";
        print FILE "${sym}endif\n" if ($override);
    }
}#}}}

# print header
sub print_header {#{{{
    my $cs = shift;
    print FILE "$cs NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE\n";
    print FILE "$cs This is an automatically generated file by $ENV{USER} on ",`date`;
    print FILE "$cs\n$cs cmd:    $self @argv_orig \n";
    print FILE "$cs\n";
}#}}}

# evaluate derivations
sub derive {#{{{
    my $eqn = shift;
    return sprintf("0x%x", eval($eqn));
}#}}}

# traverse the database and extract the key/value pair
sub gen_define {#{{{
    my $matched = shift;
    my $prefix = shift;
    my $hash = @_[0];
    my @printvars = @{@_[1]};
    my @overridable = @{@_[2]};
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (keys %$hash) {
        next if $key eq "csr";
        #print "looking at $key:$matched ($re)\n";
        if (defined($unsets{$key})) {
            print "$self:unsetting $key\n";
            delete($config{$key});
            next
        }
        if (defined($sets{$key}) && $sets{$key} ne $$hash{$key}) {
            if (($$hash{$key} =~ /derived/i) && ($$hash{$key} !~ /overridable/i)) {
                die ("$self: ERROR! $key is a derived and non-overridable parameter!\n");
            } else {
                print "$self: Overriding $key value $$hash{$key} with $sets{$key}\n";
                $$hash{$key} = $sets{$key};
            }
        }
        my $value = $$hash{$key};
        if (ref($value) eq "HASH") {
            if ($key =~ /$re/) {
                $matched = 1;
            }
            gen_define($matched,$prefix, $value, \@printvars, \@overridable);
            $matched = 0;
        } elsif (ref($value) eq "ARRAY") {
            # print "$key : @{$value}\n";
             $matched = 0;
        } else {
            if ($matched eq "1" || $key =~ /$re/) {
                if($value =~ /derive\(.*\)/o) {
                    $value = eval($value);
                }
                $override = grep(/^$key$/, @overridable);
                    print_define($prefix, $key, $value, $override);
            }
        }
    }
}#}}}

sub dump_define {#{{{
    my $matched = shift;
    my $prefix = shift;
    my $hash = @_[0];
    my @printvars = @{@_[1]};
    my @overridable = @{@_[2]};
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (keys %$hash) {
        next if $key eq "csr";
        next unless $matched || grep(/^$key$/,@dvars);
        #print "looking at $key:$matched ($re)\n";
        if (defined($unsets{$key})) {
            print "$self:unsetting $key\n";
            delete($config{$key});
            next
        }
        if (defined($sets{$key}) && $sets{$key} ne $$hash{$key}) {
            if (($$hash{$key} =~ /derived/i) && ($$hash{$key} !~ /overridable/i)) {
                die ("$self: ERROR! $key is a derived and non-overridable parameter!\n");
            } else {
                print "$self: Overriding $key value $$hash{$key} with $sets{$key}\n";
                $$hash{$key} = $sets{$key};
            }
        }
        my $value = $$hash{$key};
        if (ref($value) eq "HASH") {
            if ($key =~ /$re/) {
                $matched = 1;
            }
            dump_define($matched,$prefix, $value, \@printvars, \@overridable);
            $matched = 0;
        } elsif (ref($value) eq "ARRAY") {
            # print "$key : @{$value}\n";
             $matched = 0;
        } else {
            if ($matched eq "1" || $key =~ /$re/) {
                if($value =~ /derive\(.*\)/o) {
                    $value = eval($value);
                }
                printf ("veer: %-30s = $value\n",$key) if ($value !~ /derived/);
            }
        }
    }
}#}}}

# Perform cmd line set/unset ############################################{{{
sub map_set_unset {
    if (scalar(@sets)) {
        print "$self: Set(s) requested : @sets\n";
        foreach (@sets) {
            my ($key,$value) = m/(\w+)=*(\w+)*/o;
            $value = 1 if (!defined($value));
            $sets{$key} = $value;
        }
    }
    if (scalar(@unsets)) {
        print "$self: Unset(s) requested : @sets\n";
        foreach (@unsets) {
            $unsets{$_} = 1;
        }
    }
} #}}}
#}}}


# If arg looks like a hexadecimal string, then convert it to decimal.#{{{
# Otherwise, return arg.
sub decimal {
    my ($x) = @_;
    return hex($x)  if $x =~ /^0x/o;
    return $x;
}#}}}

# Collect memory protection specs (array of address pairs) in the given
# resutls array. Tag is either "data" or "inst".
sub collect_mem_protection {
    my ($tag, $config, $results) = @_;
    return unless exists $config{protection};

    my $prot = $config{protection};

    my $enable_tag = $tag . "_access_enable";
    my $addr_tag = $tag . "_access_addr";
    my $mask_tag = $tag . "_access_mask";

    foreach my $key (keys %{$prot}) {
        next unless $key =~ /^$enable_tag(\d+)$/;
        my $ix = $1;

        my $enable = $prot->{$key};
        if ($enable !~ /[01]$/) {
            warn("Invalid value for protection entry $key: $enable\n");
            next;
        }

        next unless ($enable eq "1" or $enable eq "1'b1");

        if (! exists $prot->{"$addr_tag$ix"}) {
            warn("Missing $addr_tag$ix\n");
            next;
        }

        if (! exists $prot->{"$mask_tag$ix"}) {
            warn("Missing $mask_tag$ix\n");
            next;
        }

        my $addr = $prot->{"$addr_tag$ix"};
        my $mask = $prot->{"$mask_tag$ix"};

        if ($addr !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $addr_tag$ix: $addr\n");
            next;
        }

        if ($mask !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $mask_tag$ix: $mask\n");
            next;
        }

        if ((hex($addr) & hex($mask)) != 0) {
            warn("Protection mask bits overlap address bits in mask $mask and addr $addr\n");
        }

        if ($mask !~ /^0x0*[137]?f*$/) {
            warn("Protection  $tag mask ($mask) must have all its one bits to the right of its zero bits\n");
            next;
        }

        my $start = hex($addr) & ~hex($mask) & 0xffffffff;
        my $end = (hex($addr) | hex($mask)) & 0xffffffff;

        $start = sprintf("0x%08x", $start);
        $end = sprintf("0x%08x", $end);

        push(@{$results}, [ $start, $end ]);
    }
}

# Collect memory protection specs (array of address pairs) in the given
# resutls array. Tag is either "data" or "inst".
sub collect_mem_protection {
    my ($tag, $config, $results) = @_;
    return unless exists $config{protection};

    my $prot = $config{protection};

    my $enable_tag = $tag . "_access_enable";
    my $addr_tag = $tag . "_access_addr";
    my $mask_tag = $tag . "_access_mask";

    foreach my $key (keys %{$prot}) {
        next unless $key =~ /^$enable_tag(\d+)$/;
        my $ix = $1;

        my $enable = $prot->{$key};
        if ($enable !~ /[01]$/) {
            warn("Invalid value for protection entry $key: $enable\n");
            next;
        }

        next unless ($enable eq "1" or $enable eq "1'b1");

        if (! exists $prot->{"$addr_tag$ix"}) {
            warn("Missing $addr_tag$ix\n");
            next;
        }

        if (! exists $prot->{"$mask_tag$ix"}) {
            warn("Missing $mask_tag$ix\n");
            next;
        }

        my $addr = $prot->{"$addr_tag$ix"};
        my $mask = $prot->{"$mask_tag$ix"};

        if ($addr !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $addr_tag$ix: $addr\n");
            next;
        }

        if ($mask !~ /^0x[0-9a-fA-F]+$/) {
            warn("Invalid $mask_tag$ix: $mask\n");
            next;
        }

        if ((hex($addr) & hex($mask)) != 0) {
            warn("Protection mask bits overlap address bits in mask $mask and addr $addr\n");
        }

        if ($mask !~ /^0x0*[137]?f*$/) {
            warn("Protection mask ($mask) must have all its one bits to the right of its zero bits\n");
            next;
        }

        my $start = hex($addr) & ~hex($mask) & 0xffffffff;
        my $end = (hex($addr) | hex($mask)) & 0xffffffff;

        $start = sprintf("0x%08x", $start);
        $end = sprintf("0x%08x", $end);

        push(@{$results}, [ $start, $end ]);
    }
}

# Collect the memory mapped registers associated with the pic (platform
# interrup controller) to include in the whisper.json file.
sub collect_mem_mapped_regs {
    my ($pic, $results) = @_;
    my $default_mask = 0;
    $results->{default_mask} = $default_mask;
    my $addr = hex($pic->{pic_region})*256*1024*1024 + hex($pic->{pic_offset});
    $results->{address} = sprintf("0x%x", $addr);
    $results->{size} = sprintf("0x%x", $pic->{pic_size}*1024);

    my @names = qw ( mpiccfg meipl meip meie meigwctrl meigwclr meidels );
    $results->{registers} = {};
    foreach my $name (@names) {
        my $tag = "pic_${name}_offset";
        next unless exists $pic->{$tag};
        my %item;
        my $offset = hex($pic->{$tag});
        $offset += 4  if ($name ne 'mpiccfg' and $name ne 'meip');
        $item{address} = sprintf("0x%x", $addr + $offset);
        $item{mask} = $pic->{"pic_${name}_mask"};
        $item{count} = $pic->{"pic_${name}_count"};
        $results->{registers}{$name} = \%item;
    }
}

sub dump_whisper_config{#{{{
    my ($config, $path) = @_;

    open(my $fh, ">", "$path") or die ("Failed to open $path for writing: $!\n");

    # Put the configuration parameters relevant to whisper into a hash
    # in preparation for a JSON dump.
    my %jh; # Json hash

    # Collect top-level integer entries.
    foreach my $tag (qw( harts xlen )) {
        $jh{$tag} = $config{$tag} + 0 if exists $config{$tag};
    }

    # Collect top-level string/hex entries.
    foreach my $tag (qw ( reset_vec nmi_vec num_mmode_perf_regs max_mmode_perf_event
                     even_odd_trigger_chains)) {
        $jh{$tag} = $config{$tag} if exists $config{$tag};
    }

    # Collect memory map configs.
    my (@inst_mem_prot, @data_mem_prot);
    collect_mem_protection("inst", $config, \@inst_mem_prot);
    collect_mem_protection("data", $config, \@data_mem_prot);
    $jh{memmap}{inst} = [@inst_mem_prot] if @inst_mem_prot;
    $jh{memmap}{data} = [@data_mem_prot] if @data_mem_prot;
    $config{memmap}{consoleio} = $config{memmap}{serialio} if exists $config{memmap}{serialio};
    foreach my $tag (qw ( size page_size serialio consoleio)) {
        $jh{memmap}{$tag} = $config{memmap}{$tag} if exists $config{memmap}{$tag};
    }

    # Collect load/store-error rollback parameter.
    if (exists $config{testbench} and exists $config{testbench}{sterr_rollback}) {
        $jh{store_error_rollback} = $config{testbench}{sterr_rollback};
    }
    if (exists $config{testbench} and exists $config{testbench}{lderr_rollback}) {
        $jh{load_error_rollback} = $config{testbench}{lderr_rollback};
    }

    # Collect dccm configs
    if (exists $config{dccm} and exists $config{dccm}{dccm_enable}) {
        $jh{dccm}{region} = $config{dccm}{dccm_region};
        $jh{dccm}{size} = 1024*decimal($config{dccm}{dccm_size}); # From 1k to bytes
        $jh{dccm}{offset} = $config{dccm}{dccm_offset};

        $jh{dccm}{size} = sprintf("0x%x", $jh{dccm}{size});
    }

    # Collect icccm configs.
    if (exists $config{iccm} and exists $config{iccm}{iccm_enable}) {
        $jh{iccm}{region} = $config{iccm}{iccm_region};
        $jh{iccm}{size} = 1024*decimal($config{iccm}{iccm_size}); # From 1k to bytes
        $jh{iccm}{offset} = $config{iccm}{iccm_offset};

        $jh{iccm}{size} = sprintf("0x%x", $jh{iccm}{size});
    }

    # Collect CSRs

    $jh{csr} = $config{csr} if exists $config{csr};

    # Collect pic configs.
    if (exists $config{pic}) {
        my %mem_mapped;
        collect_mem_mapped_regs($config{pic}, \%mem_mapped);
        $jh{'memory_mapped_registers'} = \%mem_mapped;

        # This is now deprecated. To be removed soon.
        while (my ($k, $v) = each %{$config{pic}}) {
            next if $k eq 'pic_base_addr';  # derived from region and offset
            if ($k eq 'pic_size') {
                $v *= 1024;  # from kbytes to bytes
                $v = sprintf("0x%x", $v);
            }
            $k =~ s/^pic_//o;
            $v += 0 if $v =~ /^\d+$/o;
            $jh{pic}{$k} = $v;
        }
    }

    # Collect triggers.
    $jh{triggers} = $config{triggers} if exists $config{triggers};

    # Dump JSON config file.
    my $json = JSON->new->allow_nonref;
    my $text = $json->pretty->encode(\%jh);
    print($fh $text);

    close $fh;
}#}}}


# Checker for iccm/dccm/pic sub-region address alignment. Address must be a multiple
# of size  or next higher power of 2 if size is not a power of 2.
sub check_addr_align {
    my ($section, $addr, $size) = @_;

    die("Invalid $section size: $size\n")  if $size <= 0;

    my $log_size = log2($size);
    my $p2 = 1 << $log_size;
    $size = 2*$p2  if $size != $p2;

    if (($addr % $size) != 0) {
        printf("Address of $section area(0x%x) is not a multiple of its size (0x%x)\n",
               $addr, $size);
        exit(1);
    }
}


sub log2 {
    my ($n) = @_;
    return log($n)/log(2);
}

sub gen_default_linker_script {#{{{

    open (FILE, ">$linkerfile") || die "Cannot open $linkerfile for writing $!";
    print "$self: Writing $linkerfile\n";
    print FILE "/*\n";
    print_header();

    my $io = "0xd0580000";
    $io = $config{memmap}{serialio} if exists $config{memmap}{serialio};

    my $iccm = ""; my $iccm_ctl = "";
    if (exists $config{iccm} and $config{iccm}{iccm_enable}) {
        my $sa = $config{iccm}{iccm_sadr}; my $ea = $config{iccm}{iccm_eadr};
        $iccm = " . = $sa ;";
        $iccm_ctl = "  . = 0xfffffff0; .iccm.ctl . : { LONG($sa); LONG($ea) }" ;
    }

    my $sa = $config{memmap}{external_data}; my $dccm_ctl = "";
    if (exists $config{dccm} and $config{dccm}{dccm_enable}) {
        $sa = $config{dccm}{dccm_sadr};
        $dccm_ctl = "  . = 0xfffffff8; .data.ctl : { LONG($sa); LONG(STACK) }" ;
    }
    my $data_loc = "  . = $sa ;";

    print FILE  <<EOF;
*/

OUTPUT_ARCH( "riscv" )
ENTRY(_start)

SECTIONS
{
  . = $config{reset_vec};
  .text.init .  : { *(.text.init) }
  $iccm
  .text . : { *(.text) }
  _end = .;
  . = $io;
  .data.io .  : { *(.data.io) }
  $data_loc
  .data  :  ALIGN(0x800) { *(.*data) *(.rodata*)}
  .bss :  {BSS_START = .; *(.*bss)}
  BSS_END = .;
  STACK = ALIGN(16) + 0x1000;
  $iccm_ctl
  $dccm_ctl
}

EOF
    close FILE;
} #}}}

