#! /usr/bin/env perl

use strict;   # Do not turn this off or else
use Data::Dumper;
use Getopt::Long;
use Bit::Vector;
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 config_key reset_vec tec_rv_icg numiregs nmi_vec target protection.* testbench.* dccm.* retstack core.* iccm.* btb.* bht.* icache.* pic.* regwidth memmap bus.* tech_specific_.* user_.*);

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

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

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

# Prefix all macros with
my $prefix = "RV_";
# No prefix if keyword has
my $no_prefix = 'RV|TOP|tec_rv_icg|regwidth|clock_period|^datawidth|verilator|SDVT_AHB|tech_specific_.*|user_ec_rv_icg';

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_ prefix 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

It is run by vsim (with defaults) every time the file changes, or when -config_set=VAR=value options are passed to vsim

This script can be run stand-alone by processes not running vsim

User options:

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

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

Parameters that can be set by the end user:

     -set=user_mode = {0,1}
          enable, disable user mode support in the core
     -set=ret_stack_size =      {2, 3, 4, ... 8}
          size of return stack
     -set=btb_enable = {0,1}
          BTB enabled
     -set=btb_fullya = {0,1}
          BTB Fully set-associative
     -set=btb_size =      { 8, 16, 32, 64, 128, 256, 512 }
          size of branch target buffer
     -set=bht_size =  {32, 64, 128, 256, 512, 1024, 2048}
          size of branch history buffer
     -set=div_bit = {1,2,3,4}
          number of bits to process each cycle
     -set=div_new = {0,1}
          new div algorithm
     -set=dccm_enable = {0,1}
          DCCM enabled
     -set=dccm_num_banks = {2, 4}
          DCCM number of banks
     -set=dccm_region =   { 0x0, 0x1, ... 0xf }
          number of 256Mb memory region containing DCCM
     -set=dccm_offset =   hexadecimal
          offset (in bytes) of DCCM within 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
     -set=dccm_size   =   { 4, 8, 16, 32, 48, 64, 128, 256, 512 } kB
          size of DCCM
     -set=dccm_addr_xor = { 0, 1 }
          FI hardening: XOR the (replicated) DCCM word address into the stored data so a
          misdirected access is caught by ECC (requires lockstep_enable/DCLS)
     -set=dccm_wr_readback = { 0, 1 }
          FI hardening: after every core-store write commits to the DCCM, stall and read the
          same word back to compare against the data written.
     -set=dma_buf_depth = {2,4,5}
          DMA buffer depth
     -set=fast_interrupt_redirect =  {0, 1}
          Fast interrupt redirect mechanism
     -set=iccm_enable =   { 0, 1 }
          whether or not ICCM is enabled
     -set=iccm_addr_xor = { 0, 1 }
          FI hardening: XOR the (replicated) ICCM word address into the stored data so a
          misdirected access is caught by ECC and a garbled instruction
          (requires iccm_enable and lockstep_enable/DCLS)
     -set=icache_enable = { 0, 1 }
          whether or not icache is enabled
     -set=icache_waypack = { 0, 1 }
          whether or not icache packing is enabled
     -set=icache_ecc = { 0, 1 }
          whether or not icache has ecc - EXPENSIVE 30% sram growth
          default: icache_ecc==0 (parity)
     -set=icache_addr_xor = { 0, 1 }
          FI hardening: XOR the (constant) I-cache line address into the stored data so a
          misdirected access is caught by the I-cache parity/ECC check (requires lockstep_enable/DCLS)
     -set=icache_size =   { 8, 16, 32, 64, 128, 256 } kB
          size of icache
     -set=icache_2banks = {0,1}
          Enable 2 banks for icache
     -set=icache_num_ways { 2,4}
          Number of ways in icache
     -set=icache_bypass_enable = {0,1}
          Enable Icache data bypass buffer
     -set=icache_num_bypass = {1..8}
          Number of entries in bypass buffer
     -set=icache_num_tag_bypass = {1..8}
          Number of entries in bypass buffer
     -set=icache_tag_bypass_enable = {0,1}
          Enable icache tag bypass buffer
     -set=iccm_region =   { 0x0, 0x1, ... 0xf  }
          number of 256Mb memory region containing ICCM
     -set=iccm_offset =   hexadecimal
          offset (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
     -set=iccm_size   =   { 4 , 8 , 16 , 32, 64, 128, 256, 512 } kB
          size of ICCM
     -set=iccm_num_banks = {2,4,8,16}
          Number of ICCM banks
     -set=iccm_ecc_width
          Width of ICCM ECC [bits]
     -set=lsu_stbuf_depth = {2,4,8 }
          LSU stbuf depth
     -set=lsu_num_nbload = {2,4,8 }
          LSU number of outstanding Non Blocking loads
     -set=load_to_use_plus1 = {0 1}
          Load to use latency (fast or +1cycle)
     -set=pic_2cycle  =   { 0, 1 }
          whether or not 2-cycle PIC is enabled (2 cycle pic may result
          in an overall smaller cycle time)
     -set=pic_region =    { 0x0, 0x1, ... 0xf  }
           number of 256Mb memory region containing PIC memory-mapped registers
     -set=pic_offset =    hexadecimal
          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
     -set=pic_size   =    { 32, 64, 128, 256 } kB
          size of PIC
     -set=pic_total_int = { 1, 2, 3, ..., 255 }
          number of interrupt sources in PIC
     -set=dma_buf_depth = {2,4,5}
          DMA buffer depth
     -set=timer_legal_en = {0,1}
          Internal timers legal/enabled
     -set=bitmanip_zba = {0,1}
          Bit manipulation extension ZBa enabled/legal
     -set=bitmanip_zbb = {0,1}
          Bit manipulation extension ZBb enabled/legal
     -set=bitmanip_zbc = {0,1}
          Bit manipulation extension ZBc enabled/legal
     -set=bitmanip_zbe = {0,1}
          Bit manipulation extension ZBe enabled/legal
     -set=bitmanip_zbf = {0,1}
          Bit manipulation extension ZBf enabled/legal
     -set=bitmanip_zbp = {0,1}
          Bit manipulation extension ZBp enabled/legal
     -set=bitmanip_zbr = {0,1}
          Bit manipulation extension ZBr enabled/legal
     -set=bitmanip_zbs = {0,1}
          Bit manipulation extension ZBs enabled/legal
     -fpga_optimize =   { 0, 1 }
          if 1, minimize clock-gating to facilitate FPGA builds
     -text_in_iccm = {0, 1}
          Don't add ICCM preload code in generated link.ld
     -set=pmp_entries = {0, 16, 64 }
          number of PMP entries
     -set=smepmp = {0, 1}
          Enable Smepmp PMP extension
     -set=lockstep_enable = {0, 1}
          Enable Dual Core Lockstep (DCLS) in the core
     -set=lockstep_regfile_enable = {0, 1}
          Enable observing register file in the DCLS
     -set=lockstep_regfile_read_enable = {0, 1}
          Compare the register file read output ports (instead of a fixed subset of
          registers) in the DCLS (requires lockstep_regfile_enable)
     -set=lockstep_delay = {0, 1, 2, 3, 4}
          Set delay value for the Dual Core Lockstep
     -set=mubi_width = {2, 3, 4, ..., 32}
          Set fixed width for multibit encoded logic
     -set=mubi_true = {0x0, 0x1, ..., 0xffffffff}
          Set truth multibit encoding value (in place of 1'b1). Must be complementary to `mubi_false`.
     -set=mubi_false = {0x0, 0x1, ..., 0xffffffff}
          Set false multibit encoding value (in place of 1'b0). Must be complementary to `mubi_true`.


Additionally the following may be set for bus masters and slaves using the -set=var=value option:

        {inst|data}_access_enable[0-7] : default 0
        {inst|data}_access_addr[0-7] : default 0x00000000
        {inst|data}_access_mask[0-7] : default 0xffffffff
";

my $user_mode;

my $ret_stack_size;
my $btb_size;
my $bht_size;
my $btb_fullya;
my $btb_toffset_size;
my $dccm_region;
my $dccm_offset;
my $dccm_size;
my $iccm_enable;
my $icache_enable;
my $icache_waypack;
my $icache_addr_xor;
my $icache_num_ways;
my $icache_banks_way;
my $icache_ln_sz;
my $icache_bank_width;
my $icache_ecc;
my $iccm_region;
my $iccm_offset;
my $iccm_size;
my $iccm_addr_xor;
my $icache_size;
my $pic_2cycle;
my $pic_region;
my $pic_offset;
my $pic_size;
my $pic_total_int;

my $top_align_iccm = 0;

my $target = "default";
my $snapshot ;
my $build_path ;
my $verbose;
my $load_to_use_plus1;
my $btb_enable;
my $dccm_enable;
my $dccm_addr_xor;
my $dccm_wr_readback;
my $icache_2banks;
my $lsu_stbuf_depth;
my $dma_buf_depth;
my $lsu_num_nbload;
my $dccm_num_banks;
my $iccm_num_banks;
my $iccm_ecc_width;
my $verilator;
my $icache_bypass_enable=1;
my $icache_num_bypass=2;
my $icache_num_bypass_width;
my $icache_tag_bypass_enable=1;
my $icache_tag_num_bypass=2;
my $icache_tag_num_bypass_width;

my $fast_interrupt_redirect = 1;                        # ON by default
my $lsu_num_nbload=4;
my $ahb = 0;
my $axi = 1;
my $openocd_test = 0;
my $text_in_iccm = 0;

my $lsu2dma = 0;

my $pmp_entries=16;
my $smepmp=0;
my $lockstep_enable=0;
my $lockstep_regfile_enable=0;
my $lockstep_regfile_read_enable=0;
my $lockstep_delay=3;

my $mubi_width=2;
my $mubi_true="0x2";
my $mubi_false="0x1";

$user_mode=0;
$ret_stack_size=8;
$btb_enable=1;
$btb_fullya=0;
$btb_toffset_size=12;
$btb_size=512;
$bht_size=512;
$dccm_enable=1;
$dccm_region="0xf";
$dccm_offset="0x40000"; #1*256*1024
$dccm_size=64;
$dccm_num_banks=4;
$dccm_addr_xor=0;
$dccm_wr_readback=0;
$iccm_enable=1;
$iccm_region="0xe";
$top_align_iccm = 1;
$iccm_offset="0xe000000"; #0x380*256*1024
$iccm_size=64;
$iccm_num_banks=4;
$iccm_addr_xor=0;
$iccm_ecc_width=7;
$icache_enable=1;
$icache_waypack=1;
$icache_addr_xor=0;
$icache_num_ways=2;
$icache_banks_way=2;
$icache_2banks=1;
$icache_bank_width=8;
$icache_ln_sz=64;
$icache_ecc=1;
$icache_size=16;
$pic_2cycle=0;
$pic_region="0xf";
$pic_offset="0xc0000"; # 3*256*1024
$pic_size=32;
$pic_total_int=31;
$load_to_use_plus1=0;
$lsu_stbuf_depth=4;
$dma_buf_depth=5;

my $div_bit=4;       # number of bits to process each cycle for div
my $div_new=1;       # old or new div algorithm

my $fpga_optimize = 1;

# Default bitmanip options
my $bitmanip_zba = 1;
my $bitmanip_zbb = 1;
my $bitmanip_zbc = 1;
my $bitmanip_zbe = 0;
my $bitmanip_zbf = 0;
my $bitmanip_zbp = 0;
my $bitmanip_zbr = 0;
my $bitmanip_zbs = 1;

GetOptions(
    "help"                => \$help,
    "target=s"            => \$target,
    "snapshot=s"          => \$snapshot,
    "verbose"             => \$verbose,
    "load_to_use_plus1"   => \$load_to_use_plus1,
    "ret_stack_size=s"    => \$ret_stack_size,
    "btb_fullya"          => \$btb_fullya,
    "btb_enable=s"        => \$btb_enable,
    "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,
    "dccm_addr_xor=s"     => \$dccm_addr_xor,
    "dccm_wr_readback=s"  => \$dccm_wr_readback,
    "dma_buf_depth"       => \$dma_buf_depth,
    "iccm_enable=s"       => \$iccm_enable,
    "icache_enable=s"     => \$icache_enable,
    "icache_waypack=s"    => \$icache_waypack,
    "icache_addr_xor=s"   => \$icache_addr_xor,
    "icache_num_ways=s"   => \$icache_num_ways,
    "icache_ln_sz=s"      => \$icache_ln_sz,
    "icache_ecc=s"        => \$icache_ecc,
    "icache_2banks=s"     => \$icache_2banks,
    "iccm_region=s"       => \$iccm_region,
    "iccm_offset=s"       => \$iccm_offset,
    "iccm_size=s"         => \$iccm_size,
    "iccm_addr_xor=s"     => \$iccm_addr_xor,
    "lsu_stbuf_depth"     => \$lsu_stbuf_depth,
    "lsu_num_nbload"      => \$lsu_num_nbload,
    "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,
    "set=s@"              => \@sets,
    "unset=s@"            => \@unsets,
    "fpga_optimize=s"     => \$fpga_optimize,
    "text_in_iccm=s"        => \$text_in_iccm,
) || 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");
}

# Parameter file
my $tdfile = "$build_path/el2_pdef.vh";
my $paramfile = "$build_path/el2_param.vh";

# 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 $opensource=0;



# IDEA: is ghr at 5b the right size for el2 core

if ($target eq "default") { }
elsif ($target eq "lsu2dma_axi") {
    $lsu2dma = 1;
    $iccm_enable = 1;
}
elsif ($target eq "typical_pd") {
    print "$self: Using target \"typical_pd\"\n";
    $fpga_optimize = 0;
    $ret_stack_size=2;
    $btb_size=32;
    $bht_size=128;
    $dccm_size=16;
    $dccm_num_banks=2;
    $iccm_enable=0;
}
elsif ($target eq "high_perf") {
    print "$self: Using target \"high_perf\"\n";
    $btb_size=512;
    $bht_size=2048;
}
elsif ($target eq "default_ahb") {
    print "$self: Using target \"default_ahb\"\n";
    $axi = 0;
    $ahb = 1;
}
else {
    die "$self: ERROR! Unsupported target \"$target\". Supported are 'default', 'default_ahb', 'typical_pd', 'high_perf', 'lsu2dma_axi\n" ;
}



# 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",
    },
   "mcountinhibit" => {
       "commnet"       => "Performance counter inhibit. One bit per counter.",
       "reset"         => "0x0",
       "mask"          => "0x7d",
       "poke_mask"          => "0x7d",
       "exists"        => "true",
   },
   "mcounteren" => {
        "exists"       => "false",
   },
   "mvendorid" => {
       "reset"         => "0x45",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "marchid" => {
       "reset"         => "0x00000010",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "mimpid" => {
       "reset"         => "0x4",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "misa" => {
       "reset"         => "0x40001104",
       "mask"          => "0x0",
       "exists"        => "true",
   },
   "tselect" => {
       "reset"         => "0x0",
       "mask"          => "0x3",    # Four triggers
       "exists"        => "true",
   },
   "mhartid" => {
       "reset"         => "0x0",
       "mask"          => "0x0",
       "poke_mask"     => "0xfffffff0",
       "exists"        => "true",
   },
   "dcsr" => {
       "reset"         => "0x40000003",
       "mask"          => "0x00008c04",
       "poke_mask"     => "0x00008dcc",  # cause field modifiable, nmip modifiable
       "exists"        => "true",
       "debug"         => "true",
    },
    "cycle" => {
        "exists"       => "false",
    },
    "time" => {
        "exists"       => "false",
    },
    "instret" => {
        "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",
    },
    "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"          => "0x0000000f",
       "exists"        => "true",
    },
    "mcpc" => {
       "comment"       => "Core pause",
       "number"        => "0x7c2",
       "reset"         => "0x0",
       "mask"          => "0x0",
       "exists"        => "true",
    },
    "mpmc" => {
       "number"        => "0x7c6",
       "reset"         => "0x2",
       "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"         => "0x200",
       "mask"          => "0x000003ff",
       "poke_mask"     => "0x000003ff",
       "exists"        => "true",
    },
    "mfdc" => {
       "number"        => "0x7f9",
       "reset"         => "0x00070000",
       "mask"          => "0x00071fff",
       "exists"        => "true",
    },
    "mrac" => {
       "comment"       => "Memory region io and cache control.",
       "number"        => "0x7c0",
       "reset"         => "0x0",
       "mask"          => "0xffffffff",
       "exists"        => "true",
       "shared"        => "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",
    },
    "mfdht" => {
        "comment"      => "Force Debug Halt Threshold",
        "number"       => "0x7ce",
        "reset"        => "0x0",
        "mask"         => "0x0000003f",
        "exists"       => "true",
        "shared"       => "true",
    },
    "mfdhs" => {
        "comment"      => "Force Debug Halt Status",
        "number"       => "0x7cf",
        "reset"        => "0x0",
        "mask"         => "0x00000003",
        "exists"       => "true",
    },
    "mscause" => {
        "number"       => "0x7ff",
        "reset"        => "0x0",
        "mask"         => "0x0000000f",
        "exists"       => "true",
    },

);#}}}


# These are the peformance counters events implemented for el2s. An
# event number from outside this list will be replaced by zero if
# written to an MHPMEVENT CSR.
my @perf_events = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
                   17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30,
                   31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
                   45, 46, 47, 48, 49, 50, 54, 55, 56,
                   512, 513, 514, 515, 516);

# FIXME: PMP CSR handling

#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
#
# NOTE: params/keys marked 'derived' are not settable via cmd line, unless they ALSO have the 'overridable' tag
#
our %config = (#{{{
    "harts"                 => "1",
    "xlen"                  => "32",                                # Testbench, Do Not Override
    "numiregs"              => "32",                                # Testbench, Do Not Override
    "regwidth"              => "32",                                # Testbench, Do Not Override
    "reset_vec"             => "0x80000000",                        # Testbench, Overridable
    "nmi_vec"               => "0x11110000",                        # Testbench, Overridable
    "physical"              => "1",
    "num_mmode_perf_regs"   => "4",                                 # Whisper only
    "max_mmode_perf_event"  => "516",                               # Whisper only: performance counters event ids will be clamped to this
    "target"                => $target,                             # Flow Infrastructure
    "config_key"            => "derived",
    "tec_rv_icg"            => "clockhdr",

    "retstack" => {
         "ret_stack_size"    => "$ret_stack_size",                  # Design Parm, Overridable
    },

    "btb"  => {
         "btb_enable"       => "$btb_enable",                       # Design Parm, Overridable
         "btb_fullya"       => "$btb_fullya",                       # Design Parm, Overridable
         "btb_toffset_size" => "$btb_toffset_size",                 # Constant
         "btb_size"         => "$btb_size",                         # Design Parm, Overridable
         "btb_index1_hi"    => "derived",
         "btb_index1_lo"    => "2",                                 # Constant, Do Not Override
         "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"      => "2",                                 # Constant, Do Not Override
         "btb_btag_size"    => "derived",
         "btb_btag_fold"    => "derived",
         "btb_fold2_index_hash" => "derived",
    },
    "bht"  => {
         "bht_size"         => "$bht_size",                         # Design Parm, Overridable
         "bht_addr_hi"      => "derived",
         "bht_addr_lo"      => "2",                                 # Constant, Do Not Override
         "bht_array_depth"  => "derived",
         "bht_ghr_size"     => "derived",
         "bht_ghr_range"    => "derived",
         "bht_hash_string"  => "derived",
         "bht_ghr_hash_1"   => "derived",
    },

    "core" => {
        "user_mode"          => "$user_mode",
        "div_bit"            => "$div_bit",                             # Design Parm, Overridable
        "div_new"            => "$div_new",                             # Design Parm, Overridable
        "lsu_stbuf_depth"    => "$lsu_stbuf_depth",                     # Design Parm, Overridable
        "dma_buf_depth"      => "$dma_buf_depth",                       # Design Parm, Overridable
        "lsu_num_nbload"     => "$lsu_num_nbload",                      # Design Parm, Overridable
        "opensource"         => "$opensource",                          # Flow Infrastructure
        "verilator"          => "$verilator",                           # Flow Infrastructure
        "load_to_use_plus1"  => "$load_to_use_plus1",                   # Design Parm, Overridable
        "iccm_icache"        => 'derived',                              # Used by design
        "iccm_only"          => 'derived',                              # Used by design
        "icache_only"        => 'derived',                              # Used by design
        "no_iccm_no_icache"  => 'derived',                              # Used by design
        "timer_legal_en"          => '1',                               # Design Parm, Overridable
        "bitmanip_zba"             => $bitmanip_zba,                    # Design Parm, Overridable
        "bitmanip_zbb"             => $bitmanip_zbb,                    # Design Parm, Overridable
        "bitmanip_zbc"             => $bitmanip_zbc,                    # Design Parm, Overridable
        "bitmanip_zbe"             => $bitmanip_zbe,                    # Design Parm, Overridable
        "bitmanip_zbf"             => $bitmanip_zbf,                    # Design Parm, Overridable
        "bitmanip_zbp"             => $bitmanip_zbp,                    # Design Parm, Overridable
        "bitmanip_zbr"             => $bitmanip_zbr,                    # Design Parm, Overridable
        "bitmanip_zbs"             => $bitmanip_zbs,                    # Design Parm, Overridable
        "fast_interrupt_redirect"  => "$fast_interrupt_redirect",       # Design Parm, Overridable
        "lsu2dma"            => $lsu2dma,                               # used by design/TB for LSU to DMA bridge
        "fpga_optimize"      => $fpga_optimize                          # Design Parm, Overridable
    },

    "dccm" => {
        "dccm_enable"       => "$dccm_enable",                          # Design Parm, Overridable
        "dccm_region"       => "$dccm_region",                          # Design Parm, Overridable
        "dccm_offset"       => "$dccm_offset",                          # Design Parm, Overridable
        "dccm_size"         => "$dccm_size",                            # Design Parm, Overridable
        "dccm_num_banks"    => "$dccm_num_banks",                       # Design Parm, Overridable
        "dccm_addr_xor"     => "$dccm_addr_xor",                        # Design Parm, Overridable
        "dccm_wr_readback"  => "$dccm_wr_readback",                     # Design Parm, Overridable
        "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',                               # Testbench use only : reserve dccm space for SW/stack - no random r/w
    },


    "iccm" => {
        "iccm_enable"       => "$iccm_enable",                          # Design Parm, Overridable
        "iccm_region"       => "$iccm_region",                          # Design Parm, Overridable
        "iccm_offset"       => "$iccm_offset",                          # Design Parm, Overridable
        "iccm_size"         => "$iccm_size",                            # Design Parm, Overridable
        "iccm_num_banks"    => "$iccm_num_banks",                       # Design Parm, Overridable
        "iccm_addr_xor"     => "$iccm_addr_xor",                        # Design Parm, Overridable
        "iccm_bank_bits"    => 'derived',
        "iccm_index_bits"   => 'derived',
        "iccm_rows"         => 'derived',
        "iccm_data_cell"    => 'derived',
        "iccm_sadr"         => 'derived',
        "iccm_eadr"         => 'derived',
        "iccm_reserved"     => 'derived',                               # Testbench use only : reserve iccm space for SW/handlers - no random r/w
        "iccm_bank_hi"      => 'derived',
        "iccm_ecc_width"    => "$iccm_ecc_width",
        "iccm_bank_index_lo"      => 'derived',
    },
    "icache" => {
        "icache_enable"      => "$icache_enable",                        # Design Parm, Overridable
        "icache_waypack"     => "$icache_waypack",                       # Design Parm, Overridable
        "icache_addr_xor"    => "$icache_addr_xor",                      # Design Parm, Overridable
        "icache_num_ways"    => "$icache_num_ways",                      # Design Parm, Overridable
        "icache_banks_way"   => "2",                                     # Design Parm, Constant
        "icache_bank_width"  => "8",                                     # Design Parm, Constant
        "icache_ln_sz"       => "$icache_ln_sz",                         # Design Parm, Overridable
        "icache_size"        => "$icache_size",                          # Design Parm, Overridable
        "icache_bypass_enable"      => "$icache_bypass_enable",          # Design Parm, Overridable
        "icache_num_bypass"         => "$icache_num_bypass",             # Design Parm, Overridable
        "icache_num_bypass_width"   => 'derived',
        "icache_tag_bypass_enable"      => "$icache_tag_bypass_enable",  # Design Parm, Overridable
        "icache_tag_num_bypass"         => "$icache_tag_num_bypass",     # Design Parm, Overridable
        "icache_tag_num_bypass_width"   => 'derived',
        "icache_bank_hi"    => 'derived',
        "icache_bank_lo"    => 'derived',
        "icache_data_cell"  => 'derived',
        "icache_tag_cell"   => 'derived',
        "icache_tag_depth"  => 'derived',
        "icache_data_depth"  => 'derived',
        "icache_num_lines"   => 'derived',
        "icache_num_lines_bank"  => 'derived',
        "icache_num_lines_way"  => 'derived',
        "icache_data_depth"         => 'derived',
        "icache_tag_lo"          => 'derived',
        "icache_index_hi"        => 'derived',
        "icache_data_index_lo"   => 'derived',
        "icache_data_width"      => 'derived',
        "icache_fdata_width"     => 'derived',
        "icache_tag_index_lo"    => 'derived',
        "icache_ecc"             => "$icache_ecc",                      # Design Parm, Overridable
        "icache_2banks"          => "$icache_2banks",                   # Design Parm, Overridable
        "icache_bank_bits"       => "derived",
        "icache_status_bits"     => "derived",
        "icache_num_beats"       => "derived",
        "icache_beat_bits"       => "derived",
        "icache_scnd_last"       => "derived",
        "icache_beat_addr_hi"    => "derived",
    },
    "pic" => {
        "pic_2cycle"         => "$pic_2cycle",                          # Design Parm, Overridable
        "pic_region"         => "$pic_region",                          # Design Parm, Overridable
        "pic_offset"         => "$pic_offset",                          # Design Parm, Overridable
        "pic_size"           => "$pic_size",                            # Design Parm, Overridable
        "pic_base_addr"      => 'derived',
        "pic_total_int_plus1"      => 'derived',                        # pic_total_int + 1
        "pic_total_int"      => "$pic_total_int",                       # Design Parm, Overridable
        "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',                               # Testbench only: Offset of meipl relative to pic_base_addr
        "pic_meip_offset"    => '0x1000',                               # Testbench only: Offset of meip relative to pic_base_addr
        "pic_meie_offset"    => '0x2000',                               # Testbench only: Offset of meie relative to pic_base_addr
        "pic_mpiccfg_offset" => '0x3000',                               # Testbench only: Offset of mpiccfg relative to pic_base_addr
        "pic_meipt_offset"   => '0x3004',                               # Testbench only: Offset of meipt relative to pic_base_addr -- deprecated
        "pic_meigwctrl_offset" => '0x4000',                             # Testbench only: gateway control regs relative to pic_base_addr
        "pic_meigwclr_offset"  => '0x5000',                             # Testbench only: gateway clear regs relative to pic_base_addr

        "pic_meipl_mask"     => '0xf',                                  # Whisper only
        "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"     => 'derived',
        "pic_meip_count"      => 'derived',
        "pic_meie_count"      => 'derived',
        "pic_mpiccfg_count"   => 1,
        "pic_meipt_count"     => 'derived',
        "pic_meigwctrl_count" => 'derived',
        "pic_meigwclr_count"  => 'derived',
    },
    "testbench" => {                                                    # Testbench only
        "TOP"               => "tb_top",
        "RV_TOP"            => "`TOP.rvtop_wrapper.rvtop",
        "CPU_TOP"           => "`RV_TOP.veer",
        "clock_period"      => "100",
        "build_ahb_lite"    => "$ahb",
        "build_axi4"        => "$axi",
        "build_axi_native"  => "1",
        "openocd_test"      => "$openocd_test",
        "ext_datawidth"     => "64",
        "ext_addrwidth"     => "32",
        "sterr_rollback"    => "0",
        "lderr_rollback"    => "1",
        "SDVT_AHB"          => "$ahb",
    },
    "protection" => {                                                   # Design parms, Overridable - static MPU
        "pmp_entries"         => "$pmp_entries",
        "smepmp"              => "$smepmp",
        "lockstep_enable"     => "$lockstep_enable",
        "lockstep_regfile_enable" => "$lockstep_regfile_enable",
        "lockstep_regfile_read_enable" => "$lockstep_regfile_read_enable",
        "lockstep_delay"      => "$lockstep_delay",
        "mubi_width"          => "$mubi_width",
        "mubi_true"           => "$mubi_true",
        "mubi_false"          => "$mubi_false",
        "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
        "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',
        "lsu_bus_id"       => '1',
        "lsu_bus_prty"     => '2',
        "dma_bus_tag"      => '1',
        "dma_bus_id"       => '1',
        "dma_bus_prty"     => '2',
        "sb_bus_tag"       => '1',
        "sb_bus_id"        => '1',
        "sb_bus_prty"      => '2',
        "ifu_bus_tag"      => 'derived',
        "ifu_bus_id"       => '1',
        "ifu_bus_prty"     => '2',
        "bus_prty_default" => '3',
    },
    "triggers" => \@triggers,                                           # Whisper only
    "csr" => \%csr,                                                     # Whisper only
    "perf_events" => \@perf_events,                                     # Whisper only
    "even_odd_trigger_chains" => "true",                                # Whisper only

    "tech_specific_ec_rv_icg" => '0',

    "user_ec_rv_icg" => 'user_clock_gate',
);


# These parms are used in the Verilog and will be part of global parm structure
# need to have this be width in binary
# for now autosize to the data
our %verilog_parms = (
        "user_mode" => '1',

        "lsu2dma"                 => '1',
        "timer_legal_en"          => '1',
        "bitmanip_zbb"            => '1',
        "bitmanip_zbs"            => '1',
        "bitmanip_zba"            => '1',
        "bitmanip_zbc"            => '1',
        "bitmanip_zbe"            => '1',
        "bitmanip_zbf"            => '1',
        "bitmanip_zbp"            => '1',
        "bitmanip_zbr"            => '1',
        "fast_interrupt_redirect" => '1',

        "pmp_entries"         => '7',
        "smepmp"              => '1',
        "lockstep_enable"     => '1',
        "lockstep_regfile_enable" => '1',
        "lockstep_delay"      => '3',
        "mubi_width"          => "6",
        "mubi_true"           => "32",
        "mubi_false"          => "32",
        "inst_access_enable0" => '1',
        "inst_access_addr0"   => '32',
        "inst_access_mask0"   => '32',
        "inst_access_enable1" => '1',
        "inst_access_addr1"   => '32',
        "inst_access_mask1"   => '32',
        "inst_access_enable2" => '1',
        "inst_access_addr2"   => '32',
        "inst_access_mask2"   => '32',
        "inst_access_enable3" => '1',
        "inst_access_addr3"   => '32',
        "inst_access_mask3"   => '32',
        "inst_access_enable4" => '1',
        "inst_access_addr4"   => '32',
        "inst_access_mask4"   => '32',
        "inst_access_enable5" => '1',
        "inst_access_addr5"   => '32',
        "inst_access_mask5"   => '32',
        "inst_access_enable6" => '1',
        "inst_access_addr6"   => '32',
        "inst_access_mask6"   => '32',
        "inst_access_enable7" => '1',
        "inst_access_addr7"   => '32',
        "inst_access_mask7"   => '32',
        "data_access_enable0" => '1',
        "data_access_addr0"   => '32',
        "data_access_mask0"   => '32',
        "data_access_enable1" => '1',
        "data_access_addr1"   => '32',
        "data_access_mask1"   => '32',
        "data_access_enable2" => '1',
        "data_access_addr2"   => '32',
        "data_access_mask2"   => '32',
        "data_access_enable3" => '1',
        "data_access_addr3"   => '32',
        "data_access_mask3"   => '32',
        "data_access_enable4" => '1',
        "data_access_addr4"   => '32',
        "data_access_mask4"   => '32',
        "data_access_enable5" => '1',
        "data_access_addr5"   => '32',
        "data_access_mask5"   => '32',
        "data_access_enable6" => '1',
        "data_access_addr6"   => '32',
        "data_access_mask6"   => '32',
        "data_access_enable7" => '1',
        "data_access_addr7"   => '32',
        "data_access_mask7"   => '32',
        "iccm_bits"              => '5',
        "iccm_bank_hi"           => '5',
        "iccm_bank_index_lo"     => '5',
        "icache_bank_bits"       => '3',
        "icache_status_bits"     => '3',
        "icache_num_beats"       => '4',
        "icache_beat_bits"       => '4',
        "icache_scnd_last"       => '4',
        "icache_beat_addr_hi"    => '4',
        "icache_bypass_enable"   => '1',
        "icache_num_bypass"      => '4',
        "icache_num_bypass_width"    => '4',
        "icache_tag_bypass_enable"   => '1',
        "icache_tag_num_bypass"      => '4',
        "icache_tag_num_bypass_width"    => '4',
        "iccm_icache"        => '1',
        "iccm_only"          => '1',
        "icache_only"        => '1',
        "no_iccm_no_icache"  => '1',
         "build_axi4" => '1',
         "build_ahb_lite" => '1',
         "build_axi_native" => '1',
         "lsu_num_nbload_width" => '3',
         "lsu_num_nbload"    => '5',
         "ret_stack_size"   => '4',
         "btb_fullya"       => '1',
         "btb_toffset_size" => '5',
         "btb_enable"       => '1',
         "btb_size"         => '10',
         "btb_index1_hi"    => '5',
         "btb_index1_lo"    => '5',
         "btb_index2_hi"    => '5',
         "btb_index2_lo"    => '5',
         "btb_index3_hi"    => '5',
         "btb_index3_lo"    => '5',
         "btb_addr_hi"      => '5',
         "btb_array_depth"  => '9',
         "btb_addr_lo"      => '2',
         "btb_btag_size"    => '5',
         "btb_btag_fold"    => '1',
         "btb_fold2_index_hash" => '1',
         "bht_size"         => '12',
         "bht_addr_hi"      => '4',
         "bht_addr_lo"      => '2',
         "bht_array_depth"  => '11',
         "bht_ghr_size"     => '4',
         "bht_ghr_hash_1"   => '1',
        "div_bit"                 => '3',
        "div_new"                 => '1',
        "lsu_stbuf_depth"   => '4',
        "dma_buf_depth"     => '3',
        "load_to_use_plus1"        => '1',
        "dccm_enable"       => '1',
        "dccm_region"       => '4',
        "dccm_size"         => '10',
        "dccm_num_banks"    => '5',
        "dccm_sadr"         => '32',
        "dccm_bits"         => '5',
        "dccm_bank_bits"    => '3',
        "dccm_data_width"   => '6',
        "dccm_fdata_width"  => '6',
        "dccm_byte_width"   => '3',
        "dccm_width_bits"   => '2',
        "dccm_index_bits"   => '4',
        "dccm_ecc_width"    => '3',
        "lsu_sb_bits"       => '5',
        "iccm_enable"       => '1',
        "iccm_region"       => '4',
        "iccm_size"         => '10',
        "iccm_num_banks"    => '5',
        "iccm_bank_bits"    => '3',
        "iccm_index_bits"   => '4',
        "iccm_sadr"         => '32',
        "iccm_ecc_width"    => '3',
        "icache_enable"     => '1',
        "icache_waypack"    => '1',
        "icache_num_ways"   => '3',
        "icache_banks_way"  => '3',
        "icache_bank_width" => '4',
        "icache_ln_sz"      => '7',
        "icache_size"       => '9',
        "icache_bank_hi"    => '3',
        "icache_bank_lo"    => '2',
        "icache_tag_depth"  => '13',
        "icache_data_depth"      => '14',
        "icache_tag_lo"          => '5',
        "icache_index_hi"        => '5',
        "icache_data_index_lo"   => '3',
        "icache_data_width"      => '7',
        "icache_fdata_width"     => '7',
        "icache_tag_index_lo"    => '3',
        "icache_ecc"             => '1',
        "icache_2banks"          => '1',
        "pic_2cycle"         => '1',
        "pic_region"         => '4',
        "pic_size"           => '9',
        "pic_base_addr"      => '32',
        "pic_total_int_plus1"  => '9',
        "pic_total_int"      => '8',
        "pic_int_words"      => '4',
        "pic_bits"           => '5',
        "lsu_bus_tag"      => '4',
        "lsu_bus_id"       => '1',
        "lsu_bus_prty"     => '2',
        "dma_bus_tag"      => '4',
        "dma_bus_id"       => '5',
        "dma_bus_prty"     => '2',
        "sb_bus_tag"       => '4',
        "sb_bus_id"        => '1',
        "sb_bus_prty"      => '2',
        "ifu_bus_tag"      => '4',
        "ifu_bus_id"       => '1',
        "ifu_bus_prty"     => '2',
        "bus_prty_default" => '2',
);

# to make sure parameter math works properly add 4 to each key of %verilog_parms - was an issue in btb calculations
my $key;
foreach $key (sort keys %verilog_parms) {
    if ( $key ne "user_mode" && $key ne "smepmp" ) {
        $verilog_parms{$key} += 4;
    }
}


# need to figure out what to do here
# for now none of these can be parameters


# move deletes lower

# Perform any overrides first before derived values
#
map_set_unset();

gen_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==8||$c==16||$c==32||$c==64||$c==128||$c==256||$c==512))        { die("$helpusage\n\nFAIL: btb_size == $c;         ILLEGAL !!!\n\n"); }
$c=$config{btb}{btb_size};            if (($c==64||$c==128||$c==256||$c==512) && $config{btb}{btb_fullya})        { die("$helpusage\n\nFAIL: btb_size == $c; btb_fullya=1        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==2||$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==2 || $c==4 || ($c==8  && $config{iccm}{iccm_size} != 2) || ($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: dccm_region == $c       ILLEGAL !!!\n\n"); }
$c=$config{dccm}{dccm_num_banks};     if (!(($c==2 && $config{dccm}{dccm_size} != 48) || $c==4 || ($c==8 && $config{dccm}{dccm_size} != 48) || ($c==16 && $config{dccm}{dccm_size} != 4 && $config{dccm}{dccm_size} != 48)))
                                                                                               { 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: dccm_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: dccm_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_num_bypass}; if ($c<1 || $c>8)                                        { die("$helpusage\n\nFAIL: icache_num_bypass == $c       ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_bypass_enable};     if (!($c==0 || $c==1))                            { die("$helpusage\n\nFAIL: icache_bypass_enable == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_tag_num_bypass}; if ($c<1 || $c>8)                                        { die("$helpusage\n\nFAIL: icache_tag_num_bypass == $c       ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_tag_bypass_enable};     if (!($c==0 || $c==1))                            { die("$helpusage\n\nFAIL: icache_tag_bypass_enable == $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_waypack};    if (!($c==0 || $c==1))                                   { die("$helpusage\n\nFAIL: icache_waypack == $c     ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_num_ways};   if (!($c==2 || $c==4))                                   { die("$helpusage\n\nFAIL: icache_num_ways == $c   ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_ln_sz};      if (!($c==32 || $c==64))                                 { die("$helpusage\n\nFAIL: icache_ln_sz == $c   ILLEGAL !!!\n\n"); }
$c=$config{icache}{icache_size};       if (!($c==8 || $c==16 || $c==32 || $c==64 || $c==128 || $c==256)) { die("$helpusage\n\nFAIL: icache_size == $c       ILLEGAL !!!\n\n"); }
$c=$config{core}{div_bit};             if (!($c==1 || $c==2 || $c==3 || $c==4  ))               { die("$helpusage\n\nFAIL: div_bit == $c   ILLEGAL !!!\n\n"); }
$c=$config{core}{div_new};             if (!($c==0 || $c==1))                                   { die("$helpusage\n\nFAIL: div_new == $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 || $c==5))                          { 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"); }


# force div_bit to be 1 for old div algorithm
if ($config{core}{div_new}==0 && $config{core}{div_bit}!=1) {
    die("$helpusage\n\nFAIL: div_new=0 requires div_bit=1   ILLEGAL !!!\n\n");
}

$c=$config{protection}{pmp_entries};       if (!($c==64 || $c==16 || $c==0))                       { die("$helpusage\n\nFAIL: pmp_entries must be either 0, 16 or 64 !!!\n\n"); }
$c=$config{protection}{lockstep_delay};    if ($config{protection}{lockstep_enable} && ($c<0 || $c>4)) { die("$helpusage\n\nFAIL: lockstep_delay must fit in range <0, 4>!!!\n\n"); }
$c=$config{icache}{icache_addr_xor};       if ($c==1 && !$config{protection}{lockstep_enable})       { die("$helpusage\n\nFAIL: icache_addr_xor requires lockstep_enable (DCLS) !!!\n\n"); }
$c=$config{dccm}{dccm_addr_xor};           if ($c==1 && !$config{protection}{lockstep_enable})       { die("$helpusage\n\nFAIL: dccm_addr_xor requires lockstep_enable (DCLS) !!!\n\n"); }
$c=$config{dccm}{dccm_wr_readback};        if ($c==1 && !$config{dccm}{dccm_enable})                 { die("$helpusage\n\nFAIL: dccm_wr_readback requires dccm_enable !!!\n\n"); }
$c=$config{protection}{lockstep_regfile_enable};      if ($c==1 && !$config{protection}{lockstep_enable}) { die("$helpusage\n\nFAIL: lockstep_regfile_enable requires lockstep_enable !!!\n\n"); }
$c=$config{protection}{lockstep_regfile_read_enable}; if ($c==1 && !$config{protection}{lockstep_regfile_enable}) { die("$helpusage\n\nFAIL: lockstep_regfile_read_enable requires lockstep_regfile_enable !!!\n\n"); }
$c=$config{iccm}{iccm_addr_xor};           if ($c==1 && !$config{iccm}{iccm_enable})                 { die("$helpusage\n\nFAIL: iccm_addr_xor requires iccm_enable !!!\n\n"); }
$c=$config{iccm}{iccm_addr_xor};           if ($c==1 && !$config{protection}{lockstep_enable})       { die("$helpusage\n\nFAIL: iccm_addr_xor requires lockstep_enable (DCLS) !!!\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  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask0 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask1}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask1 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask2}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask2 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask3}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask3 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask4}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask4 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask5}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask5 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask6}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask6 invalid !!!\n\n"); }
$c=$config{protection}{inst_access_mask7}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: inst_access_mask7 invalid !!!\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  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask0 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask1}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask1 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask2}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask2 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask3}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask3 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask4}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask4 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask5}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask5 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask6}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask6 invalid !!!\n\n"); }
$c=$config{protection}{data_access_mask7}; if ((hex($c)&0x3f) != 63  || invalid_mask($c))                               { die("$helpusage\n\nFAIL: data_access_mask7 invalid !!!\n\n"); }

