dakies/OSS_Verilog huggingface dataset:
module pow_n_pipe_always
# (
    parameter w = 8, n = 5
)
(
    input                      clk,
    input                      rst_n,
    input                      arg_vld,
    input      [     w - 1:0 ] arg,
    output reg [ n     - 1:0 ] res_vld,
    output reg [ n * w - 1:0 ] res
);

    reg [ w - 1 : 0 ] arg_reg [ 1 : n - 1 ];
    reg [ w - 1 : 0 ] pow     [ 2 : n     ];
    reg [     1 : n ] arg_vld_reg;

    integer i;

    always @ (posedge clk or negedge rst_n)

        if (! rst_n)
        begin
            for (i = 1; i <= n; i = i + 1)
                arg_vld_reg [i] <= 1'b0;
        end
        else
        begin
            arg_vld_reg [1] <= arg_vld;

            for (i = 1; i <= n - 1; i = i + 1)
                arg_vld_reg [i + 1] <= arg_vld_reg [i];
        end

    always @ (posedge clk)
    begin
        arg_reg [1] <= arg;

        for (i = 1; i <= n - 2; i = i + 1)
            arg_reg [i + 1] <= arg_reg [i];

        pow [2] <= arg_reg [1] * arg_reg [1];

        for (i = 2; i <= n - 1; i = i + 1)
            pow [i + 1] <= pow [i] * arg_reg [i];
    end

    always @*
    begin
        for (i = 1; i <= n; i = i + 1)
            res_vld [n - i] = arg_vld_reg [i];

        res [(n - 1) * w +: w] = arg_reg [1];

        for (i = 2; i <= n; i = i + 1)
            res [(n - i) * w +: w] = pow [i];
    end

endmodule
module board_independent_wrapper
(
    input         fast_clk,
    input         slow_clk,
    input         rst_n,
    input         fast_clk_en,
    input  [ 3:0] key,
    input  [ 7:0] sw,
    output [ 7:0] led,
    output [ 7:0] disp_en,
    output [31:0] disp,
    output [ 7:0] disp_dot
);

    wire [ 4:0] res_vld;
    wire [39:0] res;

    pow_n_pipe_always
    # (.w (8), .n (5))
    i_pow_5
    (
        .clk     ( slow_clk ),
        .rst_n   ( rst_n    ),
        .arg_vld ( key [0]  ),
        .arg     ( sw       ),
        .res_vld ( res_vld  ),
        .res     ( res      )
    );

    assign disp_en  =
    {
        res_vld [3], res_vld [3],
        res_vld [2], res_vld [2],
        res_vld [1], res_vld [1],
        res_vld [0], res_vld [0]
    };

    assign disp = res [31:0];

    assign disp_dot = 8'b0;

endmodule
module pow_5_en_single_cycle_always
# (
    parameter w = 8
)
(
    input                clk,
    input                rst_n,
    input                clk_en,
    input                arg_vld,
    input      [w - 1:0] arg,
    output reg           res_vld,
    output reg [w - 1:0] res
);

    reg           arg_vld_q;
    reg [w - 1:0] arg_q;

    always @ (posedge clk or negedge rst_n)
        if (! rst_n)
            arg_vld_q <= 1'b0;
        else if (clk_en)
            arg_vld_q <= arg_vld;
    
    always @ (posedge clk)
        if (clk_en)
            arg_q <= arg;

    wire           res_vld_d = arg_vld_q;
    wire [w - 1:0] res_d     = arg_q  * arg_q * arg_q * arg_q * arg_q;

    always @ (posedge clk or negedge rst_n)
        if (! rst_n)
            res_vld <= 1'b0;
        else if (clk_en)
            res_vld <= res_vld_d;

    always @ (posedge clk)
        if (clk_en)
            res <= res_d;

endmodule
module board_independent_wrapper
(
    input         fast_clk,
    input         slow_clk,
    input         rst_n,
    input         fast_clk_en,
    input  [ 3:0] key,
    input  [ 7:0] sw,
    output [ 7:0] led,
    output [ 7:0] disp_en,
    output [31:0] disp,
    output [ 7:0] disp_dot
);

    wire res_vld;
    
    assign led  = { 8 { res_vld } };

    pow_5_en_single_cycle_always
    # (.w (8))
    i_pow_5
    (
        .clk     ( fast_clk    ),
        .rst_n   ( rst_n       ),
        .clk_en  ( fast_clk_en ),
        .arg_vld ( key [0]     ),
        .arg     ( sw          ),
        .res_vld ( res_vld     ),
        .res     ( disp [7:0]  )
    );
    
    assign disp_en  = 8'b00000011;
    assign disp_dot = { 7'b0000000, res_vld };

endmodule
module pow_5_en_pipe_always_with_array
# (
    parameter w = 8
)
(
    input                    clk,
    input                    rst_n,
    input                    clk_en,
    input                    arg_vld,
    input      [    w - 1:0] arg,
    output reg [        4:0] res_vld,
    output reg [5 * w - 1:0] res
);

    reg [w - 1:0] arg_reg [1:4];
    reg [w - 1:0] pow     [2:5];
    reg [    1:5] arg_vld_reg;

    integer i;

    always @ (posedge clk or negedge rst_n)

        if (! rst_n)
        begin
            for (i = 1; i <= 5; i = i + 1)
                arg_vld_reg [i] <= 1'b0;
        end
        else if (clk_en)
        begin
            arg_vld_reg [1] <= arg_vld;

            for (i = 1; i <= 4; i = i + 1)
                arg_vld_reg [i + 1] <= arg_vld_reg [i];
        end

    always @ (posedge clk)

        if (clk_en)
        begin
            arg_reg [1] <= arg;

            for (i = 1; i <= 3; i = i + 1)
                arg_reg [i + 1] <= arg_reg [i];

            pow [2] <= arg_reg [1] * arg_reg [1];

            for (i = 2; i <= 4; i = i + 1)
                pow [i + 1] <= pow [i] * arg_reg [i];
        end

    always @*
    begin
        for (i = 1; i <= 5; i = i + 1)
            res_vld [5 - i] = arg_vld_reg [i];

        res [(5 - 1) * w +: w] = arg_reg [1];

        for (i = 2; i <= 5; i = i + 1)
            res [(5 - i) * w +: w] = pow [i];
    end

