#!/usr/bin/env python3
"""
Default Jinja2 template for SystemVerilog package generation
"""

DEFAULT_JINJA2_TEMPLATE = """//
//   This file is automatically generated by {{ script_name }}. See MEMORY_MAP.md for more information.
//
//
{{ memmap_comment_str }}
//
//
//   >>>> Memory Mapped Registers and Regions <<<
//
{{ registers_and_sub_ranges_comment_str }}
//

package memmappkg;

// verilator lint_off UNUSEDPARAM

localparam {{ "%-35s"|format("WB_SSEL_WIDTH") }} = {{ enum_width }};
{{ "%-64s"|format("localparam " ~ "%-35s"|format("WB_SLV_CNT") ~ " = WB_SSEL_WIDTH;") }}// equal since ssel is one-hot

// Highest cacheable address (for cache tag optimization)
localparam {{ "%-35s"|format("HIGHEST_CACHEABLE_ADDRESS") }} = {{ highest_addr_cacheable_str }};

// Slave select constants (one-hot encoded)
{% for slave in slaves -%}
{{ "%-64s"|format("localparam " ~ "%-35s"|format("WB_SLAVE_" ~ slave.slave_name.upper()) ~ " = " ~ slave.format_one_hot(slave.one_hot, enum_width) ~ ";") }}// bit position {{ slave.bitpos }}
{% endfor -%}
{{ "%-64s"|format("localparam " ~ "%-35s"|format("WB_SLAVE_NONE") ~ " = " ~ wb_slave_none_value_str ~ ";") }}// none selected

// ------------------------------------------------------------------------------------
// Functions for address translation
// ------------------------------------------------------------------------------------

{% if use_inside_range_condition %}
{% include 'get_slave_sel_inside.sv.j2' %}
{% else %}
{% include 'get_slave_sel_or_chain.sv.j2' %}
{% endif %}

// ------------------------------------------------------------------------------------
// Functions for slaves
// ------------------------------------------------------------------------------------

`ifdef SIMULATION

// Slave can use these to determine whether it is selected by the given address
{% for slave in slaves -%}
localparam {{ "%-35s"|format(slave.slave_name.lower()+"_bitpos") }} = {{ slave.bitpos }};
{% endfor %}

{% for slave in slaves %}
function logic is_selected_slave_{{ slave.slave_name.lower() }} (input logic [31:0] addr, input logic [{{ enum_width-1 }}:0] slave_sel_i);
    is_selected_slave_{{ "%-24s"|format(slave.slave_name.lower()) }} = slave_sel_i[{{ slave.slave_name.lower()+"_bitpos" }}];
endfunction
{% endfor %}

`ifdef SLANG
$static_assert(is_selected_slave_sdram(32'h0000_0000, WB_SLAVE_SDRAM), "WB_SLAVE_SDRAM should cause selected slave to be sdram");
$static_assert(!is_selected_slave_sdram(32'h0000_0000, WB_SLAVE_FLASH), "WB_SLAVE_FLASH should not cause selected slave to be sdram");
$static_assert(!is_selected_slave_sdram(32'h0000_0000, WB_SLAVE_BRAM), "WB_SLAVE_BRAM should not cause selected slave to be sdram");
$static_assert(!is_selected_slave_sdram(32'h0000_0000, WB_SLAVE_PERIPHERALS), "WB_SLAVE_PERIPHERALS should not cause selected slave to be sdram");
$static_assert(!is_selected_slave_sdram(32'h0000_0000, WB_SLAVE_NONE), "WB_SLAVE_NONE should not cause selected slave to be sdram");

$static_assert(!is_selected_slave_flash(32'h0000_0000, WB_SLAVE_SDRAM), "WB_SLAVE_SDRAM should not cause selected slave to be flash");
$static_assert(is_selected_slave_flash(32'h0000_0000, WB_SLAVE_FLASH), "WB_SLAVE_FLASH should cause selected slave to be flash");
$static_assert(!is_selected_slave_flash(32'h0000_0000, WB_SLAVE_BRAM), "WB_SLAVE_BRAM should not cause selected slave to be flash");
$static_assert(!is_selected_slave_flash(32'h0000_0000, WB_SLAVE_PERIPHERALS), "WB_SLAVE_PERIPHERALS should not cause selected slave to be flash");
$static_assert(!is_selected_slave_flash(32'h0000_0000, WB_SLAVE_NONE), "WB_SLAVE_NONE should not cause selected slave to be flash");

$static_assert(!is_selected_slave_bram(32'h0000_0000, WB_SLAVE_SDRAM), "WB_SLAVE_SDRAM should not cause selected slave to be bram");
$static_assert(!is_selected_slave_bram(32'h0000_0000, WB_SLAVE_FLASH), "WB_SLAVE_FLASH should not cause selected slave to be bram");
$static_assert(is_selected_slave_bram(32'h0000_0000, WB_SLAVE_BRAM), "WB_SLAVE_BRAM should not cause selected slave to be bram");
$static_assert(!is_selected_slave_bram(32'h0000_0000, WB_SLAVE_PERIPHERALS), "WB_SLAVE_PERIPHERALS should not cause selected slave to be bram");
$static_assert(!is_selected_slave_bram(32'h0000_0000, WB_SLAVE_NONE), "WB_SLAVE_NONE should not cause selected slave to be bram");

$static_assert(!is_selected_slave_peripherals(32'h0000_0000, WB_SLAVE_SDRAM), "WB_SLAVE_SDRAM should not cause selected slave to be peripherals");
$static_assert(!is_selected_slave_peripherals(32'h0000_0000, WB_SLAVE_FLASH), "WB_SLAVE_FLASH should not cause selected slave to be peripherals");
$static_assert(!is_selected_slave_peripherals(32'h0000_0000, WB_SLAVE_BRAM), "WB_SLAVE_BRAM should not cause selected slave to be peripherals");
$static_assert(is_selected_slave_peripherals(32'h0000_0000, WB_SLAVE_PERIPHERALS), "WB_SLAVE_PERIPHERALS should cause selected slave to be peripherals");
$static_assert(!is_selected_slave_peripherals(32'h0000_0000, WB_SLAVE_NONE), "WB_SLAVE_NONE should not cause selected slave to be peripherals");
`endif

`endif

// convert one-hot to index using priority encoder
function automatic int get_slave_idx (input logic [{{ enum_width-1 }}:0] slave_sel_i);
    get_slave_idx = 0; // default to none selected
    for (int i = 0; i < WB_SLV_CNT; i++) begin
        if (slave_sel_i[i]) begin 
            get_slave_idx = i;
        end
    end
endfunction

`ifdef SLANG
$static_assert(get_slave_idx(WB_SLAVE_SDRAM) == sdram_bitpos, "WB_SLAVE_SDRAM should have index 0");
$static_assert(get_slave_idx(WB_SLAVE_FLASH) == flash_bitpos, "WB_SLAVE_FLASH should have index 1");
$static_assert(get_slave_idx(WB_SLAVE_BRAM) == bram_bitpos, "WB_SLAVE_BRAM should have index 2");
$static_assert(get_slave_idx(WB_SLAVE_PERIPHERALS) == peripherals_bitpos, "WB_SLAVE_PERIPHERALS should have index 3");
$static_assert(get_slave_idx(WB_SLAVE_NONE) == 0, "WB_SLAVE_NONE should have index 0");
`endif

// ------------------------------------------------------------------------------------
// Functions for access legality
// ------------------------------------------------------------------------------------

typedef enum logic [0:0] {
    READ_LEGAL  = 1'b0,
    WRITE_LEGAL = 1'b1
} access_mode_t;
{% for slave in slaves -%}
function logic is_legal_access_{{ slave.slave_name.lower() }}(input logic[31:0] addr, access_mode_t access);
    case (access)
        READ_LEGAL:  is_legal_access_{{ slave.slave_name.lower() }} = {% for expr in slave.read_legal_exprs -%}
                {{ expr }}
            {%- endfor -%};
        WRITE_LEGAL: is_legal_access_{{ slave.slave_name.lower() }} = {% for expr in slave.write_legal_exprs -%}
                {{ expr }}
            {%- endfor -%};
        default:     is_legal_access_{{ slave.slave_name.lower() }} = 1'b0;
    endcase
endfunction

{% endfor -%}

// ------------------------------------------------------------------------------------
// Register address constants
// ------------------------------------------------------------------------------------

{% for slave in slaves -%}
{% if slave.registers %}

//
// {{ slave.slave_name }} register addresses
//
{% for register in slave.registers -%}
localparam REG_{{ register.get_name_snake_case(False).upper().ljust(30) }} = {{ register.get_addr_hex_str(True, 4, "verilog") }};
{% endfor -%}
{% endif -%}
{% endfor %}

// ------------------------------------------------------------------------------------
// Register test functions
// ------------------------------------------------------------------------------------

{% for slave in slaves %}
{% if slave.registers %}
{% if not loop.first %}

{% endif %}
//
// {{ slave.slave_name }} register test functions
//

{% for register in slave.registers %}
{% set reg_snake = register.get_name_snake_case(False) %}
{% set fn_name = reg_snake if reg_snake.endswith('_register') else reg_snake + '_register' %}
// is addr the {{ register.name }}?
function logic is_addr_{{ fn_name }} (input logic [31:0] addr);
    is_addr_{{ fn_name }} = (addr==REG_{{ register.get_name_snake_case(False).upper() }});
endfunction
{% endfor %}

{% endif %}
{% endfor %}

// ------------------------------------------------------------------------------------
// Range and buffer test functions
// ------------------------------------------------------------------------------------

{% if use_inside_range_condition %}
{% include 'range_buffer_tests_inside.sv.j2' %}
{% else %}
{% include 'range_buffer_tests_gte_lte.sv.j2' %}
{% endif %}

// verilator lint_on UNUSEDPARAM

endpackage

"""