# Verify multibit parameters
my $w = $config{protection}{mubi_width};
my $t = $config{protection}{mubi_true};
my $f = $config{protection}{mubi_false};
if (($w<2 || $w>32) || ($w % 2))                    { die("$helpusage\n\nFAIL: mubi_width must be multiple of 2 in rage <2, 32> !!!\n\n");}
if ((hex($t) > ((2**$w)-1)) || (hex($t) <= 0)) { die("$helpusage\n\nFAIL: mubi_true value is outside of permitted range!!!\n\n");}
if ((hex($f) > ((2**$w)-1)) || (hex($f) <= 0)) { die("$helpusage\n\nFAIL: mubi_false value is outside of permitted range!!!\n\n");}
if ((hex($t) != ((~hex($f)) & (2**$w - 1)))) { die("$helpusage\n\nFAIL: mubi_true and mubi_false values must be complementary!!!\n\n");}

if ((hex($config{protection}{inst_access_addr0}) & hex($config{protection}{inst_access_mask0}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr0 and inst_access_mask0 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr1}) & hex($config{protection}{inst_access_mask1}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr1 and inst_access_mask1 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr2}) & hex($config{protection}{inst_access_mask2}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr2 and inst_access_mask2 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr3}) & hex($config{protection}{inst_access_mask3}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr3 and inst_access_mask3 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr4}) & hex($config{protection}{inst_access_mask4}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr4 and inst_access_mask4 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr5}) & hex($config{protection}{inst_access_mask5}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr5 and inst_access_mask5 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr6}) & hex($config{protection}{inst_access_mask6}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr6 and inst_access_mask6 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{inst_access_addr7}) & hex($config{protection}{inst_access_mask7}))!=0)    { die("$helpusage\n\nFAIL: inst_access_addr7 and inst_access_mask7 must be orthogonal!!!\n\n"); }

