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

{#- self clear logic #}
{% macro self_clear(sig, enable=False, width=1) %}
    {% if enable %}
    {{ sig }} <= {{ zeros(width) }};
    {%- endif %}
{%- endmacro %}

{#- 'always' header with reset logic #}
{% macro always_begin(sig='', width=1, init=0) %}
    {% set rst_type = config['register_reset']%}
    {% 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
{%- endmacro %}

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

{#- port: bitfield output #}
{% macro port_bf_out(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_out
{%- endmacro %}

{#- port: bitfield input  #}
{% macro port_bf_in(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_in
{%- endmacro %}

{#- port: bitfield input enable #}
{% macro port_bf_en(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_en
{%- endmacro %}

{#- port: bitfield clear enable #}
{% macro port_bf_clr(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_clr
{%- endmacro %}

{#- port: bitfield set enable #}
{% macro port_bf_set(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_set
{%- endmacro %}

{#- port: bitfield read enable #}
{% macro port_bf_ren(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_ren
{%- endmacro %}

{#- port: bitfield read valid #}
{% macro port_bf_rvalid(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_rvalid
{%- endmacro %}

{#- port: bitfield write enable #}
{% macro port_bf_wen(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_wen
{%- endmacro %}

{#- port: bitfield write ready #}
{% macro port_bf_wready(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_wready
{%- endmacro %}

{#- signal: bitfield registered read valid #}
{% macro sig_bf_rvalid_ff(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_rvalid_ff
{%- endmacro %}

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

{#- port: bitfield read access strobe #}
{% macro port_bf_raccess(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_raccess
{%- endmacro %}

{#- port: bitfield write access strobe #}
{% macro port_bf_waccess(reg, bf) %}
csr_{{ reg.name.lower() }}_{{ bf.name.lower() }}_waccess
{%- endmacro %}

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

{#- signal: register read enable #}
{% macro sig_csr_ren(reg) %}
csr_{{ reg.name.lower() }}_ren
{%- endmacro %}

{#- signal: register read enable registered #}
{% macro sig_csr_ren_ff(reg) %}
csr_{{ reg.name.lower() }}_ren_ff
{%- endmacro %}

{#- signal: register write enable #}
{% macro sig_csr_wen(reg) %}
csr_{{ reg.name.lower() }}_wen
{%- endmacro %}

{#- TEMPLATE NAMESPACE #}
{% set tmp = namespace() %}
{% from "amm2lb_verilog.j2" import amm_core with context %}
{% from "axil2lb_verilog.j2" import axil_core with context %}
{% from "apb2lb_verilog.j2" import apb_core with context %}

{#- TEMPLATE #}
// Created with Corsair v{{ corsair_ver }}

module {{ module_name }} #(
    parameter ADDR_W = {{ config['address_width'] }},
    parameter DATA_W = {{ config['data_width'] }},
    parameter STRB_W = DATA_W / 8
)(
    // System
    input clk,
    input rst,
{% for reg in rmap %}
    {% for bf in reg %}
    // {{ reg.name }}.{{ bf.name }}
        {% if 'a' in bf.hardware %}
            {% if 'i' in bf.hardware %}
    output {{ port_bf_raccess(reg, bf) }},
            {% endif %}
            {% if 'o' in bf.hardware %}
    output {{ port_bf_waccess(reg, bf) }},
            {% endif %}
        {% endif %}
        {% if 'l' in bf.hardware %}
    input {{ port_bf_lock(reg, bf) }},
        {% endif %}
        {% if 'e' in bf.hardware %}
    input {{ port_bf_en(reg, bf) }},
        {% endif %}
        {% if 'c' in bf.hardware %}
    input {{ port_bf_clr(reg, bf) }},
        {% endif %}
        {% if 's' in bf.hardware %}
    input {{ port_bf_set(reg, bf) }},
        {% endif %}
        {% if 'q' in bf.hardware and 'r' in bf.access %}
    input {{ port_bf_rvalid(reg, bf) }},
    output {{ port_bf_ren(reg, bf) }},
    input {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ port_bf_in(reg, bf) }},
        {% endif %}
        {% if 'i' in bf.hardware %}
    input {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ port_bf_in(reg, bf) }},
        {% endif %}
        {% if 'o' in bf.hardware %}
    output {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ port_bf_out(reg, bf) }},
        {% endif %}
        {% if 'q' in bf.hardware and  'w' in bf.access %}
    output {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ port_bf_out(reg, bf) }},
    input {{ port_bf_wready(reg, bf) }},
    output {{ port_bf_wen(reg, bf) }},
        {% endif %}
    {% endfor %}

{% endfor %}
{% if interface == 'apb' %}
{{ apb_core(regmap_embed=True) }}
{% elif interface == 'axil' %}
{{ axil_core(regmap_embed=True) }}
{% elif interface == 'amm' %}
{{ amm_core(regmap_embed=True) }}
{% else %}
    // Local Bus
    input  [ADDR_W-1:0] waddr,
    input  [DATA_W-1:0] wdata,
    input               wen,
    input  [STRB_W-1:0] wstrb,
    output              wready,
    input  [ADDR_W-1:0] raddr,
    input               ren,
    output [DATA_W-1:0] rdata,
    output              rvalid
);
{% endif %}
{% for reg in rmap %}
//------------------------------------------------------------------------------
// CSR:
// [{{ '0x%x' % reg.address }}] - {{ reg.name }} - {{ reg.description }}
//------------------------------------------------------------------------------
wire {{ range_decl(config['data_width'] - 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'] - 1 > tmp.last_bit %}
assign {{ sig_csr_rdata(reg) }}{{ range(config['data_width'] - 1, tmp.last_bit) }} = {{ zeros(config['data_width'] - tmp.last_bit) }};
    {% endif %}

    {% if 'w' in reg.access %}
wire {{ sig_csr_wen(reg) }};
assign {{ sig_csr_wen(reg) }} = wen && (waddr == {{ literal(reg.address, config['address_width']) }});
    {% endif %}

    {% if 'r' in reg.access %}
wire {{ sig_csr_ren(reg) }};
assign {{ sig_csr_ren(reg) }} = ren && (raddr == {{ literal(reg.address, config['address_width']) }});
reg {{ sig_csr_ren_ff(reg) }};
{{ always_begin(sig=sig_csr_ren_ff(reg)
)}} begin
        {{ sig_csr_ren_ff(reg) }} <= {{ sig_csr_ren(reg) }};
    end
end
    {% endif %}
    {% for bf in reg %}
//---------------------
// Bit field:
// {{ reg.name }}{{ range(bf.msb, bf.lsb) }} - {{ bf.name }} - {{ bf.description }}
// access: {{ bf.access }}, hardware: {{ bf.hardware }}
//---------------------
        {% if 'a' in bf.hardware %}
            {% if 'o' in bf.hardware %}
assign {{ port_bf_waccess(reg, bf) }} = wready && {{ sig_csr_wen(reg) }};
            {% endif %}
            {% if 'i' in bf.hardware %}
assign {{ port_bf_raccess(reg, bf) }} = rvalid && {{ sig_csr_ren(reg) }};
            {% endif %}
        {% endif %}
reg {{ range_decl(bf.width - 1, bf.is_vector()) }} {{ sig_bf_ff(reg, bf) }};

        {% if 'wo' in bf.access %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ zeros(bf.width) }};
        {% elif 'r' in bf.access and 'q' in bf.hardware %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ port_bf_in(reg, bf) }};
        {% else %}
assign {{ sig_csr_rdata(reg) }}{{ range(bf.msb, bf.lsb) }} = {{ sig_bf_ff(reg, bf) }};
        {% endif %}

        {% if 'w' in bf.access and 'q' in bf.hardware %}
assign {{ port_bf_out(reg, bf) }} = wdata{{ range(bf.msb, bf.lsb) }};
        {% elif 'o' in bf.hardware %}
assign {{ port_bf_out(reg, bf) }} = {{ sig_bf_ff(reg, bf) }};
        {% endif %}
        {% if 'r' in bf.access and 'q' in bf.hardware %}
assign {{ port_bf_ren(reg, bf) }} = {{ sig_csr_ren(reg) }} & (~{{ sig_csr_ren_ff(reg) }});
        {% endif %}
        {% if 'w' in bf.access and 'q' in bf.hardware %}
assign {{ port_bf_wen(reg, bf) }} = {{ sig_csr_wen(reg) }};
        {% endif %}

{{ always_begin(sig=sig_bf_ff(reg, bf), width=bf.width, init=bf.reset)
}} {% if 'l' in bf.hardware %}if (!{{ port_bf_lock(reg, bf) }}){% endif %} begin
        {% if 's' in bf.hardware %}
        if ({{ port_bf_set(reg, bf) }}) begin
            {{ sig_bf_ff(reg, bf) }} <= {{ ones(bf.width) }};
        end else
        {%- endif %} {% if 'c' in bf.hardware -%}
        if ({{ port_bf_clr(reg, bf) }}) begin
            {{ sig_bf_ff(reg, bf) }} <= {{ zeros(bf.width) }};
        end else
        {%- endif %} {% if bf.access in ['roc', 'rolh'] -%}
        if ({{ sig_csr_ren(reg) }}) begin
            {{ sig_bf_ff(reg, bf) }} <= {{ zeros(bf.width) }};
        end else
        {%- endif %} {% if bf.access == 'roll' -%}
        if ({{ sig_csr_ren(reg) }}) begin
            {{ sig_bf_ff(reg, bf) }} <= {{ ones(bf.width) }};
        end else
        {%- endif %} {% if 'w' in bf.access -%}
        if ({{ sig_csr_wen(reg) }}) begin
            {% for num, val in bf.byte_strobes.items() %}
                {% if 'w1c' in bf.access %}
            if (wstrb[{{ num }}] && wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }}) begin
                {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= {{ zeros(val['bf_msb'] - val['bf_lsb'] + 1) }};
                {% elif 'w1s' in bf.access %}
            if (wstrb[{{ num }}] && wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }}) begin
                {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= {{ ones(val['bf_msb'] - val['bf_lsb'] + 1) }};
                {% else %}
            if (wstrb[{{ num }}]) begin
                {{ sig_bf_ff(reg, bf) }}{{ range(val['bf_msb'], val['bf_lsb'], bf.is_vector()) }} <= wdata{{ range(val['wdata_msb'], val['wdata_lsb']) }};
                {% endif %}
            end
            {% endfor %}
        end else
        {%- endif %} {% if 'i' in bf.hardware -%} {%- if 'e' in bf.hardware and 'll' in bf.access -%}
        if ({{ port_bf_en(reg, bf) }} && ({{ port_bf_in(reg, bf) }} == {{ zeros(bf.width) }})) begin
            {% elif 'e' in bf.hardware and 'lh' in bf.access -%}
        if ({{ port_bf_en(reg, bf) }} && ({{ port_bf_in(reg, bf)}} == {{ ones(bf.width) }})) begin
            {% elif 'll' in bf.access -%}
        if ({{ port_bf_in(reg, bf) }} == {{ zeros(bf.width) }}) begin
            {% elif 'lh' in bf.access -%}
        if ({{ port_bf_in(reg, bf) }} == {{ ones(bf.width) }}) begin
            {% elif 'e' in bf.hardware -%}
        if ({{ port_bf_en(reg, bf) }}) begin
            {% else -%}
        begin
            {%- endif %}
            {{ sig_bf_ff(reg, bf) }} <= {{ port_bf_in(reg, bf) }};
        {% elif 'i' in bf.hardware -%}
        begin
            {{ sig_bf_ff(reg, bf) }} <= {{ port_bf_in(reg, bf) }};
        {% elif 'sc' in bf.access -%}
        begin
            {{ sig_bf_ff(reg, bf) }} <= {{ zeros(bf.width) }};
        {% else -%}
        begin
            {{ sig_bf_ff(reg, bf) }} <= {{ sig_bf_ff(reg, bf) }};
        {% endif %}
        end
    end
end

        {% if 'r' in bf.access and 'q' in bf.hardware %}
reg {{ sig_bf_rvalid_ff(reg, bf) }};
{{ always_begin(sig=sig_bf_rvalid_ff(reg, bf)
)}} begin
        {{ sig_bf_rvalid_ff(reg, bf) }} <= {{ port_bf_rvalid(reg, bf) }};
    end
end
        {%endif%}

    {% endfor %}
{% endfor %}
//------------------------------------------------------------------------------
// Write ready
//------------------------------------------------------------------------------
{% set tmp.fifo_cnt = 0 %}
{% for reg in rmap %}
    {% for bf in reg %}
        {% if 'q' in bf.hardware and  'w' in bf.access %}
            {% if tmp.fifo_cnt == 0 %}
reg wready_drv;

always @(*) begin
    if ({{ sig_csr_wen(reg) }})
        wready_drv = {{ port_bf_wready(reg, bf) }};
            {% else %}
    else if ({{ sig_csr_wen(reg) }})
        wready_drv = {{ port_bf_wready(reg, bf) }};
            {% endif %}
            {% set tmp.fifo_cnt = tmp.fifo_cnt + 1 %}
        {% endif %}
    {% endfor %}
{% endfor %}
{% if tmp.fifo_cnt %}
    else
        wready_drv = 1'b1;
end

assign wready = wready_drv;
{% else %}
assign wready = 1'b1;
{% endif %}

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

//------------------------------------------------------------------------------
// Read data valid
//------------------------------------------------------------------------------
reg rvalid_ff;
{{ always_begin(sig='rvalid_ff'
)}} if (ren && rvalid) begin
        rvalid_ff <= 1'b0;
    end else if (ren) begin
        rvalid_ff <= 1'b1;
    end
end

{% set tmp.fifo_cnt = 0 %}
{% for reg in rmap %}
    {% for bf in reg %}
        {% if 'q' in bf.hardware and  'r' in bf.access %}
            {% if tmp.fifo_cnt == 0 %}
reg rvalid_drv;
always @(*) begin
    if ({{ sig_csr_ren_ff(reg) }})
        rvalid_drv = {{ sig_bf_rvalid_ff(reg, bf) }};
            {% else %}
    else if ({{ sig_csr_ren_ff(reg) }})
        rvalid_drv = {{ sig_bf_rvalid_ff(reg, bf) }};
            {% endif %}
            {% set tmp.fifo_cnt = tmp.fifo_cnt + 1 %}
        {% endif %}
    {% endfor %}
{% endfor %}
{% if tmp.fifo_cnt %}
    else
        rvalid_drv = rvalid_ff;
end

assign rvalid = rvalid_drv;
{% else %}
assign rvalid = rvalid_ff;
{% endif %}

endmodule
