{# MACRO #}
{#- vector range for select operations #}
{% macro range(msb, lsb, is_vector=true) %}
    {% if is_vector %}
        {% if msb == lsb %}
[{{ msb }}]
        {%- else %}
[{{ msb }}:{{ lsb }}]
        {%- endif %}
    {%- endif %}
{%- endmacro %}

{#- vector range for declarations #}
{% macro range_decl(msb, is_vector=true) %}
    {% if is_vector %}
[{{ msb }}:0]
    {%- endif %}
{%- endmacro %}

{#- literal #}
{% macro literal(val, width=1) %}
    {% if width == 1 %}
1'b{{ val }}
    {%- else %}
{{ width}}'h{{ '%x' % val }}
    {%- endif %}
{%- endmacro %}

{#- special literal for all zeros #}
{% macro zeros(width=1) %}
    {% if width == 1 %}
1'b0
    {%- else %}
{{ width }}'h0
    {%- endif %}
{%- endmacro %}

{#- special literal for all ones #}
{% macro ones(width=1) %}
    {% if width == 1 %}
1'b1
    {%- else %}
{{ "{%d{1'b1}}" % width }}
    {%- endif %}
{%- endmacro %}

{#- 'always' header with reset logic #}
{% macro always_begin(sig='', width=1, init=0) %}
    {% set rst_type = config['register_reset'].value%}
    {% if rst_type == 'init_only' %}
always @(posedge clk) begin
    {% else %}
        {% if rst_type == 'sync_pos' %}
always @(posedge clk) begin
    if (rst) begin
        {% elif rst_type == 'sync_neg' %}
always @(posedge clk) begin
    if (!rst) begin
        {% elif rst_type == 'async_pos' %}
always @(posedge clk or posedge rst) begin
    if (rst) begin
        {% elif rst_type == 'async_neg' %}
always @(posedge clk or negedge rst) begin
    if (!rst) begin
        {% endif %}
        {{ sig }} <= {{ literal(init, width) }};
    end else
    {%- endif %}
{%- endmacro %}

{#- 'always' part with write strobes #}
{% macro always_write(reg, bf) %}
    {% if 'w' in bf.access %}
 if (lb_wen && (lb_waddr == {{ literal(reg.address, config['address_width'].value) }})) begin
        {% for num, val in bf.byte_strobes.items() %}
            {% if 'w1tc' in bf.modifiers %}
        if (lb_wstrb[{{ num }}] && lb_wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }})
            {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= {{ zeros(val['bf_msb'] - val['bf_lsb'] + 1) }};
            {% elif 'w1ts' in bf.modifiers %}
        if (lb_wstrb[{{ num }}] && lb_wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }})
            {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= {{ ones(val['bf_msb'] - val['bf_lsb'] + 1) }};
            {% elif 'w1tt' in bf.modifiers %}
        if (lb_wstrb[{{ num }}] && lb_wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }})
            {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= ~{{ sig_bf_ff(reg, bf) }};
            {% else %}
        if (lb_wstrb[{{ num }}])
            {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= lb_wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }};
            {% endif %}
        {% endfor %}
        {% if 'hwu' in bf.modifiers %}
    end else
        {%- else %}
    end
        {%- endif %}
    {%- endif %}
{%- endmacro %}

{#- 'always' part for external updates #}
{% macro always_update(reg, bf) %}
    {% if 'hwu' in bf.modifiers %}
 if ({{ sig_bf_port_upd(reg, bf) }}) begin
        {{ sig_bf_ff(reg, bf) }} <= {{ sig_bf_port_new(reg, bf) }};
    end
    {%- endif %}
{%- endmacro %}

{#- 'always' part for self clear #}
{% macro always_self_clear(reg, bf) %}
    {% if 'sc' in bf.modifiers %}
 else begin
        {{ sig_bf_ff(reg, bf) }} <= {{ zeros(bf.width) }};
    end
    {%- endif %}
{%- endmacro %}

{#- 'always' part with clear after read #}
{% macro always_read_clear(reg, bf) %}
    {% if bf.access == 'ro' and 'rtc' in bf.modifiers %}
 if (lb_ren && (lb_raddr == {{ literal(reg.address, config['address_width'].value) }})) begin
        {{ sig_bf_ff(reg, bf) }} <= {{ zeros(bf.width) }};
    end
    {%- endif %}
{%- endmacro %}

{#- signal: csr for rdata bus #}
{% macro sig_csr_rdata(reg) %}
csr_{{ reg.name.lower() }}_rdata
{%- endmacro %}

{#- signal: bitfield port io #}
{% macro sig_bf_port_io(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}
{%- endmacro %}

{#- signal: bitfield port new data for update  #}
{% macro sig_bf_port_new(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_new
{%- endmacro %}

{#- signal: bitfield port update enable #}
{% macro sig_bf_port_upd(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_upd
{%- endmacro %}

{#- signal: bitfield flip-flops for write #}
{% macro sig_bf_ff(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_ff
{%- endmacro %}

{#- TEMPLATE NAMESPACE #}
{% set tmp = namespace() %}

{#- TEMPLATE #}
// Created with Corsair v{{ corsair_ver }}
// Register map module v{{ config['version'].value }}

module {{ config['name'].value }} #(
    parameter ADDR_W = {{ config['address_width'].value }},
    parameter DATA_W = {{ config['data_width'].value }},
    parameter STRB_W = DATA_W / 8
)(
    // System
    input clk,
{% if config['register_reset'].value != 'init_only' %}
    input rst,
{% endif %}
{% for reg in rmap %}
    // CSR: {{ reg.name }}
    {% for bf in reg %}
        {% if bf.access == 'ro' and 'const' not in bf.modifiers and 'hwu' not in bf.modifiers %}
    input {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ sig_bf_port_io(reg, bf) }},
        {% elif 'w' in bf.access %}
    output {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ sig_bf_port_io(reg, bf) }},
        {% endif %}
        {% if 'hwu' in bf.modifiers %}
    input {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ sig_bf_port_new(reg, bf) }},
    input {{ sig_bf_port_upd(reg, bf) }},
        {% endif %}
    {% endfor %}
{% endfor %}
    // Local Bus
    input  [ADDR_W-1:0] lb_waddr,
    input  [DATA_W-1:0] lb_wdata,
    input               lb_wen,
    input  [STRB_W-1:0] lb_wstrb,
    output              lb_wready,
    input  [ADDR_W-1:0] lb_raddr,
    input               lb_ren,
    output [DATA_W-1:0] lb_rdata,
    output              lb_rvalid
);

{% for reg in rmap %}
//------------------------------------------------------------------------------
// CSR:
// [{{ '0x%x' % reg.address }}] - {{ reg.name }} - {{ reg.description }}
//------------------------------------------------------------------------------
wire {{ range_decl(config['data_width'].value - 1) }} {{ sig_csr_rdata(reg) }};
{# fill unused bits with zeros #}
    {% set tmp.last_bit = 0 %}
    {% for bf in reg %}
        {% if bf.lsb > tmp.last_bit %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.lsb - 1, tmp.last_bit) }} = {{ zeros(bf.lsb - tmp.last_bit) }};
        {% endif %}
        {% set tmp.last_bit = bf.msb + 1 %}
    {% endfor %}
    {% if config['data_width'].value - 1 > tmp.last_bit %}
assign {{ sig_csr_rdata(reg) }}{{ range(config['data_width'].value - 1, tmp.last_bit) }} = {{ zeros(config['data_width'].value - tmp.last_bit) }};
    {% endif %}

{# generate bit fields #}
    {% for bf in reg %}
//---------------------
// Bit field:
// {{ reg.name }}{{ range(bf.msb, bf.lsb) }} - {{ bf.name }} - {{ bf.description }}
// {{ bf.access }}{{ ', ' + ', '.join(bf.modifiers) if bf.modifiers else '' }}
//---------------------
        {% if bf.access == 'ro' and 'hwu' not in bf.modifiers %}
        {% elif config['register_reset'].value == 'init_only' %}
reg {{ range_decl( bf.width - 1, bf.is_vector()) }} {{ sig_bf_ff(reg, bf) }} = {{ literal(bf.initial, bf.width) }};
        {% else %}
reg {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ sig_bf_ff(reg, bf) }};
        {% endif %}
        {% if bf.access == 'wo' %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ zeros(bf.width) }};
        {% elif bf.access == 'ro' and 'const' in bf.modifiers %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ literal(bf.initial, bf.width) }};
        {% elif bf.access == 'ro' and 'hwu' not in bf.modifiers %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ sig_bf_port_io(reg, bf) }};
        {% else %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ sig_bf_ff(reg, bf) }};
        {% endif %}
        {% if 'w' in bf.access %}
assign {{ sig_bf_port_io(reg, bf) }} = {{ sig_bf_ff(reg, bf) }};
        {% endif %}

        {% if bf.access != 'ro' or 'hwu' in bf.modifiers %}
{{ always_begin(sig=sig_bf_ff(reg, bf), width=bf.width, init=bf.initial)
}}{{ always_write(reg, bf)
}}{{ always_update(reg, bf)
}}{{ always_self_clear(reg, bf)
}}{{ always_read_clear(reg, bf)
}}
end
        {% endif %}

    {% endfor %}
{% endfor %}
//------------------------------------------------------------------------------
// Write ready
//------------------------------------------------------------------------------
assign lb_wready = 1'b1;

//------------------------------------------------------------------------------
// Read address decoder
//------------------------------------------------------------------------------
reg {{ range_decl(config['data_width'].value - 1) }} lb_rdata_ff;
{{ always_begin(sig='lb_rdata_ff', width=config['data_width'].value, init=config['regmap']['read_filler'].value
)}} if (lb_ren) begin
        case (lb_raddr)
{% for reg in rmap %}
            {{ literal(reg.address, config['address_width'].value) }}: lb_rdata_ff <= {{ sig_csr_rdata(reg) }};
{% endfor %}
            default: lb_rdata_ff <= {{ literal(config['regmap']['read_filler'].value, config['data_width'].value) }};
        endcase
    end else begin
        lb_rdata_ff <= {{ literal(config['regmap']['read_filler'].value, config['data_width'].value) }};
    end
end
assign lb_rdata = lb_rdata_ff;

//------------------------------------------------------------------------------
// Read data valid
//------------------------------------------------------------------------------
reg lb_rvalid_ff;
{{ always_begin(sig='lb_rvalid_ff'
)}} if (lb_ren && lb_rvalid) begin
        lb_rvalid_ff <= 1'b0;
    end else if (lb_ren) begin
        lb_rvalid_ff <= 1'b1;
    end
end
assign lb_rvalid = lb_rvalid_ff;

endmodule