if ((hex($config{protection}{data_access_addr0}) & hex($config{protection}{data_access_mask0}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr0 and data_access_mask0 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr1}) & hex($config{protection}{data_access_mask1}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr1 and data_access_mask1 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr2}) & hex($config{protection}{data_access_mask2}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr2 and data_access_mask2 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr3}) & hex($config{protection}{data_access_mask3}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr3 and data_access_mask3 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr4}) & hex($config{protection}{data_access_mask4}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr4 and data_access_mask4 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr5}) & hex($config{protection}{data_access_mask5}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr5 and data_access_mask5 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr6}) & hex($config{protection}{data_access_mask6}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr6 and data_access_mask6 must be orthogonal!!!\n\n"); }
if ((hex($config{protection}{data_access_addr7}) & hex($config{protection}{data_access_mask7}))!=0)    { die("$helpusage\n\nFAIL: data_access_addr7 and data_access_mask7 must be orthogonal!!!\n\n"); }


# Fill in derived configuration entries.

if (($config{icache}{icache_enable}==0 || grep(/icache_enable/, @unsets)) && $config{iccm}{iccm_enable}==0) {
    $config{core}{no_iccm_no_icache}=1;
}
elsif (($config{icache}{icache_enable}==0 || grep(/icache_enable/, @unsets)) && $config{iccm}{iccm_enable}==1) {
    $config{core}{iccm_only}=1;
}
elsif ($config{icache}{icache_enable}==1 && $config{iccm}{iccm_enable}==0) {
    $config{core}{icache_only}=1;
}
elsif ($config{icache}{icache_enable}==1 && $config{iccm}{iccm_enable}==1) {
    $config{core}{iccm_icache}=1;
}

if (!$config{dccm}{dccm_enable}) {
    $config{core}{fast_interrupt_redirect} = 0;
    print "$self: Disabling fast_interrupt_redirect because DCCM not enabled\n";
}



$config{btb}{btb_btag_fold} = 0;
$config{btb}{btb_fold2_index_hash} = 0;
$config{btb}{btb_btag_size} = 31;

if($config{btb}{btb_size}==512){
    $config{btb}{btb_index1_hi} = 9;
    $config{btb}{btb_index2_hi} = 17;
    $config{btb}{btb_index3_hi} = 25;
    $config{btb}{btb_array_depth}= 256;
    $config{btb}{btb_btag_size} = 5;
} elsif($config{btb}{btb_size}==256){
    $config{btb}{btb_index1_hi} = 8;
    $config{btb}{btb_index2_hi} = 15;
    $config{btb}{btb_index3_hi} = 22;
    $config{btb}{btb_array_depth}= 128;
    $config{btb}{btb_btag_size} = 6;
} elsif($config{btb}{btb_size}==128){
    $config{btb}{btb_index1_hi} = 7;
    $config{btb}{btb_index2_hi} = 13;
    $config{btb}{btb_index3_hi} = 19;
    $config{btb}{btb_array_depth}= 64;
    $config{btb}{btb_btag_size} = 7;
} elsif($config{btb}{btb_size}==64){
    $config{btb}{btb_index1_hi} = 6;
    $config{btb}{btb_index2_hi} = 11;
    $config{btb}{btb_index3_hi} = 16;
    $config{btb}{btb_array_depth}= 32;
    $config{btb}{btb_btag_size} = 8;
} elsif($config{btb}{btb_size}==32){
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 9;
    $config{btb}{btb_index3_hi} = 13;
    $config{btb}{btb_array_depth}= 16;
    $config{btb}{btb_btag_size} = 9 unless $config{btb}{btb_fullya};
    $config{btb}{btb_btag_fold} = 1;
} elsif($config{btb}{btb_size}<32){
    #verif issues require these even though they are not needed
    $config{btb}{btb_index1_hi} = 5;
    $config{btb}{btb_index2_hi} = 8;
    $config{btb}{btb_index3_hi} = 11;
    $config{btb}{btb_fullya} = 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};

if($config{bht}{bht_size}==2048){
    $config{bht}{bht_ghr_size}= 10;
    $config{bht}{bht_ghr_range}= "9:0";
    $config{bht}{bht_array_depth}= 1024;
    $config{bht}{bht_addr_hi}= 11;
} elsif($config{bht}{bht_size}==1024){
    $config{bht}{bht_ghr_size}= 9;
    $config{bht}{bht_ghr_range}= "8:0";
    $config{bht}{bht_array_depth}= 512;
    $config{bht}{bht_addr_hi}= 10;
} elsif($config{bht}{bht_size}==512){
    $config{bht}{bht_ghr_size}= 8;
    $config{bht}{bht_ghr_range}= "7:0";
    $config{bht}{bht_array_depth}= 256;
    $config{bht}{bht_addr_hi}= 9;
} elsif($config{bht}{bht_size}==256){
    $config{bht}{bht_ghr_size}= 7;
    $config{bht}{bht_ghr_range}= "6:0";
    $config{bht}{bht_addr_hi} = 8;
    $config{bht}{bht_array_depth}= 128;
} elsif($config{bht}{bht_size}==128){
    $config{bht}{bht_ghr_size}= 6;
    $config{bht}{bht_ghr_range}= "5:0";
    $config{bht}{bht_addr_hi} = 7;
    $config{bht}{bht_array_depth}= 64;
} elsif($config{bht}{bht_size}==64){
    $config{bht}{bht_ghr_size}= 5;
    $config{bht}{bht_ghr_range}= "4:0";
    $config{bht}{bht_addr_hi} = 6;
    $config{bht}{bht_array_depth}= 32;
} elsif($config{bht}{bht_size}==32){
    $config{bht}{bht_ghr_size}= 4;
    $config{bht}{bht_ghr_range}= "3:0";
    $config{bht}{bht_addr_hi} = 5;
    $config{bht}{bht_array_depth}= 16;
}
$config{bht}{bht_ghr_hash_1} = ($config{bht}{bht_ghr_size} > ($config{btb}{btb_index1_hi}-1));

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




# PIC derived
$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{pic}{pic_total_int_plus1} = $config{pic}{pic_total_int} + 1;
$config{pic}{pic_meipl_count} = $config{pic}{pic_total_int};
$config{pic}{pic_meip_count} = $config{pic}{pic_int_words};
$config{pic}{pic_meie_count} = $config{pic}{pic_total_int};
$config{pic}{pic_meipt_count} = $config{pic}{pic_total_int};
$config{pic}{pic_meigwctrl_count} = $config{pic}{pic_total_int};
$config{pic}{pic_meigwclr_count} = $config{pic}{pic_total_int};

$config{icache}{icache_num_bypass_width}     = int(log2($config{icache}{icache_num_bypass})) + 1;
$config{icache}{icache_tag_num_bypass_width} = int(log2($config{icache}{icache_tag_num_bypass})) + 1;

$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{bus}{ifu_bus_tag} = log2($config{icache}{icache_ln_sz}/8);

$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}>=16)? 5120 : ($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{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_num_lines}      = $config{icache}{icache_size}*1024/$config{icache}{icache_ln_sz};
$config{icache}{icache_num_lines_way}  = $config{icache}{icache_num_lines}/$config{icache}{icache_num_ways};
$config{icache}{icache_num_lines_bank} = $config{icache}{icache_num_lines}/($config{icache}{icache_num_ways} * $config{icache}{icache_banks_way});
$config{icache}{icache_data_depth}        = $config{icache}{icache_num_lines_bank} * $config{icache}{icache_ln_sz} /$config{icache}{icache_bank_width};
$config{icache}{icache_data_index_lo}  = log2($config{icache}{icache_bank_width}) + log2($config{icache}{icache_banks_way});
$config{icache}{icache_index_hi}       = $config{icache}{icache_data_index_lo} + log2($config{icache}{icache_data_depth}) -1;
$config{icache}{icache_bank_hi}        = $config{icache}{icache_data_index_lo}  - 1;
$config{icache}{icache_bank_lo}        = log2($config{icache}{icache_bank_width});
$config{icache}{icache_tag_index_lo}   = log2($config{icache}{icache_ln_sz});
$config{icache}{icache_tag_lo}         = log2($config{icache}{icache_num_lines_way}) + $config{icache}{icache_tag_index_lo};
$config{icache}{icache_tag_depth}      = $config{icache}{icache_num_lines}/$config{icache}{icache_num_ways};
$config{icache}{icache_data_width}     = 8*$config{icache}{icache_bank_width};

$config{icache}{icache_bank_bits}     = 1+$config{icache}{icache_bank_hi}-$config{icache}{icache_bank_lo};
$config{icache}{icache_status_bits}   =  $config{icache}{icache_num_ways}-1;
$config{icache}{icache_num_beats}     = ($config{icache}{icache_ln_sz}==64) ? 8 : 4;
$config{icache}{icache_beat_bits}     = ($config{icache}{icache_ln_sz}==64) ? 3 : 2;
$config{icache}{icache_scnd_last}     = ($config{icache}{icache_ln_sz}==64) ? 6 : 2;
$config{icache}{icache_beat_addr_hi}  = ($config{icache}{icache_ln_sz}==64) ? 5 : 4;


if (($config{icache}{icache_ecc})) {
$config{icache}{icache_fdata_width} = $config{icache}{icache_data_width} + 7;
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_data_depth}x$config{icache}{icache_fdata_width}";
$config{icache}{icache_tag_cell}    = ($config{icache}{icache_tag_depth} == 32) ? "ram_$config{icache}{icache_tag_depth}x26" : "ram_$config{icache}{icache_tag_depth}x25";

}
else {
$config{icache}{icache_fdata_width} = $config{icache}{icache_data_width} + 4;
$config{icache}{icache_data_cell}   = "ram_$config{icache}{icache_data_depth}x$config{icache}{icache_fdata_width}";
$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";

$config{iccm}{iccm_bank_hi}        = 2+$config{iccm}{iccm_bank_bits}-1;
$config{iccm}{iccm_bank_index_lo}  = 1+$config{iccm}{iccm_bank_hi};

# 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}"} = "";

# 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});
#

# 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{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}{external_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

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

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",
        "mubi_true"  => "$config{protection}{mubi_width}",
        "mubi_false" => "$config{protection}{mubi_width}",
);
#}}}