endmodule
module board_independent_wrapper
(
    input         fast_clk,
    input         slow_clk,
    input         rst_n,
    input         fast_clk_en,
    input  [ 3:0] key,
    input  [ 7:0] sw,
    output [ 7:0] led,
    output [ 7:0] disp_en,
    output [31:0] disp,
    output [ 7:0] disp_dot
);

    wire [ 4:0] res_vld;
    wire [39:0] res;

    pow_5_en_pipe_always_with_array
    # (.w (8))
    i_pow_5
    (
        .clk     ( fast_clk    ),
        .rst_n   ( rst_n       ),
        .clk_en  ( fast_clk_en ),
        .arg_vld ( key [0]     ),
        .arg     ( sw          ),
        .res_vld ( res_vld     ),
        .res     ( res         )
    );

    assign disp_en  =
    {
        res_vld [3], res_vld [3],
        res_vld [2], res_vld [2],
        res_vld [1], res_vld [1],
        res_vld [0], res_vld [0]
    };

    assign disp = res [31:0];

    assign disp_dot = 8'b0;

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	    
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_single_cycle_struct pow_5_en_single_cycle_struct (KEY[0], KEY[3], KEY[1],  SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_multi_cycle_always pow_5_multi_cycle_always (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_pipe_always pow_5_en_pipe_always (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	    
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_single_cycle_struct pow_5_single_cycle_struct (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	    
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_n_en_pipe_struct pow_n_en_pipe_struct (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	    
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_pipe_struct pow_5_en_pipe_struct (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_single_cycle_always pow_5_single_cycle_always (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_multi_cycle_struct pow_5_multi_cycle_struct (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_pipe_always_with_array pow_5_pipe_always_with_array (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_n_pipe_struct pow_n_pipe_struct (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module strobe_generator
# (
    parameter w = 24
)
(
    input      clk,
    input      rst_n,
    output reg strobe
);

    reg [w - 1:0] cnt;

    always @ (posedge clk or negedge rst_n)
        if (! rst_n)
            cnt <= { w { 1'b0 } };
        else
            cnt <= cnt + 1'b1;

    always @ (posedge clk or negedge rst_n)
        if (! rst_n)
            strobe <= 1'b0;
        else
            strobe <= (cnt [w - 1:0] == 1'b0);

endmodule
module display_driver
(
    input        en,
    input  [3:0] dig,
    input        dot,
    output [7:0] _gfedcba
);

    // . g f e d c b a   // Letter from the diagram below
    // 7 6 5 4 3 2 1 0   // Bit in _gfedcba

    //   --a--
    //  |     |
    //  f     b
    //  |     |
    //   --g--
    //  |     |
    //  e     c
    //  |     |
    //   --d-- 

    reg [6:0] gfedcba;

    always @*
        if (! en)
            gfedcba = 7'b1111111;
        else
            case (dig)
            4'h0: gfedcba = 7'b1000000;
            4'h1: gfedcba = 7'b1111001;
            4'h2: gfedcba = 7'b0100100;
            4'h3: gfedcba = 7'b0110000;
            4'h4: gfedcba = 7'b0011001;
            4'h5: gfedcba = 7'b0010010;
            4'h6: gfedcba = 7'b0000010;
            4'h7: gfedcba = 7'b1111000;
            4'h8: gfedcba = 7'b0000000;
            4'h9: gfedcba = 7'b0010000;
            4'ha: gfedcba = 7'b0001000;
            4'hb: gfedcba = 7'b0000011;
            4'hc: gfedcba = 7'b1000110;
            4'hd: gfedcba = 7'b0100001;
            4'he: gfedcba = 7'b0000110;
            4'hf: gfedcba = 7'b0001110;
            endcase

    assign _gfedcba = { ~ dot, gfedcba };

endmodule
module clk_divider
# (
    parameter w = 24
)
(
    input  clk,
    input  rst_n,
    output divided_clk
);

    reg [w - 1:0] cnt;

    always @ (posedge clk or negedge rst_n)
        if (! rst_n)
            cnt <= { w { 1'b0 } };
        else
            cnt <= cnt + 1'b1;

    assign divided_clk = cnt [w - 1];

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	    
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_multi_cycle_struct pow_5_en_multi_cycle_struct(KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);


endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_multi_cycle_always pow_5_en_multi_cycle_always (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_pipe_always pow_5_pipe_always (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_n_en_pipe_always pow_n_en_pipe_always (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_pipe_struct pow_5_pipe_struct (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
    output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_multi_cycle_struct_todo pow_5_multi_cycle_struct_todo (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_n_pipe_always pow_n_pipe_always (KEY[0], KEY[3], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
	 
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_single_cycle_always pow_5_en_single_cycle_always (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9], LEDR[7:0]);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    pow_5_en_pipe_always_with_array pow_5_en_pipe_always_with_array (KEY[0], KEY[3], KEY[1], SW[9], SW[7:0], LEDR[9:5], LEDR[4:0]);

endmodule
module isqrt_slice_reg
(
    input             clock,
    input             reset_n,
    input      [31:0] ix,
    input      [31:0] iy,
    output reg [31:0] ox,
    output reg [31:0] oy
);

    parameter [31:0] m = 32'h4000_0000;

    wire [31:0] cox, coy;

    isqrt_slice_comb #(.m (m)) i
    (
        .ix ( ix  ),
        .iy ( iy  ),
        .ox ( cox ),
        .oy ( coy )
    );

    always @(posedge clock or negedge reset_n)
    begin
        if (! reset_n)
        begin
            ox <= 0;
            oy <= 0;
        end
        else
        begin
            ox <= cox;
            oy <= coy;
        end
    end

endmodule
module testbench;

    localparam [4:0] n_pipe_stages = 16;

    reg         clock;
    reg         reset_n;
    reg  [31:0] x;
    wire [15:0] y;

    initial
    begin
        clock = 1;

        forever
            # 50 clock = ! clock;
    end

    initial
    begin
        reset_n <= 0;
        repeat (20) @(posedge clock);
        reset_n <= 1;
    end

    reg [31:0] fifo [0 : n_pipe_stages - 1];
    integer fifo_next_index; initial fifo_next_index = 0;

    isqrt isqrt
    (
        .clock   ( clock   ),
        .reset_n ( reset_n ),
        .x       ( x       ),
        .y       ( y       )
    );

    task check (input [31:0] x, input [32:0] y);
    begin
        $display ("sqrt (%d) = %d    %d <= %d < %d",
            x, y, y ** 2, x, (y + 1) ** 2);

        if (y ** 2 > x)
            $display ("Error! y ** 2 > x");

        if (x >= (y + 1) ** 2)
            $display ("Error! x >= (y + 1) ** 2");
    end
    endtask

    task test (input [31:0] i);
        reg [31:0] oldest_x_in_fifo;
    begin
        x <= i;

        oldest_x_in_fifo = fifo [fifo_next_index];
        fifo [fifo_next_index] = i;

        fifo_next_index = fifo_next_index + 1;

        if (fifo_next_index == n_pipe_stages)
            fifo_next_index = 0;

        @(posedge clock);
        check (oldest_x_in_fifo, y);
    end
    endtask

    integer i;

    initial
    begin
        $dumpvars;

        @(posedge reset_n);
        @(posedge clock);

        for (i = 0; i < 256; i = i + 1)
            test (i);

        for (i = 0; i < 256; i = i + 1)
            test ('hFFFF_FFFF - i);

        for (i = 0; i < 256; i = i + 1)
            test ($random);

        $finish;
    end

endmodule
module isqrt_slice_comb
(
    input  [31:0] ix,
    input  [31:0] iy,
    output [31:0] ox,
    output [31:0] oy
);

    parameter [31:0] m = 32'h4000_0000;

    wire [31:0] b      = iy | m;
    wire        x_ge_b = ix >= b;

    assign ox = x_ge_b ? ix - b : ix;
    assign oy = (iy >> 1) | (x_ge_b ? m : 0);

endmodule
module isqrt
(
    input         clock,
    input         reset_n,
    input  [31:0] x,
    output [15:0] y
);

    localparam [31:0] m = 32'h4000_0000;

    wire [31:0] ix [0:15], iy [0:15];
    wire [31:0] ox [0:15], oy [0:15];

    generate
        genvar i;

        for (i = 0; i < 16; i = i + 1)
        begin : u
            isqrt_slice_reg #(.m (m >> i * 2)) inst
            (
                .clock   ( clock   ),
                .reset_n ( reset_n ),
                .ix      ( ix [i]  ),
                .iy      ( iy [i]  ),
                .ox      ( ox [i]  ),
                .oy      ( oy [i]  )
            );
        end

        for (i = 1; i < 16; i = i + 1)
        begin : v
            assign ix [i] = ox [i - 1];
            assign iy [i] = oy [i - 1];
        end

    endgenerate


    assign ix [0] = x;
    assign iy [0] = 0;

    assign y = oy [15];

endmodule
module testbench;

    reg  [31:0] x;
    wire [15:0] y;

    isqrt isqrt (x, y);

    task check (input [31:0] x, input [32:0] y);  // Note that [32:0] is important to avoid overflow
    begin
        $display ("sqrt (%d) = %d    %d <= %d < %d",
            x, y, y ** 2, x, (y + 1) ** 2);

        if (y ** 2 > x)
            $display ("Error! y ** 2 > x");

        if (x >= (y + 1) ** 2)
            $display ("Error! x >= (y + 1) ** 2");
    end
    endtask

    task test (input [31:0] i);
    begin
        #1;
        x <= i;
        #1;
        check (x, y);
    end
    endtask

    integer i;

    initial
    begin
        $dumpvars;

        for (i = 0; i < 256; i = i + 1)
            test (i);

        for (i = 0; i < 256; i = i + 1)
            test ('hFFFF_FFFF - i);

        for (i = 0; i < 256; i = i + 1)
            test ($random);

        $finish;
    end

endmodule
module isqrt
(
    input      [31:0] x,
    output reg [15:0] y
);

    reg [31:0] m, tx, ty, b;

    always @*
    begin
        m  = 31'h4000_0000;
        tx = x;
        ty = 0;
    
        repeat (16)
        begin
            b  = ty |  m;
            ty = ty >> 1;
            
            if (tx >= b)
            begin
                tx = tx - b;
                ty = ty | m;
            end
            
            m = m >> 2;
        end

        y = ty [15:0];
    end

endmodule
module isqrt_slice
(
    input  [31:0] ix,
    input  [31:0] iy,
    output [31:0] ox,
    output [31:0] oy
);

    parameter [31:0] m = 32'h4000_0000;

    wire [31:0] b      = iy | m;
    wire        x_ge_b = ix >= b;

    assign ox = x_ge_b ? ix - b : ix;
    assign oy = (iy >> 1) | (x_ge_b ? m : 0);

endmodule
module isqrt
(
    input  [31:0] x,
    output [15:0] y
);

    localparam [31:0] m = 32'h4000_0000;

    wire [31:0] ix [0:15], iy [0:15];
    wire [31:0] ox [0:15], oy [0:15];

    isqrt_slice #(.m (m >>  0 * 2)) i00 (.ix (ix [ 0]), .iy (iy [ 0]), .ox (ox [ 0]), .oy (oy [ 0]));
    isqrt_slice #(.m (m >>  1 * 2)) i01 (.ix (ix [ 1]), .iy (iy [ 1]), .ox (ox [ 1]), .oy (oy [ 1]));
    isqrt_slice #(.m (m >>  2 * 2)) i02 (.ix (ix [ 2]), .iy (iy [ 2]), .ox (ox [ 2]), .oy (oy [ 2]));
    isqrt_slice #(.m (m >>  3 * 2)) i03 (.ix (ix [ 3]), .iy (iy [ 3]), .ox (ox [ 3]), .oy (oy [ 3]));
    isqrt_slice #(.m (m >>  4 * 2)) i04 (.ix (ix [ 4]), .iy (iy [ 4]), .ox (ox [ 4]), .oy (oy [ 4]));
    isqrt_slice #(.m (m >>  5 * 2)) i05 (.ix (ix [ 5]), .iy (iy [ 5]), .ox (ox [ 5]), .oy (oy [ 5]));
    isqrt_slice #(.m (m >>  6 * 2)) i06 (.ix (ix [ 6]), .iy (iy [ 6]), .ox (ox [ 6]), .oy (oy [ 6]));
    isqrt_slice #(.m (m >>  7 * 2)) i07 (.ix (ix [ 7]), .iy (iy [ 7]), .ox (ox [ 7]), .oy (oy [ 7]));
    isqrt_slice #(.m (m >>  8 * 2)) i08 (.ix (ix [ 8]), .iy (iy [ 8]), .ox (ox [ 8]), .oy (oy [ 8]));
    isqrt_slice #(.m (m >>  9 * 2)) i09 (.ix (ix [ 9]), .iy (iy [ 9]), .ox (ox [ 9]), .oy (oy [ 9]));
    isqrt_slice #(.m (m >> 10 * 2)) i10 (.ix (ix [10]), .iy (iy [10]), .ox (ox [10]), .oy (oy [10]));
    isqrt_slice #(.m (m >> 11 * 2)) i11 (.ix (ix [11]), .iy (iy [11]), .ox (ox [11]), .oy (oy [11]));
    isqrt_slice #(.m (m >> 12 * 2)) i12 (.ix (ix [12]), .iy (iy [12]), .ox (ox [12]), .oy (oy [12]));
    isqrt_slice #(.m (m >> 13 * 2)) i13 (.ix (ix [13]), .iy (iy [13]), .ox (ox [13]), .oy (oy [13]));
    isqrt_slice #(.m (m >> 14 * 2)) i14 (.ix (ix [14]), .iy (iy [14]), .ox (ox [14]), .oy (oy [14]));
    isqrt_slice #(.m (m >> 15 * 2)) i15 (.ix (ix [15]), .iy (iy [15]), .ox (ox [15]), .oy (oy [15]));

    assign ix [ 0] = x;
    assign ix [ 1] = ox [ 0];
    assign ix [ 2] = ox [ 1];
    assign ix [ 3] = ox [ 2];
    assign ix [ 4] = ox [ 3];
    assign ix [ 5] = ox [ 4];
    assign ix [ 6] = ox [ 5];
    assign ix [ 7] = ox [ 6];
    assign ix [ 8] = ox [ 7];
    assign ix [ 9] = ox [ 8];
    assign ix [10] = ox [ 9];
    assign ix [11] = ox [10];
    assign ix [12] = ox [11];
    assign ix [13] = ox [12];
    assign ix [14] = ox [13];
    assign ix [15] = ox [14];

    assign iy [ 0] = 0;
    assign iy [ 1] = oy [ 0];
    assign iy [ 2] = oy [ 1];
    assign iy [ 3] = oy [ 2];
    assign iy [ 4] = oy [ 3];
    assign iy [ 5] = oy [ 4];
    assign iy [ 6] = oy [ 5];
    assign iy [ 7] = oy [ 6];
    assign iy [ 8] = oy [ 7];
    assign iy [ 9] = oy [ 8];
    assign iy [10] = oy [ 9];
    assign iy [11] = oy [10];
    assign iy [12] = oy [11];
    assign iy [13] = oy [12];
    assign iy [14] = oy [13];
    assign iy [15] = oy [14];

    assign y       = oy [15];

endmodule
module testbench;

    localparam [4:0] n_pipe_stages = `N_PIPE_STAGES;

    reg         clock;
    reg         reset_n;
    reg  [31:0] x;
    wire [15:0] y;

    initial
    begin
        clock = 1;

        forever
            # 50 clock = ! clock;
    end

    initial
    begin
        reset_n <= 0;
        repeat (20) @(posedge clock);
        reset_n <= 1;
    end

    reg [31:0] fifo [0 : n_pipe_stages - 1];
    integer fifo_next_index; initial fifo_next_index = 0;

    isqrt
    # (
        .n_pipe_stages (n_pipe_stages)
    )
    isqrt
    (
        .clock   ( clock   ),
        .reset_n ( reset_n ),
        .x       ( x       ),
        .y       ( y       )
    );

    task check (input [31:0] x, input [32:0] y);
    begin
        $display ("check sqrt (%d) = %d    %d <= %d < %d",
            x, y, y ** 2, x, (y + 1) ** 2);

        if (y ** 2 > x)
            $display ("Error! y ** 2 > x");

        if (x >= (y + 1) ** 2)
            $display ("Error! x >= (y + 1) ** 2");
    end
    endtask

    task test (input [31:0] i);
        reg [31:0] oldest_x_in_fifo;
    begin
        x <= i;

        oldest_x_in_fifo = fifo [fifo_next_index];
        fifo [fifo_next_index] = i;

        fifo_next_index = fifo_next_index + 1;

        if (fifo_next_index == n_pipe_stages)
            fifo_next_index = 0;

        @(posedge clock);

        $write ("current: x %d  |  ", x);
        check (oldest_x_in_fifo, y);
    end
    endtask

    integer i;

    initial
    begin
        $dumpvars;

        @(posedge reset_n);
        @(posedge clock);

        for (i = 0; i < 256; i = i + 1)
            test (i);

        for (i = 0; i < 256; i = i + 1)
            test ('hFFFF_FFFF - i);

        for (i = 0; i < 256; i = i + 1)
            test ($random);

        $finish;
    end

endmodule
module isqrt
(
    input         clock,
    input         reset_n,
    input  [31:0] x,
    output [15:0] y
);

    parameter n_pipe_stages = 16;

    localparam n_slices           = 16;
    localparam n_slices_per_stage = n_slices / n_pipe_stages;

    localparam [31:0] m = 32'h4000_0000;

    wire [31:0] ix [0:15], iy [0:15];
    wire [31:0] ox [0:15], oy [0:15];

    generate
        genvar i;

        for (i = 0; i < 16; i = i + 1)
        begin : u
            if (i % n_slices_per_stage != n_slices_per_stage - 1)
            begin
                isqrt_slice_comb #(.m (m >> i * 2)) inst
                (
                    .ix      ( ix [i]  ),
                    .iy      ( iy [i]  ),
                    .ox      ( ox [i]  ),
                    .oy      ( oy [i]  )
                );
            end
            else
            begin
                isqrt_slice_reg #(.m (m >> i * 2)) inst
                (
                    .clock   ( clock   ),
                    .reset_n ( reset_n ),
                    .ix      ( ix [i]  ),
                    .iy      ( iy [i]  ),
                    .ox      ( ox [i]  ),
                    .oy      ( oy [i]  )
                );
            end
        end

        for (i = 1; i < 16; i = i + 1)
        begin : v
            assign ix [i] = ox [i - 1];
            assign iy [i] = oy [i - 1];
        end

    endgenerate


    assign ix [0] = x;
    assign iy [0] = 0;

    assign y = oy [15];

endmodule
module isqrt
(
    input  [31:0] x,
    output [15:0] y
);

    localparam [31:0] m = 32'h4000_0000;

    wire [31:0] wx [0:16], wy [0:16];

    assign wx [0] = x;
    assign wy [0] = 0;

    isqrt_slice #(m >>  0 * 2) i00 (wx [ 0], wy [ 0], wx [ 1], wy [ 1]);
    isqrt_slice #(m >>  1 * 2) i01 (wx [ 1], wy [ 1], wx [ 2], wy [ 2]);
    isqrt_slice #(m >>  2 * 2) i02 (wx [ 2], wy [ 2], wx [ 3], wy [ 3]);
    isqrt_slice #(m >>  3 * 2) i03 (wx [ 3], wy [ 3], wx [ 4], wy [ 4]);
    isqrt_slice #(m >>  4 * 2) i04 (wx [ 4], wy [ 4], wx [ 5], wy [ 5]);
    isqrt_slice #(m >>  5 * 2) i05 (wx [ 5], wy [ 5], wx [ 6], wy [ 6]);
    isqrt_slice #(m >>  6 * 2) i06 (wx [ 6], wy [ 6], wx [ 7], wy [ 7]);
    isqrt_slice #(m >>  7 * 2) i07 (wx [ 7], wy [ 7], wx [ 8], wy [ 8]);
    isqrt_slice #(m >>  8 * 2) i08 (wx [ 8], wy [ 8], wx [ 9], wy [ 9]);
    isqrt_slice #(m >>  9 * 2) i09 (wx [ 9], wy [ 9], wx [10], wy [10]);
    isqrt_slice #(m >> 10 * 2) i10 (wx [10], wy [10], wx [11], wy [11]);
    isqrt_slice #(m >> 11 * 2) i11 (wx [11], wy [11], wx [12], wy [12]);
    isqrt_slice #(m >> 12 * 2) i12 (wx [12], wy [12], wx [13], wy [13]);
    isqrt_slice #(m >> 13 * 2) i13 (wx [13], wy [13], wx [14], wy [14]);
    isqrt_slice #(m >> 14 * 2) i14 (wx [14], wy [14], wx [15], wy [15]);
    isqrt_slice #(m >> 15 * 2) i15 (wx [15], wy [15], wx [16], wy [16]);

    assign y = wy [16];

endmodule
module testbench;

    reg         clock;
    reg         reset_n;
    reg         run;
    reg  [31:0] x;
    wire        ready;
    wire [15:0] y;

    initial
    begin
        clock = 1;

        forever
            # 50 clock = ! clock;
    end

    initial
    begin
        reset_n <= 0;
        repeat (20) @(posedge clock);
        reset_n <= 1;
    end

    isqrt isqrt
    (
        .clock   ( clock   ),
        .reset_n ( reset_n ),
        .run     ( run     ),
        .x       ( x       ),
        .ready   ( ready   ),
        .y       ( y       )
    );

    task check (input [31:0] x, input [32:0] y);
    begin
        $display ("sqrt (%d) = %d    %d <= %d < %d",
            x, y, y ** 2, x, (y + 1) ** 2);

        if (y ** 2 > x)
            $display ("Error! y ** 2 > x");

        if (x >= (y + 1) ** 2)
            $display ("Error! x >= (y + 1) ** 2");
    end
    endtask

    task test (input [31:0] i);
    begin
        x   <= i;
        run <= 1;

        @(posedge clock);

        run <= 0;

        @(posedge clock);

        while (! ready)
            @(posedge clock);

        check (x, y);
    end
    endtask

    integer i;

    initial
    begin
        $dumpvars;

        @(posedge reset_n);
        @(posedge clock);

        for (i = 0; i < 256; i = i + 1)
            test (i);

        for (i = 0; i < 256; i = i + 1)
            test ('hFFFF_FFFF - i);

        for (i = 0; i < 256; i = i + 1)
            test ($random);

        $finish;
    end

endmodule
module isqrt
(
    input             clock,
    input             reset_n,
    input             run,
    input      [31:0] x,
    output reg        ready,
    output reg [15:0] y
);

    reg [31:0] m,  r_m;
    reg [31:0] tx, r_tx;
    reg [31:0] ty, r_ty;
    reg [31:0] b,  r_b;

    reg new_ready;

    always @*
    begin
        if (run)
        begin
            m  = 31'h4000_0000;
            tx = x;
            ty = 0;
        end
        else
        begin
            m  = r_m;
            tx = r_tx;
            ty = r_ty;
        end
    
        b  = ty |  m;
        ty = ty >> 1;
            
        if (tx >= b)
        begin
            tx = tx - b;
            ty = ty | m;
        end

        new_ready = m [0];
            
        m = m >> 2;
    end

    always @(posedge clock or negedge reset_n)
    begin
        if (! reset_n)
        begin
            ready <= 0;
            y     <= 0;
        end
        else if (new_ready)
        begin
            ready <= 1;
            y     <= ty [15:0];
        end
        else
        begin
            ready <= 0;

            r_m   <= m;
            r_tx  <= tx;
            r_ty  <= ty;
            r_b   <= b;

        end
    end

endmodule
module isqrt
(
    input  [31:0] x,
    output [15:0] y
);

    localparam [31:0] m = 32'h4000_0000;

    wire [31:0] ix [0:15], iy [0:15];
    wire [31:0] ox [0:15], oy [0:15];

    generate
        genvar i;

        for (i = 0; i < 16; i = i + 1)
        begin : u
            isqrt_slice #(.m (m >> i * 2)) inst
            (
                .ix (ix [i]),
                .iy (iy [i]),
                .ox (ox [i]),
                .oy (oy [i])
            );
        end

        for (i = 1; i < 16; i = i + 1)
        begin : v
            assign ix [i] = ox [i - 1];
            assign iy [i] = oy [i - 1];
        end

    endgenerate


    assign ix [0] = x;
    assign iy [0] = 0;

    assign y = oy [15];

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    isqrt isqrt (KEY[0], KEY[3], SW, LEDR);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    isqrt isqrt (SW, LEDR);

endmodule
module top (SW, KEY, LEDR);

	 input wire [9:0] SW;        // DE-series switches
    input wire [3:0] KEY;       // DE-series pushbuttons
   
	 output wire [9:0] LEDR;     // DE-series LEDs   

    isqrt isqrt (KEY[0], KEY[3], KEY[1], SW, LEDR[9], LEDR[8:0]);

endmodule
module roma
#(parameter DATA_WIDTH=8, 
parameter SIZE=16)
(
input clk,
output reg [DATA_WIDTH*SIZE-1:0] data_rom
);

genvar t;
reg [DATA_WIDTH-1:0] rom [SIZE-1:0];
wire [DATA_WIDTH*SIZE-1:0] rom_wire;

initial
begin
$readmemh ("D:/7th_Semester_MIEM/Romanoff/sys_array_basic/a_data.hex", rom);
end

generate
for (t=0;t<SIZE;t=t+1) begin: generate_roma
assign rom_wire[DATA_WIDTH*t +: DATA_WIDTH] = rom[t];
end
endgenerate

always @(posedge clk) 
begin 
data_rom <= rom_wire;
end

endmodule
module shift_reg

#(parameter DATA_WIDTH=8,
parameter LENGTH=4)

(

input clock,
input reset_n,
input [1:0] ctrl_code,

input [DATA_WIDTH*LENGTH-1:0] data_in,
input [DATA_WIDTH-1:0] data_write,
output reg [DATA_WIDTH-1:0] data_read,
output reg [DATA_WIDTH*LENGTH-1:0] data_out

);

localparam REG_UPLOAD = 0,
                REG_LOAD = 1,
                REG_WRITE = 2,
                REG_READ = 3; //Gray coding of states

reg [DATA_WIDTH*LENGTH-1:0] contents;

always @(posedge clock)
begin
    if (~reset_n)
    begin
        contents <= {DATA_WIDTH*LENGTH{1'b0}};
    end
    else
    begin
    case (ctrl_code)
    REG_UPLOAD: data_out <= contents;
    REG_LOAD: contents <= data_in;
    REG_READ: begin contents <= {contents[DATA_WIDTH-1:0],contents[DATA_WIDTH*LENGTH-1:DATA_WIDTH]}; data_read <= contents[DATA_WIDTH-1:0]; end
    REG_WRITE: contents <= {data_write,contents[DATA_WIDTH*LENGTH-1:DATA_WIDTH]};
    endcase
    end

end

endmodule
module romb
#(parameter DATA_WIDTH=8, 
parameter SIZE=16)
(
input clk,
output reg [DATA_WIDTH*SIZE-1:0] data_rom
);

genvar t;
reg [DATA_WIDTH-1:0] rom [SIZE-1:0];
wire [DATA_WIDTH*SIZE-1:0] rom_wire;

initial
begin
$readmemh ("D:/7th_Semester_MIEM/Romanoff/sys_array_basic/b_data.hex", rom);
end

generate
for (t=0;t<SIZE;t=t+1) begin: generate_romb
assign rom_wire[DATA_WIDTH*t +: DATA_WIDTH] = rom[t];
end
endgenerate

always @(posedge clk) 
begin 
data_rom <= rom_wire;
end

endmodule
module sys_array_cell

#(parameter DATA_WIDTH=8)

(
    input  clock,
    input  reset_n,
	 input  param_load,

    input  [DATA_WIDTH - 1:0] input_data,
	 input  [2*DATA_WIDTH-1:0] prop_data,
	 input  [DATA_WIDTH-1:0] param_data,
    output reg [2*DATA_WIDTH-1:0] out_data,
    output reg [DATA_WIDTH-1:0] prop_param
);

reg [DATA_WIDTH-1:0] param;

    always @(posedge clock)
    begin
        if (~reset_n)
        begin
				out_data <= {2*DATA_WIDTH{1'b0}};
                                param <= {DATA_WIDTH{1'b0}};
        end
        else if (param_load) 
		      begin
				param <= param_data;
				end
		  else
		  begin
		  out_data<= prop_data+input_data*param;
		  prop_param <= input_data;
		  end	   
    end

endmodule
module seg7_tohex
(
input [3:0] code,
output reg [7:0] hexadecimal
);

always @ (*)
begin
case (code)
4'b0000: hexadecimal <= 8'b11000000;//all inverted for outputs
4'b0001: hexadecimal <= 8'b11111001;//
4'b0010: hexadecimal <= 8'b10100100;//
4'b0011: hexadecimal <= 8'b10110000;//
4'b0100: hexadecimal <= 8'b10011001;//
4'b0101: hexadecimal <= 8'b10010010;//
4'b0110: hexadecimal <= 8'b10000010;//
4'b0111: hexadecimal <= 8'b11111000;//
4'b1000: hexadecimal <= 8'b10000000;//
4'b1001: hexadecimal <= 8'b10010000;//
4'b1010: hexadecimal <= 8'b10001000;//
4'b1011: hexadecimal <= 8'b10000011;//
4'b1100: hexadecimal <= 8'b11000110;//
4'b1101: hexadecimal <= 8'b10100001;//
4'b1110: hexadecimal <= 8'b10000110;//
4'b1111: hexadecimal <= 8'b10001110;//
default: hexadecimal <= 8'b11111111;//
endcase
end

endmodule
module sys_array_basic

#(parameter DATA_WIDTH=8,
parameter ARRAY_W=4, //i
parameter ARRAY_L=4) //j

(
    input  clk,
    input  reset_n,
    input  param_load,

    input [DATA_WIDTH*ARRAY_L-1:0] input_module,
    input [DATA_WIDTH*ARRAY_W*ARRAY_L-1:0] parameter_data,
    output [2*DATA_WIDTH*ARRAY_W-1:0] out_module
);

wire [2*DATA_WIDTH*ARRAY_W*ARRAY_L-1:0] output_data;

wire [DATA_WIDTH*ARRAY_W*ARRAY_L-1:0] propagate_module;

/*reg [DATA_WIDTH-1:0] param;

    always @(posedge clock)
    begin
        if (~reset_n)
        begin
				out_data <= 8'b00000000;
        end
        else if (param_load) 
		      begin
				param <= param_data;
				end
		  else
		  begin
		  out_data<= input_data*param+prop_data;
		  end	   
    end */

genvar i;
genvar j;
genvar t;

generate

for(i=0;i<ARRAY_W;i=i+1) begin : generate_array_proc
 for (j=0;j<ARRAY_L;j=j+1) begin : generate_array_proc2
 
 if ((i === 0) && (j===0))
 begin
 sys_array_cell #(.DATA_WIDTH(DATA_WIDTH)) cell_inst ( 
 .clock(clk),
 .reset_n(reset_n),
 .param_load(param_load),
 .input_data(input_module[DATA_WIDTH-1:0]),
 .prop_data({2*DATA_WIDTH{1'b0}}),
 .param_data(parameter_data[DATA_WIDTH-1:0]),
 .out_data(output_data[2*DATA_WIDTH-1:0]),
 .prop_param(propagate_module[DATA_WIDTH-1:0])
 );
 end
 else if (i === 0)
 begin
 sys_array_cell #(.DATA_WIDTH(DATA_WIDTH)) cell_inst (
 .clock(clk),
 .reset_n(reset_n),
 .param_load(param_load),
 .input_data(input_module[DATA_WIDTH*(j+1)-1:DATA_WIDTH*j]), //
 .prop_data(output_data[2*DATA_WIDTH*(ARRAY_L*i+j)-1:2*DATA_WIDTH*(ARRAY_L*i+j-1)]), //
 .param_data(parameter_data[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)]), //
 .out_data(output_data[2*DATA_WIDTH*(ARRAY_L*i+j+1)-1:2*DATA_WIDTH*(ARRAY_L*i+j)]),
 .prop_param(propagate_module[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)])//
 );
 end
 else if (j === 0)
 begin
 sys_array_cell #(.DATA_WIDTH(DATA_WIDTH)) cell_inst (
 .clock(clk),
 .reset_n(reset_n),
 .param_load(param_load),
 .input_data(propagate_module[DATA_WIDTH*(ARRAY_L*(i-1)+j+1)-1:DATA_WIDTH*(ARRAY_L*(i-1)+j)]),//
 .prop_data({2*DATA_WIDTH{1'b0}}), //
 .param_data(parameter_data[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)]), //
 .out_data(output_data[2*DATA_WIDTH*(ARRAY_L*i+j+1)-1:2*DATA_WIDTH*(ARRAY_L*i+j)]),
 .prop_param(propagate_module[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)])//
 );
 end
 else
 begin
 sys_array_cell #(.DATA_WIDTH(DATA_WIDTH)) cell_inst (
 .clock(clk),
 .reset_n(reset_n),
 .param_load(param_load),
 .input_data(propagate_module[DATA_WIDTH*(ARRAY_L*(i-1)+j+1)-1:DATA_WIDTH*(ARRAY_L*(i-1)+j)]),//
 .prop_data(output_data[2*DATA_WIDTH*(ARRAY_L*i+j)-1:2*DATA_WIDTH*(ARRAY_L*i+j-1)]), //
 .param_data(parameter_data[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)]), //
 .out_data(output_data[2*DATA_WIDTH*(ARRAY_L*i+j+1)-1:2*DATA_WIDTH*(ARRAY_L*i+j)]),
 .prop_param(propagate_module[DATA_WIDTH*(ARRAY_L*i+j+1)-1:DATA_WIDTH*(ARRAY_L*i+j)])//
 );
 end
 
 end
end

endgenerate

generate

for (t=0;t<ARRAY_W;t=t+1) begin: output_prop
assign out_module[2*DATA_WIDTH*(t+1)-1:2*DATA_WIDTH*t] = output_data[2*DATA_WIDTH*(ARRAY_L*(t+1))-1:2*DATA_WIDTH*(ARRAY_L*(t+1)-1)];
end

endgenerate

endmodule
module sys_array_wrapper 

#(parameter DATA_WIDTH=8,
parameter ARRAY_W=4, //i
parameter ARRAY_L=4, //j
parameter CLOCK_DIVIDE=25) 

(
    input  clk,
    input  reset_n,
    input  load_params,
    input  start_comp,
    
	 output [4*DATA_WIDTH-1:0] hex_connect
);

localparam MEM_SIZE=ARRAY_W*ARRAY_L;
localparam ARRAY_SIZE=ARRAY_W*ARRAY_W;
localparam NUM_HEX = DATA_WIDTH >> 1; //number of hex modules needed to display a result (DW / 2)

wire clk_div, ready;
wire [2*DATA_WIDTH - 1:0] output_value, empty_data_write;
wire [DATA_WIDTH*ARRAY_W*ARRAY_L - 1:0] input_data_a, input_data_b;
wire [2*DATA_WIDTH*ARRAY_W*ARRAY_W-1:0] outputs_fetcher, empty_data_out;

reg [CLOCK_DIVIDE : 0] small_cnt;
reg [1:0] propagate_reg_ctrl; //control the output of propagate parallel to serial converter register

genvar ii;

roma #(.DATA_WIDTH(DATA_WIDTH), .SIZE(MEM_SIZE)) rom_instance_a (.clk(clk), .data_rom(input_data_a));
romb #(.DATA_WIDTH(DATA_WIDTH), .SIZE(MEM_SIZE)) rom_instance_b (.clk(clk), .data_rom(input_data_b));

sys_array_fetcher #(.DATA_WIDTH(DATA_WIDTH), .ARRAY_W(ARRAY_W), .ARRAY_L(ARRAY_L)) fetching_unit
(
.clock(clk),
.reset_n(reset_n),
.load_params(~load_params),
.start_comp(~start_comp),
.input_data_b(input_data_b),
.input_data_a(input_data_a),
.ready(ready),
.out_data(outputs_fetcher)
);

clock_divider #(.DIVIDE_LEN(CLOCK_DIVIDE)) clock_divaaa (.clock(clk), .div_clock(clk_div));

shift_reg #(.DATA_WIDTH(2*DATA_WIDTH), .LENGTH(ARRAY_SIZE)) propagate_reg
(
.clock(clk_div),
.reset_n(reset_n),
.ctrl_code(propagate_reg_ctrl),
.data_in(outputs_fetcher),
.data_read(output_value),
.data_out(empty_data_out),
.data_write(empty_data_write)
);

generate
for (ii=0; ii<NUM_HEX; ii=ii+1) begin : generate_hexes
seg7_tohex hex_converter_i (.code(output_value[4*ii +: 4]), .hexadecimal(hex_connect[8*ii +: 8]));
end
endgenerate

always @(posedge clk)
begin

if (~reset_n) begin propagate_reg_ctrl <= 2'b00; small_cnt <= {(CLOCK_DIVIDE+1){1'b0}}; end

else begin

case (ready)

1'b0: propagate_reg_ctrl <= 2'b01;
1'b1: begin 
	if (small_cnt[CLOCK_DIVIDE] == 1'b0) begin propagate_reg_ctrl <= 2'b01; small_cnt <= small_cnt+1'b1; end
	else propagate_reg_ctrl <= 2'b11;
	end
default: propagate_reg_ctrl <= 2'b00;

endcase
end

end
endmodule
module clock_divider

#(parameter DIVIDE_LEN=23)

(
input clock,
output div_clock
);

reg [DIVIDE_LEN-1:0] cnt;

initial cnt <= {DIVIDE_LEN{1'b0}};

always @(posedge clock)
begin
cnt <= cnt+1'b1;
end

assign div_clock=cnt[DIVIDE_LEN-1];

endmodule
module rombias
#(parameter DATA_WIDTH=8, 
parameter SIZE=16)
(
input clk,
output reg [DATA_WIDTH*SIZE-1:0] data_rom
);

genvar t;
reg [DATA_WIDTH-1:0] rom [SIZE-1:0];
wire [DATA_WIDTH*SIZE-1:0] rom_wire;

initial
begin
$readmemh ("D:/7th_Semester_MIEM/Romanoff/sys_array_mnist/biases.hex", rom);
end

generate
for (t=0;t<SIZE;t=t+1) begin: generate_romb
assign rom_wire[DATA_WIDTH*t +: DATA_WIDTH] = rom[t];
end
endgenerate

always @(posedge clk) 
begin 
data_rom <= rom_wire;
end

endmodule
module roma
#(parameter DATA_WIDTH=8, 
parameter SIZE=16)
(
input clk,
output reg [DATA_WIDTH*SIZE-1:0] data_rom
);

genvar t;
reg [DATA_WIDTH-1:0] rom [SIZE-1:0];
wire [DATA_WIDTH*SIZE-1:0] rom_wire;

initial
begin
$readmemh ("D:/7th_Semester_MIEM/Romanoff/sys_array_mnist/a_data.hex", rom);
end

generate
for (t=0;t<SIZE;t=t+1) begin: generate_roma
assign rom_wire[DATA_WIDTH*t +: DATA_WIDTH] = rom[t];
end
endgenerate

always @(posedge clk) 
begin 
data_rom <= rom_wire;
end

endmodule
module romb
#(parameter DATA_WIDTH=8, 
parameter SIZE=16)
(
input clk,
output reg [DATA_WIDTH*SIZE-1:0] data_rom
);

genvar t;
reg [DATA_WIDTH-1:0] rom [SIZE-1:0];
wire [DATA_WIDTH*SIZE-1:0] rom_wire;

initial
begin
$readmemh ("D:/7th_Semester_MIEM/Romanoff/sys_array_mnist/b_data.hex", rom);
end

generate
for (t=0;t<SIZE;t=t+1) begin: generate_romb
assign rom_wire[DATA_WIDTH*t +: DATA_WIDTH] = rom[t];
end
endgenerate

always @(posedge clk) 
begin 
data_rom <= rom_wire;
end

endmodule
module sys_array_cell

#(parameter DATA_WIDTH=8)

(
    input  clock,
    input  reset_n,
    input  param_load,

    input  signed [DATA_WIDTH - 1:0] input_data,
    input  signed [2*DATA_WIDTH-1:0] prop_data,
    input  signed [DATA_WIDTH-1:0] param_data,
    output reg signed [2*DATA_WIDTH-1:0] out_data,
    output reg signed [DATA_WIDTH-1:0] prop_param
);

reg signed [DATA_WIDTH-1:0] param;

    always @(posedge clock)
    begin
        if (~reset_n)
        begin
				out_data <= {2*DATA_WIDTH{1'b0}};
                                param <= {DATA_WIDTH{1'b0}};
        end
        else if (param_load) 
		      begin
				param <= param_data;
				end
		  else
		  begin
		  out_data<= prop_data+input_data*param;
		  prop_param <= input_data;
		  end	   
    end

endmodule
module seg7_tohex
(
input [9:0] code,
output reg [7:0] hexadecimal
);

always @ (*)
begin
case (code)
10'b0000000001: hexadecimal <= 8'b11000000;//all inverted for outputs; 0
10'b0000000010: hexadecimal <= 8'b11111001;// 1
10'b0000000100: hexadecimal <= 8'b10100100;// 2
10'b0000001000: hexadecimal <= 8'b10110000;// 3
10'b0000010000: hexadecimal <= 8'b10011001;// 4
10'b0000100000: hexadecimal <= 8'b10010010;// 5
10'b0001000000: hexadecimal <= 8'b10000010;// 6
10'b0010000000: hexadecimal <= 8'b11111000;// 7
10'b0100000000: hexadecimal <= 8'b10000000;// 8
10'b1000000000: hexadecimal <= 8'b10010000;// 9
default: hexadecimal <= 8'b11111111;// disabled
endcase
end

endmodule
module sys_array_wrapper 

#(parameter DATA_WIDTH=16,
parameter ARRAY_W=10, //i
parameter ARRAY_L=784, //j
parameter CLOCK_DIVIDE=25) 

(
    input  clk,
    input  reset_n,
    input  load_params,
    input  start_comp,
    
    output [7:0] hex_connect
);

localparam MEM_SIZE=ARRAY_W*ARRAY_L;
localparam ARRAY_SIZE=ARRAY_W*ARRAY_W;

wire clk_div, ready;
wire [9:0] hex_input;
wire [DATA_WIDTH*ARRAY_W-1:0] biases;
wire [2*DATA_WIDTH*ARRAY_W - 1:0] output_value, empty_data_write;
wire [DATA_WIDTH*ARRAY_W*ARRAY_L - 1:0] input_data_a, input_data_b;
wire [2*DATA_WIDTH*ARRAY_W*ARRAY_W-1:0] outputs_fetcher, transposed_outputs, empty_data_out;

reg [CLOCK_DIVIDE : 0] small_cnt;
reg [1:0] propagate_reg_ctrl; //control the output of propagate parallel to serial converter register

genvar i,j;

roma #(.DATA_WIDTH(DATA_WIDTH), .SIZE(MEM_SIZE)) rom_instance_a (.clk(clk), .data_rom(input_data_a));
romb #(.DATA_WIDTH(DATA_WIDTH), .SIZE(MEM_SIZE)) rom_instance_b (.clk(clk), .data_rom(input_data_b));
rombias #(.DATA_WIDTH(DATA_WIDTH), .SIZE(ARRAY_W)) rom_instance_bias (.clk(clk), .data_rom(biases));

sys_array_fetcher #(.DATA_WIDTH(DATA_WIDTH), .ARRAY_W(ARRAY_W), .ARRAY_L(ARRAY_L)) fetching_unit
(
.clock(clk),
.reset_n(reset_n),
.load_params(load_params),
.start_comp(start_comp),
.input_data_b(input_data_b),
.input_data_a(input_data_a),
.ready(ready),
.out_data(outputs_fetcher)
);

generate
for (i=0;i<ARRAY_W;i=i+1) begin: transpose_i
for (j=0;j<ARRAY_W;j=j+1) begin: transpose_j
assign transposed_outputs [2*DATA_WIDTH*(ARRAY_W*j+i) +: 2*DATA_WIDTH] = outputs_fetcher [2*DATA_WIDTH*(ARRAY_W*i+j) +: 2*DATA_WIDTH];
end
end
endgenerate

clock_divider #(.DIVIDE_LEN(CLOCK_DIVIDE)) clock_divaaa (.clock(clk), .div_clock(clk_div));

shift_reg #(.DATA_WIDTH(2*DATA_WIDTH*ARRAY_W), .LENGTH(ARRAY_W)) propagate_reg
(
.clock(clk_div),
.reset_n(reset_n),
.ctrl_code(propagate_reg_ctrl),
.data_in(transposed_outputs),
.data_read(output_value),
.data_out(empty_data_out),
.data_write(empty_data_write)
);

sum_and_max #(.DATA_WIDTH(DATA_WIDTH), .LENGTH(ARRAY_W)) max_voter
(
.clk(clk_div),
.reset_n(reset_n),
.ready(ready),
.input_data(output_value),
.biases(biases),
.maxi(hex_input)
);

seg7_tohex hex_converter (.code(hex_input), .hexadecimal(hex_connect));

always @(posedge clk)
begin

if (~reset_n) begin propagate_reg_ctrl <= 2'b00; small_cnt <= {(CLOCK_DIVIDE+1){1'b0}}; end

else begin

case (ready)

1'b0: propagate_reg_ctrl <= 2'b01;
1'b1: begin 
	if (small_cnt[CLOCK_DIVIDE] == 1'b0) begin propagate_reg_ctrl <= 2'b01; small_cnt <= small_cnt+1'b1; end
	else propagate_reg_ctrl <= 2'b11;
	end
default: propagate_reg_ctrl <= 2'b00;

endcase
end

end
endmodule
module testbench_wrapper
#(parameter DATA_WIDTH=8,
parameter ARRAY_W=5, //j
parameter ARRAY_L=2, //i
parameter CLOCK_DIVIDE=2);

reg clk, reset_n, param_load, start_comp;
wire [4*DATA_WIDTH-1:0] hex_output;

sys_array_wrapper #(.DATA_WIDTH(DATA_WIDTH), .ARRAY_W(ARRAY_W), .ARRAY_L(ARRAY_L), .CLOCK_DIVIDE(CLOCK_DIVIDE)) wrappper
(
.clk(clk),
.reset_n(reset_n),
.load_params(param_load),
.start_comp(start_comp),
.hex_connect(hex_output)
);

initial $dumpvars;

initial begin
clk = 0;
forever #10 clk=!clk;
end

initial
begin
reset_n=0; param_load = 0; start_comp = 0;
#80; reset_n=1;
#20;
param_load = 1;
#20; param_load = 0; #10; start_comp = 1; #20; start_comp = 0; #100;
end
endmodule
module testbench_sys_array
#(parameter DATA_WIDTH=8,
parameter ARRAY_W=4, //j
parameter ARRAY_L=4);//i

reg [DATA_WIDTH*ARRAY_W*ARRAY_L-1:0] parameters_test;
reg [DATA_WIDTH*ARRAY_L-1:0] inputs_test;
wire [2*DATA_WIDTH*ARRAY_W-1:0] outputs_test;
reg clk, reset_n, param_load;

sys_array_basic #(.DATA_WIDTH(DATA_WIDTH), .ARRAY_W(ARRAY_W), .ARRAY_L(ARRAY_L)) systolic_array
(
.clk(clk),
.reset_n(reset_n),
.param_load(param_load),
.parameter_data(parameters_test),
.input_module(inputs_test),
.out_module(outputs_test)
);

initial $dumpvars;

initial begin
clk = 0;
forever #10 clk=!clk;
end

integer ii;

initial
begin
reset_n=0; param_load = 0;
#80; reset_n=1;
#20;
for (ii=0; ii<16; ii=ii+1) begin
parameters_test[DATA_WIDTH*ii +: DATA_WIDTH] = ii+1;
end
param_load = 1;
#20; param_load = 0; #10; inputs_test[7:0] = 8'd1; //1st clk cycle
#20; inputs_test[7:0] = 8'd5; inputs_test[15:8] = 8'd2; //2nd clk cycle
#20; inputs_test[7:0] = 8'd9; inputs_test[15:8] = 8'd6; inputs_test[23:16] = 8'd3; //3rd clk cycle
#20; inputs_test[7:0] = 8'd13; inputs_test[15:8] = 8'd10; inputs_test[23:16] = 8'd7; inputs_test[31:24] = 8'd4; //4th clock cycle
#20; inputs_test[15:8] = 8'd14; inputs_test[23:16] = 8'd11; inputs_test[31:24] = 8'd8; //5th clock cycle
#20; inputs_test[23:16] = 8'd15; inputs_test[31:24] = 8'd12; //6th clock cycle
#20; inputs_test[31:24] = 8'd16; //7th clk cycle
end
endmodule
module testbench_fetcher
#(parameter DATA_WIDTH=8,
parameter ARRAY_W=4, //j
parameter ARRAY_L=4);//i

localparam ARRAY_SIZE=ARRAY_L*ARRAY_W;

reg [DATA_WIDTH*ARRAY_W*ARRAY_L-1:0] parameters_test;
reg [DATA_WIDTH*ARRAY_W*ARRAY_L - 1:0] inputs_test;
wire [2*DATA_WIDTH*ARRAY_W*ARRAY_W-1:0] outputs_test;
reg clk, reset_n, param_load, start_comp;
wire ready;

sys_array_fetcher #(.DATA_WIDTH(DATA_WIDTH), .ARRAY_W(ARRAY_W), .ARRAY_L(ARRAY_L)) fetching_unit
(
.clock(clk),
.reset_n(reset_n),
.load_params(param_load),
.start_comp(start_comp),
.input_data_b(parameters_test),
.input_data_a(inputs_test),
.ready(ready),
.out_data(outputs_test)
);

initial $dumpvars;

initial begin
clk = 0;
forever #10 clk=!clk;
end

integer ii;

initial
begin
reset_n=0; param_load = 0; start_comp = 0;
#80; reset_n=1;
#20;
for (ii=0; ii<ARRAY_SIZE; ii=ii+1) begin
parameters_test[DATA_WIDTH*ii +: DATA_WIDTH] = ii+1;
inputs_test[DATA_WIDTH*ii +: DATA_WIDTH] = ii+1;
end
param_load = 1;
#20; param_load = 0; #10; start_comp = 1; #20; start_comp = 0; #100;
end
endmodule