#!/usr/bin/env python
"""
Generates a Crossbar with the specified number of slaves
"""

from __future__ import print_function

import argparse
import math
from jinja2 import Template

def main():
    parser = argparse.ArgumentParser(description=__doc__.strip())
    parser.add_argument('-s', '--nslaves',  type=int, default=2, help="number of slaves")
    parser.add_argument('-n', '--name',   type=str, help="module name")
    parser.add_argument('-o', '--output', type=str, help="output file name")

    args = parser.parse_args()

    try:
        generate(**args.__dict__)
    except IOError as ex:
        print(ex)
        exit(1)

def generate(nslaves=2, name=None, output=None):
    if name is None:
        name = "Crossbar{0}_wb".format(nslaves)

    if output is None:
        output = name + ".v"

    print("Opening file '{0}'...".format(output))

    output_file = open(output, 'w')

    print("Generating {0} port Wishbone arbiter {1}...".format(nslaves, name))

    

    t = Template(u"""/*
    Wishbone B-4 Crossbar
    *** Autogenerated file ***
*/

`default_nettype none
`include "Utils.vh"

module {{name}} #(
    parameter DATA_WIDTH = 32,
    parameter ADDR_WIDTH = 32,
    parameter SELECT_WIDTH = (DATA_WIDTH/8),
    {%- for p in nslaves %}
    parameter DEVICE{{p}}_ADDR = 32'h00000000,
    parameter DEVICE{{p}}_MASK = 32'h0000ff00{%- if p != lastslave%},{%- endif %}
    {%- endfor %}
)(
    // Wishbone slave port
    input  wire [ADDR_WIDTH-1:0]   wbs_adr_i,
    input  wire [DATA_WIDTH-1:0]   wbs_dat_i,
    output reg  [DATA_WIDTH-1:0]   wbs_dat_o,
    input  wire                    wbs_we_i,
    input  wire [SELECT_WIDTH-1:0] wbs_sel_i,
    input  wire                    wbs_stb_i,
    input  wire                    wbs_cyc_i,
    output wire                    wbs_ack_o,
    output wire                    wbs_err_o,
    
    {%- for p in nslaves %}
    
    // Wishbone master {{p}} port
    output wire [ADDR_WIDTH-1:0]   wbm{{p}}_adr_o,
    input  wire [DATA_WIDTH-1:0]   wbm{{p}}_dat_i,
    output wire [DATA_WIDTH-1:0]   wbm{{p}}_dat_o,
    output wire                    wbm{{p}}_we_o,
    output wire [SELECT_WIDTH-1:0] wbm{{p}}_sel_o,
    output reg                     wbm{{p}}_cyc_o,
    output wire                    wbm{{p}}_stb_o,
    input  wire                    wbm{{p}}_ack_i,
    input  wire                    wbm{{p}}_err_i{%- if p != lastslave%},{%- endif %}
    {%- endfor %}
);
    // Device selection
    localparam DEVICE_NONE = 'd0;
    {%- for p in nslaves %}
    localparam DEVICE_{{p}} = 'd{{p+1}};
    {%- endfor %}

    reg [{{w-1}}:0] selected_device;
    always @(*) begin
        selected_device = DEVICE_NONE;
        if (wbs_cyc_i) begin
            {%- for p in nslaves %}
            if ((wbs_adr_i & DEVICE{{p}}_MASK) == DEVICE{{p}}_ADDR)
                selected_device = DEVICE_{{p}};
            {%- endfor %}
        end
    end

    // Master Addr Out
    {%- for p in nslaves %}
    assign wbm{{p}}_adr_o = wbs_adr_i;
    {%- endfor %}

    // Master Data in (Muxed)
    always @(*) begin /* COMBINATORIAL */
        case(selected_device)
            {%- for p in nslaves %}
            DEVICE_{{p}}:    wbs_dat_o = wbm{{p}}_dat_i;
            {%- endfor %}
            default: begin
                wbs_dat_o = 32'hxxxxxxxx;
            end
        endcase
    end
    
    // Master Data Out
    {%- for p in nslaves %}
    assign wbm{{p}}_dat_o = wbs_dat_i;
    {%- endfor %}
                 
    // Master WE Out
    {%- for p in nslaves %}
    assign wbm{{p}}_we_o = wbs_we_i;
    {%- endfor %}
    
    // Master Sel Out
    {%- for p in nslaves %}
    assign wbm{{p}}_sel_o = wbs_sel_i;
    {%- endfor %}
                 
    
    /*
        === Cyc muxing ===
    */
    always @(*) begin /* COMBINATORIAL */
        // Defaults
        {%- for p in nslaves %}
        wbm{{p}}_cyc_o = 1'b0;
        {%- endfor %}
        
        case(selected_device)
            {%- for p in nslaves %}
            DEVICE_{{p}}: wbm{{p}}_cyc_o = wbs_cyc_i;
            {%- endfor %}
            
            default: begin
                {%- for p in nslaves %}
                wbm{{p}}_cyc_o = 1'b0;
                {%- endfor %}
            end
        endcase
    end

    // Stb Out
    {%- for p in nslaves %}
    assign wbm{{p}}_stb_o = wbm{{p}}_cyc_o & wbs_stb_i;
    {%- endfor %}
                 
    // ACK Out
    assign wbs_ack_o = {%- for p in nslaves %}
                       {%- if p != 0 %} 
                        | {%- endif %} wbm{{p}}_ack_i {%- if p == lastslave%};{%- endif %}
                       {%- endfor %}
                 
    // Error signal
    wire select_error = (selected_device == DEVICE_NONE);
    always @(*) begin
        if (select_error) begin 
            `debug($display("XBAR-SEL-ERR: Unknown Device Selected: 0x%x", wbs_adr_i);) 
        end
    end
    
    assign wbs_err_o = wbs_cyc_i & (select_error
                        {%- for p in nslaves %}
                        | wbm{{p}}_err_i{%- if p == lastslave%});{%- endif %}
                        {%- endfor %}
endmodule

""")
    
    output_file.write(t.render(
        n=nslaves,
        w=int(math.ceil(math.log(nslaves+1, 2))),
        name=name,
        nslaves=range(nslaves),
        lastslave=nslaves-1
    ))
    
    print("Done")

if __name__ == "__main__":
    main()