my $d=$config{protection}{lockstep_enable}; if ($d==0 || !grep(/lockstep_enable=1/, @sets))  { delete $config{"protection"}{"lockstep_enable"}; }
$c=$config{protection}{lockstep_delay}; if ($d==0 || ($c==0 && !grep(/lockstep_delay=/, @sets))) { delete $config{"protection"}{"lockstep_delay"}; }
$c=$config{protection}{lockstep_regfile_enable}; if ($d==0 || ($c==0 || !grep(/lockstep_regfile_enable=/, @sets))) { delete $config{"protection"}{"lockstep_regfile_enable"}; }
$c=$config{protection}{lockstep_regfile_read_enable}; if ($d==0 || ($c==0 || !grep(/lockstep_regfile_read_enable=/, @sets))) { delete $config{"protection"}{"lockstep_regfile_read_enable"}; }

if ($d==0) {
    delete $config{protection}{mubi_width};
    delete $verilog_parms{mubi_width};
    delete $config{protection}{mubi_true};
    delete $verilog_parms{mubi_true};
    delete $config{protection}{mubi_false};
    delete $verilog_parms{mubi_false};
}

print "\nVeeR configuration for target=$target\n\n";
dump_define("","", \%config,[]);


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

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

# Sanity checks

# Do not allow Smepmp to be enabled when user mode is disabled
# TODO: Allowing this combination would require adding additional CSR decoder
# variants.
if(( $config{core}{user_mode} eq 0 ) && ( $config{protection}{smepmp} eq 1 )) {
    die "$self: ERROR! Currently Smepmp feature cannot be enabled when user mode support is disabled\n";
}

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";
}