DEFAULT_GET_SLAVE_SEL_FUNC_OR_CHAIN = """
// Compute slave_sel_o for the given address
function logic [{{ enum_width-1 }}:0] get_slave_sel_o (input logic [31:0] addr);
    {% for s in slave_addr_ranges %}
    if (
        {%- for r in s.ranges %}
        {%- if r.start == 0 %}
        ($unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }}){{ ' ||' if not loop.last else '' }}
        {%- else %}
        ($unsigned(addr) >= 32'h{{ "%08x"|format(r.start) }} && $unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }}){{ ' ||' if not loop.last else '' }}
        {%- endif %}
        {%- endfor %}
    ) return WB_SLAVE_{{ s.slave_name.upper() }};
    {% endfor %}
    return WB_SLAVE_NONE; // No slave selected
endfunction
"""

DEFAULT_GET_SLAVE_SEL_FUNC_INSIDE = """
// Compute slave_sel_o for the given address
function logic [{{ enum_width-1 }}:0] get_slave_sel_o (input logic [31:0] addr);
    unique case ($unsigned(addr)) inside
    {% for s in slave_addr_ranges %}
    {% if s.ranges|length > 0 %}
        {% if s.ranges|length == 1 %}
        [32'h{{ "%08x"|format(s.ranges[0].start) }} : 32'h{{ "%08x"|format(s.ranges[0].end) }}]       : get_slave_sel_o = WB_SLAVE_{{ s.slave_name.upper() }};
        {% else %}
        {% for r in s.ranges %}
        [32'h{{ "%08x"|format(r.start) }} : 32'h{{ "%08x"|format(r.end) }}]{{ "," if not loop.last else "       : get_slave_sel_o = WB_SLAVE_" ~ s.slave_name.upper() ~ ";" }}
        {% endfor %}
        {% endif %}
    {% endif %}
    {% endfor %}
        default                             : get_slave_sel_o = WB_SLAVE_NONE;
    endcase
endfunction
"""