#printf( "axi4 %s\n",$config{"testbench"}{"build_axi4"});
#printf( "ahb_lite %s\n",$config{"testbench"}{"build_ahb_lite"});
#printf( "axi_native %s\n",$config{"testbench"}{"build_axi_native"});

if( $target eq "el2_formal_axi" ) {
    $config{testbench}{build_axi_native} = 1;
    $config{testbench}{build_axi4} = 1;
    print( "\$config{testbench}{build_axi_native} = $config{testbench}{build_axi_native} \n" );
    print( "\$config{testbench}{build_axi4      } = $config{testbench}{build_axi4      } \n" );
}


if (($config{testbench}{build_ahb_lite} == 1)) {
    delete $config{testbench}{build_axi4};
    $config{testbench}{build_axi_native}=1;
    $config{testbench}{SDVT_AHB}=1;
    $verilog_parms{build_axi4} = 0;
}
elsif (($config{testbench}{build_axi4} == 1)) {
    $config{testbench}{build_axi_native}=1;
    delete $config{testbench}{build_ahb_lite};
    $config{testbench}{SDVT_AHB}=0;
    $verilog_parms{build_ahb_lite} = 0;
}
elsif (($config{testbench}{build_axi_native} == 1)) {
    die("illegal to set build_axi_native w/out build_axi4");
}

#printf( "axi4 %s\n",$config{"testbench"}{"build_axi4"});
#printf( "ahb_lite %s\n",$config{"testbench"}{"build_ahb_lite"});
#printf( "axi_native %s\n",$config{"testbench"}{"build_axi_native"});


# Over-ride MFDC reset value for AXI.
# Disable Bus barrier and 64b for AXI
if (defined($config{"testbench"}{"build_axi_native"}) && ($config{"testbench"}{"build_axi_native"} ne "0")) {
    if (! (defined($config{testbench}{build_ahb_lite}) && $config{testbench}{build_ahb_lite} ne "0")) {
        $config{csr}{mfdc}{reset} = "0x00070040" if exists $config{csr}{mfdc};
    }

}



# parm processing before any values are deleted from the hash

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

delete $config{testbench}{openocd_test} if ($config{testbench}{openocd_test} == 0);

# Remove TECH_SPECIFIC_* defines if they are set to 0
foreach my $key (sort keys(%config)) {
    if (grep(/tech_specific_/, $key)) {
        if ($config{$key} == 0) {
            delete $config{$key};
        }
    }
}

print "$self: Writing $tdfile\n";
print "$self: Writing $paramfile\n";
open (FILE1, ">$tdfile") || die "Cannot open $tdfile for writing $!\n";
open (FILE2, ">$paramfile") || die "Cannot open $paramfile for writing $!\n";
print_header_to(\*FILE1, "//");
print_header_to(\*FILE2, "//");
gen_define("","`", \%config, \%verilog_parms, \@verilog_vars);
dump_parms(\%verilog_parms);
close FILE1;
close FILE2;