DEFAULT_RANGE_TEST_FUNCTIONS_GTE_LTE = """{% for slave in slaves_collected %}
{% if slave.ranges|length > 0 %}
{% if not loop.first %}

{% endif %}
//
// {{ slave.name }} range and buffer test functions
//

// is addr handled by {{ slave.name }} bus slave
function logic is_addr_in_{{ slave.name|lower|replace(' ', '_') }} (input logic [31:0] addr);
    is_addr_in_{{ slave.name|lower|replace(' ', '_') }} = (
        {%- for r in slave.ranges %}
        {%- if r.start == 0 -%}
        $unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }}
        {%- else -%}
        ($unsigned(addr) >= 32'h{{ "%08x"|format(r.start) }} && $unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }})
        {%- endif -%}
        {{ ' ||' if not loop.last else '' }}
        {%- endfor %}
    );
endfunction

{% for r in slave.ranges %}
// is addr in range {{ r.name }} of the {{ slave.name }} bus slave?
function logic is_addr_in_{{ r.name_snake }}_range (input logic [31:0] addr);
    is_addr_in_{{ r.name_snake }}_range = ({% if r.start == 0 %}$unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }}{% else %}$unsigned(addr) >= 32'h{{ "%08x"|format(r.start) }} && $unsigned(addr) <= 32'h{{ "%08x"|format(r.end) }}{% endif %});
endfunction
{% for b in r.buffers %}
{% set bfn = b.name_snake if b.name_snake.endswith('_buffer') else b.name_snake + '_buffer' %}
// is addr in buffer {{ b.name }}?
function logic is_addr_in_{{ bfn }} (input logic [31:0] addr);
    is_addr_in_{{ bfn }} = ({% if b.start == 0 %}$unsigned(addr) <= 32'h{{ "%08x"|format(b.end) }}{% else %}$unsigned(addr) >= 32'h{{ "%08x"|format(b.start) }} && $unsigned(addr) <= 32'h{{ "%08x"|format(b.end) }}{% endif %});
endfunction
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
"""