$config{config_key}="32'hdeadbeef";

# end parms

# deletes
if (($load_to_use_plus1==0) && !grep(/load_to_use_plus1/, @sets)) { delete $config{"core"}{"load_to_use_plus1"}; }
if (($iccm_enable==0) && !grep(/iccm_enable/, @sets))             { delete $config{"iccm"}{"iccm_enable"}; }


# new code to handle the -set=parm=0 value correctly for common_defines.vh
$c=$config{core}{user_mode};         if ($c==0 && !grep(/user_mode=1/, @sets))            { delete $config{"core"}{"user_mode"}; }
$c=$config{core}{load_to_use_plus1}; if ($c==0 && !grep(/load_to_use_plus1=1/, @sets))    { delete $config{"core"}{"load_to_use_plus1"}; }
$c=$config{core}{opensource};        if ($c==0 && !grep(/opensource=1/, @sets))           { delete $config{"core"}{"opensource"}; }
$c=$config{core}{verilator};         if ($c==0 && !grep(/verilator=1/, @sets))            { delete $config{"core"}{"verilator"}; }
$c=$config{core}{div_new};           if ($c==0 && !grep(/div_new=1/, @sets))              { delete $config{"core"}{"div_new"}; }
# not needed
#$c=$config{core}{div_bit};           if ($c==0 && !grep(/div_bit=1/, @sets))              { delete $config{"core"}{"div_bit"}; }
$c=$config{iccm}{iccm_enable};       if ($c==0 && !grep(/iccm_enable=1/, @sets))          { delete $config{"iccm"}{"iccm_enable"}; }
$c=$config{iccm}{iccm_addr_xor};     if ($c==0 && !grep(/iccm_addr_xor=1/, @sets))        { delete $config{"iccm"}{"iccm_addr_xor"}; }
$c=$config{btb}{btb_enable};         if ($c==0 && !grep(/btb_enable=1/, @sets))           { delete $config{"btb"}{"btb_enable"}; }
$c=$config{dccm}{dccm_enable};       if ($c==0 && !grep(/dccm_enable=1/, @sets))          { delete $config{"dccm"}{"dccm_enable"}; }
$c=$config{dccm}{dccm_addr_xor};     if ($c==0 && !grep(/dccm_addr_xor=1/, @sets))        { delete $config{"dccm"}{"dccm_addr_xor"}; }
$c=$config{dccm}{dccm_wr_readback};  if ($c==0 && !grep(/dccm_wr_readback=1/, @sets))     { delete $config{"dccm"}{"dccm_wr_readback"}; }
$c=$config{icache}{icache_waypack};  if ($c==0 && !grep(/icache_waypack=1/, @sets))       { delete $config{"icache"}{"icache_waypack"}; }
$c=$config{icache}{icache_addr_xor}; if ($c==0 && !grep(/icache_addr_xor=1/, @sets))       { delete $config{"icache"}{"icache_addr_xor"}; }
$c=$config{icache}{icache_enable};   if ($c==0 && !grep(/icache_enable=1/, @sets))        { delete $config{"icache"}{"icache_enable"}; }

$c=$config{icache}{icache_2banks};   if ($c==0 && !grep(/icache_2banks=1/, @sets))        { delete $config{"icache"}{"icache_2banks"}; }
$c=$config{pic}{pic_2cycle};         if ($c==0 && !grep(/pic_2cycle=1/, @sets))           { delete $config{"pic"}{"pic_2cycle"}; }

$c=$config{btb}{btb_fullya};         if ($c==0 && !grep(/btb_fullya=1/, @sets))           { delete $config{"btb"}{"btb_fullya"}; }
$c=$config{protection}{smepmp};      if ($c==0 && !grep(/smepmp=1/, @sets))               { delete $config{"protection"}{"smepmp"}; }



if ($target eq "default") {
}
elsif (($config{"testbench"}{"build_axi4"} == 1)) {
    delete $config{"testbench"}{"build_ahb_lite"};
    delete $config{"testbench"}{"build_axi_native_ahb"};
}
elsif (($config{"testbench"}{"build_ahb_lite"} == 1)) {
    delete $config{"testbench"}{"build_axi4"};
    delete $config{"testbench"}{"build_axi_native"};
    delete $config{"testbench"}{"build_axi_native_ahb"};
}
elsif (($config{"testbench"}{"build_axi_native_ahb"} == 1)) {
    delete $config{"testbench"}{"build_axi4"};
    delete $config{"testbench"}{"build_axi_native_ahb"};
}
elsif (($config{"testbench"}{"build_axi_native"} == 1)) {
    die("illegal to set build_axi_native w/out build_axi4");
}
else {
    delete $config{"testbench"}{"build_ahb_lite"};
    delete $config{"testbench"}{"build_axi4"};
    delete $config{"testbench"}{"build_axi_native"};
    delete $config{"testbench"}{"build_axi_native_ahb"};
}






##################### 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("//");
begin_include_guard(\*FILE, "`", "COMMON_DEFINES");
print FILE "`define RV_ROOT \"".$ENV{RV_ROOT}."\"\n";
gen_define("","`", \%config, "", \@verilog_vars);
end_include_guard(\*FILE, "`", "COMMON_DEFINES");
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("//");
begin_include_guard(\*FILE, "#", "DEFINES");
gen_define("","#", \%config, "", \@asm_vars, \@asm_overridable);
end_include_guard(\*FILE, "#", "DEFINES");
close FILE;

# add `define PHYSICAL 1
# remove `undef RV_ICCM_ENABLE

my $pddata='
`include "common_defines.vh"
`undef TEC_RV_ICG
`define RV_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);


# PIC address map based on config
`$ENV{RV_ROOT}/tools/picmap -t $config{pic}{pic_total_int} > $build_path/pic_map_auto.h`;

# 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 {#{{{
    print_header_to(\*FILE, @_);
}#}}}

sub print_header_to {#{{{
    my $fh = shift;
    my $cs = shift;
    my $current_year = (localtime)[5] + 1900;
    $cs = "" unless defined $cs;
    print $fh "$cs Copyright 2019-$current_year Western Digital Corporation or its affiliates.\n";
    print $fh "$cs Copyright 2022-$current_year Antmicro <www.antmicro.com>\n";
    print $fh "$cs\n";
    print $fh "$cs SPDX-License-Identifier: Apache-2.0\n";
    print $fh "$cs Licensed under the Apache License, Version 2.0, see LICENSE for details.\n";
    print $fh "$cs\n";
    print $fh "$cs This is an automatically generated file by $ENV{USER} on ",`date`;
    print $fh "$cs\n$cs cmd:    $self @argv_orig \n";
    print $fh "$cs\n";
}#}}}

# Begins include guard
sub begin_include_guard {
    my ($fh, $sym, $name) = @_;
    print $fh "${sym}ifndef $prefix$name\n";
    print $fh "${sym}define $prefix$name\n\n";
}

# Ends include guard
sub end_include_guard {
    my ($fh, $sym, $name) = @_;
    print $fh "\n${sym}endif // $prefix$name\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 $parms = @_[1];
    my @printvars = @{@_[2]};
    my @overridable = @{@_[3]} if defined @_[3];
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (sort 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});
            if ($parms and defined($parms->{$key})) {
                $parms->{$key} = 0;
            }
            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, $parms, \@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);
                }
                my $override = grep(/^$key$/, @overridable);
                print_define($prefix, $key, $value, $override) if ($value !~ /derived/);
                #printf("$key = $value\n");
                if ($parms and defined($parms->{$key})) {
                    $value=decimal($value);
                    #printf("verilog parm $key = $value %s\n",$parms->{$key});
                    $value=d2b($key,$value,$parms->{$key});
                    #printf("verilog parm $key = $value\n");
                    $parms->{$key}=$value;
                }
            }
        }
    }
}#}}}

sub dump_define {#{{{
    my $matched = shift;
    my $prefix = shift;
    my $hash = @_[0];
    my @printvars = @{@_[1]};
    my @overridable = @{@_[2]} if defined @_[2];
    my $re = join("|",@printvars);
    $re = qr/($re)/;
    #print Dumper($hash);
    foreach my $key (sort 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 : @unsets\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 (sort 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 $tag 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 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 $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 $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 CSRs not included in verilog.
    my @removed_csrs;
    if (! $config{core}{timer_legal_en}) {
        push(@removed_csrs, $_) for qw (mitcnt0 mitbnd0 mitctl0
                                        mitcnt1 mitbnd1 mitctl1);
    }

    # Collect fast interrupt enable.
    if (exists $config{core}{fast_interrupt_redirect}) {
        $jh{fast_interrupt_redirect} = $config{core}{fast_interrupt_redirect};
        # meicpct CSR is not built if fast interrupt.
        push(@removed_csrs, 'meicpct') if $jh{fast_interrupt_redirect};
    }

    # Remove CSRs not configured into verilog.
    delete $jh{csr}{$_} foreach @removed_csrs;

    # Collect zb extension configs
    $jh{enable_zbb} = $config{core}{bitmanip_zbb};
    $jh{enable_zbs} = $config{core}{bitmanip_zbs};
    $jh{enable_zba} = $config{core}{bitmanip_zba};
    $jh{enable_zbc} = $config{core}{bitmanip_zbc};
    $jh{enable_zbe} = $config{core}{bitmanip_zbe};
    $jh{enable_zbf} = $config{core}{bitmanip_zbf};
    $jh{enable_zbp} = $config{core}{bitmanip_zbp};
    $jh{enable_zbr} = $config{core}{bitmanip_zbr};

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

    # Collect performance events. Uncomment when RTL ready.
    if (exists $config{perf_events}) {
        $jh{mmode_perf_events} = $config{perf_events};
    }

    # Make atomic instructions illegal outside of DCCM.
    $jh{amo_illegal_outside_dccm} = "true";

    # Make ld/st instructions trigger misaligned exceptions if base
    # address (value in rs1) and effective address refer to regions of
    # different types.
    $jh{effective_address_compatible_with_base} = "true";

    # 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 b2d {#{{{
    my ($v) = @_;

    $v = oct("0b" . $v);

    return($v);
}#}}}

sub d2b {#{{{
    my ($key,$v,$LEN) = @_;

    my $repeat;

    $v = sprintf "%b",$v;
    if (length($v)<$LEN) {
        $repeat=$LEN-length($v);
        $v="0"x$repeat.$v;
    }
    elsif (length($v)>$LEN) {
        die("d2b: parm $key value $v > len $LEN");
    }

    return($v);
}#}}}


sub invalid_mask {#{{{
    my ($m) = @_;

    if ($m =~ /^0x(0)*([137]?f+)$/) { return(0); }

    return(1);
}#}}}


sub b2h {#{{{
    my ($bin) = @_;

    # Make input bit string a multiple of 4
    $bin = substr("0000",length($bin)%4) . $bin if length($bin)%4;

    my ($hex, $nybble) = ("");
    while (length($bin)) {
        ($nybble,$bin) = (substr($bin,0,4), substr($bin,4));
        $nybble = eval "0b$nybble";
        $hex .= substr("0123456789ABCDEF", $nybble, 1);
    }
    return $hex;
}#}}}

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

    $btb_size = $btb_index_hi - 1;

    my $ghr_hi = $ghr_size - 1;
    my $ghr_lo = $btb_size;

    my $ghr_start = "{";
    if($ghr_size > $btb_size){
        return  "{ghr[$ghr_hi:$ghr_lo], hashin[$btb_index_hi:2]^ghr[$ghr_lo-1:0]} // cf1";
    }
    else {
        return "{hashin[$ghr_size+1:2]^ghr[$ghr_size-1:0]}// cf2";
    }
}#}}}

sub dump_parms {#{{{
    my ($hash) = @_;

    my ($bvalue, $blen, $upper);
    printf(FILE1 "typedef struct packed {\n");
    foreach my $key (sort keys %$hash) {
        $bvalue=$hash->{$key};
        $blen=length($bvalue);
        $upper=$key;
        $upper=~ tr/a-z/A-Z/;
        if ($blen==1) {
            printf(FILE1 "\tlogic %-10s $upper;\n");
        }
        else {
            printf(FILE1 "\tlogic %-10s $upper;\n",sprintf("[%d:0]",$blen-1));
        }
    }
   printf(FILE1 "} el2_param_t;\n\n");

    my $bcat="";
    my $parmcnt=0;
    foreach my $key (sort keys %$hash) {
        printf("// $key = %s\n",$verilog_parms{$key});
        $bcat.=$hash->{$key};
        $parmcnt++;
    }

    my $bvalue="";
    my $pcnt=0;
    my $delim=",";
    my $msb;
    printf(FILE2 "// parameter el2_param_t pt = '{\n");
    foreach my $key (sort keys %$hash) {
        $upper=$key;
        $upper=~ tr/a-z/A-Z/;
        $pcnt++;
        if ($pcnt==$parmcnt) { undef $delim; }
        if ($hash->{$key} eq "0") { $hash->{$key}="0000"; }
        if (length($hash->{$key}) != 1) {
            $msb=substr($hash->{$key},0,4);  # require upper 4b to be 0
            if ($msb ne "0000") { die("parameter $upper upper 4b must be 0"); }
        }
        printf(FILE2 "// \t%-22s : %d\'h%-10s $delim\n",$upper,length($hash->{$key}),b2h($hash->{$key}));
    }
    printf(FILE2 "// }\n");

    printf(FILE2 "parameter el2_param_t pt = %d'h%s\n",length($bcat),b2h($bcat));

}#}}}

sub gen_default_linker_script {#{{{

    open (FILE, ">$linkerfile") || die "Cannot open $linkerfile for writing $!\n";
    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} and $text_in_iccm) {
        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 "*/\n";
    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;
} #}}}