DEFAULT_RANGE_TEST_FUNCTIONS_INSIDE = """{% for slave in slaves_collected %}
{% if slave.ranges|length > 0 %}
{% if not loop.first %}

{% endif %}
//
// {{ slave.name }} range and buffer test functions
//

// is addr handled by {{ slave.name }} bus slave
function logic is_addr_in_{{ slave.name|lower|replace(' ', '_') }} (input logic [31:0] addr);
    {% if slave.ranges|length == 1 %}
    is_addr_in_{{ slave.name|lower|replace(' ', '_') }} = ($unsigned(addr) inside { [32'h{{ "%08x"|format(slave.ranges[0].start) }} : 32'h{{ "%08x"|format(slave.ranges[0].end) }}] });
    {% else %}
    is_addr_in_{{ slave.name|lower|replace(' ', '_') }} = ($unsigned(addr) inside {
        {% for r in slave.ranges %}
        [32'h{{ "%08x"|format(r.start) }} : 32'h{{ "%08x"|format(r.end) }}]{{ "," if not loop.last else "" }}
        {% endfor %}
    });
    {% endif %}
endfunction

{% for r in slave.ranges %}
// is addr in range {{ r.name }} of the {{ slave.name }} bus slave?
function logic is_addr_in_{{ r.name_snake }}_range (input logic [31:0] addr);
    is_addr_in_{{ r.name_snake }}_range = ($unsigned(addr) inside { [32'h{{ "%08x"|format(r.start) }} : 32'h{{ "%08x"|format(r.end) }}] });
endfunction
{% for b in r.buffers %}
{% set bfn = b.name_snake if b.name_snake.endswith('_buffer') else b.name_snake + '_buffer' %}
// is addr in buffer {{ b.name }}?
function logic is_addr_in_{{ bfn }} (input logic [31:0] addr);
    is_addr_in_{{ bfn }} = ($unsigned(addr) inside { [32'h{{ "%08x"|format(b.start) }} : 32'h{{ "%08x"|format(b.end) }}] });
endfunction
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
"""