/*
 * Copyright (c) 2024 Uri Shaked
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

module tt_um_mandelbrot_accel (
    input  wire [7:0] ui_in,    // Dedicated inputs
    output wire [7:0] uo_out,   // Dedicated outputs
    input  wire [7:0] uio_in,   // IOs: Input path
    output wire [7:0] uio_out,  // IOs: Output path
    output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
    input  wire       ena,      // always 1 when the design is powered, so you can ignore it
    input  wire       clk,      // clock
    input  wire       rst_n     // reset_n - low to reset
);

  assign uo_out  = {o_iter, o_unbounded};
  assign uio_oe  = 0;
  assign uio_out = 0;

  wire i_start = ui_in[0];
  wire i_load_Cr = ui_in[1];
  wire i_load_Ci = ui_in[2];
  wire [7:0] data_in = uio_in;

  reg o_unbounded;
  reg [6:0] o_iter;

  reg [23:0] data_in_reg;
  wire [31:0] data_in_word = {data_in, data_in_reg};

  reg [31:0] Cr_next;
  reg [31:0] Ci_next;

  reg [31:0] Zr;
  reg [31:0] Zi;
  reg [31:0] Cr;
  reg [31:0] Ci;
  wire [31:0] Rr;
  wire [31:0] Ri;
  wire unbounded;

  mandelbrot_func mandelbrot (
      .Ci(Ci),
      .Cr(Cr),
      .Zr(Zr),
      .Zi(Zi),
      .Rr(Rr),
      .Ri(Ri),
      .unbounded(unbounded)
  );

  always @(posedge clk or negedge rst_n)
    if (~rst_n) begin
      Zr <= 0;
      Zi <= 0;
      Cr <= 0;
      Ci <= 0;
      Cr_next <= 0;
      Ci_next <= 0;
      o_unbounded <= 0;
      data_in_reg <= 0;
      o_iter <= 0;
    end else begin
      if (i_load_Cr) begin
        Cr_next <= data_in_word;
      end
      if (i_load_Ci) begin
        Ci_next <= data_in_word;
      end
      if (i_start) begin
        Zr <= i_load_Cr ? data_in_word : Cr_next;
        Zi <= i_load_Ci ? data_in_word : Ci_next;
        Cr <= i_load_Cr ? data_in_word : Cr_next;
        Ci <= i_load_Ci ? data_in_word : Ci_next;
        o_unbounded <= 0;
        o_iter <= 1;
      end else begin
        Zr <= Rr;
        Zi <= Ri;
        data_in_reg <= {data_in, data_in_reg[23:8]};
        if (unbounded) begin
          o_unbounded <= 1;
        end else if (!o_unbounded) begin
          o_iter <= o_iter + 1;
        end
      end
    end

  // List all unused inputs to prevent warnings
  wire _unused = &{ena, ui_in[7:3], 1'b0};

endmodule

// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0

`default_nettype none
module fp_add_sub (
    input  wire [31:0] a_operand,
    input  wire [31:0] b_operand,
    input  wire op_subtract,
    output reg  [31:0] o_result
);

  add_sub a(
    .in_x(a_operand),
    .in_y(b_operand),
    .operation(op_subtract),
    .round_mode(3'b000),
    .out_z(o_result)
  );

endmodule

module add_sub(
  input  wire [31:0] in_x,
  input  wire [31:0] in_y,
  input  wire        operation,
  input  wire [2:0]  round_mode,
  output wire [31:0] out_z,
  output wire [4:0]  exceptions);

  wire        sign_x, sign_y;
  wire [7:0]  exp_x, exp_y, exp_a, exp_b, subnorm_exp, norm_exp;
  wire [22:0] mant_x, mant_y, mant_a, mant_b;

  wire        x_is_zero, x_is_inf, x_is_qNaN, x_is_sNaN;
  wire        y_is_zero, y_is_inf, y_is_qNaN, y_is_sNaN;
  wire        a_is_subnorm, b_is_subnorm;
  wire        hd_bit_a, hd_bit_b;

  wire [26:0] arg1, arg2;
  wire [26:0] rt_shift_mant;
  wire [26:0] lt_shft_mant, norm_sum;
  wire [26:0] mant_sum;
  wire [23:0] rounded_mant;
  wire [31:0] inter_result, of_result;

  wire        comp, exp_shft_comp;
  wire        operator_y, subtract;
  wire        cout, cout_check;

  wire [7:0]  exp_diff;
  wire [4:0]  ld_zero_cnt, inc_dec_exp_amt;
  wire [7:0]  inter_shft_amt, shft_amt;
  wire        round_of;

  wire        sign_z;
  wire [7:0]  exp_z;
  wire [22:0] mant_z;

  wire        invalid_operation;
  wire        divide_by_zero;
  wire        overflow;
  wire        underflow;
  wire        inexact;
  wire [9:0]  x_check_res, y_check_res;

  // checking inputs for special values
  special_check #(8, 24) check_x (.in(in_x), .result(x_check_res));
  special_check #(8, 24) check_y (.in(in_y), .result(y_check_res));

  assign x_is_zero = x_check_res[3] | x_check_res[4];
  assign x_is_inf  = x_check_res[0] | x_check_res[7];
  assign x_is_qNaN = x_check_res[9];
  assign x_is_sNaN = x_check_res[8];

  assign y_is_zero = y_check_res[3] | y_check_res[4];
  assign y_is_inf  = y_check_res[0] | y_check_res[7];
  assign y_is_qNaN = y_check_res[9];
  assign y_is_sNaN = y_check_res[8];

  // unpacking inputs
  assign sign_x = in_x[31];
  assign sign_y = in_y[31];
  assign exp_x  = in_x[30:23];
  assign exp_y  = in_y[30:23];
  assign mant_x = in_x[22:0];
  assign mant_y = in_y[22:0];

  // comparing both numbers
  assign comp = (exp_y > exp_x) ? 1'b1 : (exp_y != exp_x) ? 1'b0 : (mant_y > mant_x);

  // determining operation to be performed
  assign operator_y = sign_y ^ operation;
  assign subtract   = sign_x ^ operator_y;

  // determining output sign
  assign sign_z = x_is_zero ? (operator_y) : (y_is_zero ? sign_x :
                  (subtract ? (comp ? operator_y : sign_x) : sign_x));

  // swapping operands
  assign {exp_a, mant_a} = comp ? {exp_y, mant_y} : {exp_x, mant_x};
  assign {exp_b, mant_b} = comp ? {exp_x, mant_x} : {exp_y, mant_y};

  // checking for subnormal numbers
  assign a_is_subnorm = (|exp_a == 0);
  assign b_is_subnorm = (|exp_b == 0);

  // checking difference in exponents
  assign exp_diff = (a_is_subnorm | b_is_subnorm) & (exp_a != exp_b) ? (exp_a - exp_b - 1)
                    : (exp_a - exp_b);

  // generating hidden bits
  assign hd_bit_a = !a_is_subnorm;
  assign hd_bit_b = !b_is_subnorm;

  // right shifting mantissa to make exponents equal
  right_shifter exp_equalizer (.mantisa({hd_bit_b, mant_b, 3'b000}), .shift_amount(exp_diff),
                               .out(rt_shift_mant));

  // computing sum of the mantissas
  assign arg1 = {hd_bit_a, mant_a, 3'b0};
  assign arg2 = subtract ? (~rt_shift_mant + 27'b1) : rt_shift_mant;

  assign {cout, mant_sum} = {1'b0,arg1} + {1'b0,arg2};
  assign cout_check       = cout & ~subtract;

  leading_zero norm_dist_checker (.in(mant_sum[26:3]), .out(ld_zero_cnt));

  // computing the shift amount
  assign inter_shft_amt = a_is_subnorm ? 8'b0 : {3'b0, ld_zero_cnt};
  assign exp_shft_comp  = (exp_a <= inter_shft_amt);
  assign shft_amt       = exp_shft_comp ? (exp_a - |exp_a) : inter_shft_amt;

  left_shifter #(27) norm_shifter (.mantisa(mant_sum), .shift_amount(shft_amt),
                             .out(lt_shft_mant));

  // determining the exponent increment/decrement
  assign norm_sum        = cout_check ? {cout, mant_sum[26:2], |mant_sum[1:0]} : lt_shft_mant;
  assign inc_dec_exp_amt = a_is_subnorm ? 5'b0 : cout_check ? 5'b1 : shft_amt;

  rounding add_sub_rounder (.sign(sign_z), .mantisa(norm_sum), .round_mode(round_mode),
                            .rounded_mantisa(rounded_mant), .rounding_overflow(round_of));

  // determine exponent in case of normal numbers
  assign norm_exp = cout_check ? (exp_a + inc_dec_exp_amt + round_of) :
                    (exp_a - inc_dec_exp_amt + round_of);

  // determine exponent in case of subnormal numbers
  assign subnorm_exp = (rounded_mant[23] & !(|norm_exp)) ? 8'b1 :
                       (norm_exp - ((hd_bit_a | hd_bit_b) & exp_shft_comp & !rounded_mant[23]));

  assign {exp_z, mant_z} = x_is_zero ? {exp_y, mant_y} : (y_is_zero ? {exp_x, mant_x} :
                           ((mant_x == mant_y) & (exp_x == exp_y) & subtract ? 'd0 :
                           {subnorm_exp, rounded_mant[22:0]}));

  // result check for special numbers
  assign inter_result = (x_is_qNaN | y_is_qNaN) ? {1'h0, 8'hff, 23'h400000} :
                        ((x_is_inf | y_is_inf) ? {sign_z, 8'hff, 23'h0} : ((exp_z == 8'hff) ?
                        {sign_z, exp_z, 23'd0} : {sign_z, exp_z, mant_z}));

  assign invalid_operation = !(x_is_qNaN | y_is_qNaN) & (x_is_inf & y_is_inf & subtract) |
                               x_is_sNaN | y_is_sNaN;

  // does not occur in addition subtraction
  assign divide_by_zero = 0;

  assign overflow = !(x_is_qNaN | y_is_qNaN) & &exp_z & !(x_is_inf | y_is_inf | x_is_qNaN |
                      y_is_qNaN | x_is_sNaN | y_is_sNaN);

  // determining result in case of overflow
  assign of_result = ({32{(round_mode == 3'h0) | (round_mode == 3'h4)}} & {sign_z, 8'hff, 23'h0}) |
                     ({32{round_mode == 3'h1}} & {sign_z, 8'hfe, 23'h7fffff}) |
                     ({32{round_mode == 3'h2}} & (sign_z ? {1'h1, 8'hff, 23'h0} :
                                                 {1'h0, 8'hfe, 23'h7fffff})) |
                     ({32{round_mode == 3'h3}} & (sign_z ? {1'h1, 8'hfe, 23'h7fffff} :
                                                 {1'h0, 8'hff, 23'h0}));

  // does not occur in addition subtraction
  assign underflow = 0;

  assign inexact = !(x_is_qNaN | y_is_qNaN) & (|norm_sum[2:0] | overflow | underflow) &
                   !(x_is_zero | y_is_zero | x_is_qNaN | y_is_qNaN | x_is_sNaN | y_is_sNaN |
                     x_is_inf  | y_is_inf);

  assign exceptions = {invalid_operation, divide_by_zero, overflow, underflow, inexact};

  // assign output
  assign out_z = overflow ? of_result : underflow ? 32'd0 : invalid_operation ?
                 {1'h0, 8'hff, 23'h400000} : inter_result;

endmodule

module left_shifter #(parameter mant = 24)
(
	input  wire [mant-1:0] mantisa,
	input  wire [7:0] shift_amount,

	output wire [mant-1:0] out
);

	wire [mant-1:0] temp;

	assign temp  = mantisa << shift_amount;
	assign out   = {temp[mant-1:1], mantisa[0]};

endmodule

module right_shifter
(
  input  wire [26:0] mantisa,
  input  wire [7:0] shift_amount,

  output wire [26:0] out
);

  assign out = ({27{(shift_amount[7:0]==8'd0)}}  &         mantisa)                          |
              ({27{(shift_amount[7:0]==8'd1)}}  & {1'd0,  mantisa[26:2],   |mantisa[1:0]})  |
              ({27{(shift_amount[7:0]==8'd2)}}  & {2'd0,  mantisa[26:3],   |mantisa[2:0]})  |
              ({27{(shift_amount[7:0]==8'd3)}}  & {3'd0,  mantisa[26:4],   |mantisa[3:0]})  |
              ({27{(shift_amount[7:0]==8'd4)}}  & {4'd0,  mantisa[26:5],   |mantisa[4:0]})  |
              ({27{(shift_amount[7:0]==8'd5)}}  & {5'd0,  mantisa[26:6],   |mantisa[5:0]})  |
              ({27{(shift_amount[7:0]==8'd6)}}  & {6'd0,  mantisa[26:7],   |mantisa[6:0]})  |
              ({27{(shift_amount[7:0]==8'd7)}}  & {7'd0,  mantisa[26:8],   |mantisa[7:0]})  |
              ({27{(shift_amount[7:0]==8'd8)}}  & {8'd0,  mantisa[26:9],   |mantisa[8:0]})  |
              ({27{(shift_amount[7:0]==8'd9)}}  & {9'd0,  mantisa[26:10],  |mantisa[9:0]})  |
              ({27{(shift_amount[7:0]==8'd10)}} & {10'd0, mantisa[26:11],  |mantisa[10:0]}) |
              ({27{(shift_amount[7:0]==8'd11)}} & {11'd0, mantisa[26:12],  |mantisa[11:0]}) |
              ({27{(shift_amount[7:0]==8'd12)}} & {12'd0, mantisa[26:13],  |mantisa[12:0]}) |
              ({27{(shift_amount[7:0]==8'd13)}} & {13'd0, mantisa[26:14],  |mantisa[13:0]}) |
              ({27{(shift_amount[7:0]==8'd14)}} & {14'd0, mantisa[26:15],  |mantisa[14:0]}) |
              ({27{(shift_amount[7:0]==8'd15)}} & {15'd0, mantisa[26:16],  |mantisa[15:0]}) |
              ({27{(shift_amount[7:0]==8'd16)}} & {16'd0, mantisa[26:17],  |mantisa[16:0]}) |
              ({27{(shift_amount[7:0]==8'd17)}} & {17'd0, mantisa[26:18],  |mantisa[17:0]}) |
              ({27{(shift_amount[7:0]==8'd18)}} & {18'd0, mantisa[26:19],  |mantisa[18:0]}) |
              ({27{(shift_amount[7:0]==8'd19)}} & {19'd0, mantisa[26:20],  |mantisa[19:0]}) |
              ({27{(shift_amount[7:0]==8'd20)}} & {20'd0, mantisa[26:21],  |mantisa[20:0]}) |
              ({27{(shift_amount[7:0]==8'd21)}} & {21'd0, mantisa[26:22],  |mantisa[21:0]}) |
              ({27{(shift_amount[7:0]==8'd22)}} & {22'd0, mantisa[26:23],  |mantisa[22:0]}) |
              ({27{(shift_amount[7:0]==8'd23)}} & {23'd0, mantisa[26:24],  |mantisa[23:0]}) |
              ({27{(shift_amount[7:0]==8'd24)}} & {24'd0, mantisa[26:25],  |mantisa[24:0]}) |
              ({27{(shift_amount[7:0]==8'd25)}} & {25'd0, mantisa[26],     |mantisa[25:0]}) |
              ({27{(shift_amount[7:0]>=8'd26)}} & {26'd0,                  |mantisa[26:0]});
endmodule

module rounding
(
  input  wire sign,
  input  wire [26:0] mantisa,
  input  wire [2:0] round_mode,

  output wire [23:0] rounded_mantisa,
  output wire rounding_overflow
);

  wire [23:0] rne, rtz, rdn, rup, rmm;
  wire rne_overflow, rtz_overflow, rdn_overflow, rup_overflow, rmm_overflow;

  assign {rne_overflow, rne} = mantisa[2] ? (|mantisa[1:0] ? ({1'b0, mantisa[26:3]} + 1'b1)
                               : ({1'b0, mantisa[26:3]} + mantisa[3])) : {1'b0, mantisa[26:3]};

  assign {rtz_overflow, rtz} = {1'b0, mantisa[26:3]};

  assign {rdn_overflow, rdn} = |mantisa[2:0] ? (sign ? ({1'b0, mantisa[26:3]} + 1'b1)
                                : {1'b0, mantisa[26:3]}) : {1'b0, mantisa[26:3]};

  assign {rup_overflow, rup} = |mantisa[2:0] ? (sign ? {1'b0, mantisa[26:3]}
                                  : ({1'b0, mantisa[26:3]} + 1'b1)) : {1'b0, mantisa[26:3]};
  assign {rmm_overflow, rmm} = mantisa[2] ? ({1'b0, mantisa[26:3]} + 1'b1) : {1'b0, mantisa[26:3]};

  assign rounded_mantisa     = ({24{round_mode == 3'b000}} & rne) |
                               ({24{round_mode == 3'b001}} & rtz) |
                               ({24{round_mode == 3'b010}} & rdn) |
                               ({24{round_mode == 3'b011}} & rup) |
                               ({24{round_mode == 3'b100}} & rmm);

  assign rounding_overflow   = ((round_mode == 3'b000) & rne_overflow) |
                               ((round_mode == 3'b001) & rtz_overflow) |
                               ((round_mode == 3'b010) & rdn_overflow) |
                               ((round_mode == 3'b011) & rup_overflow) |
                               ((round_mode == 3'b100) & rmm_overflow);

endmodule

module leading_zero
(
  input  wire [23:0] in,

  output wire [4:0] out
);
  assign out[4:0]       = ({5{(in[22]  & (&(~(in[23]))))}}    & 5'd1)  |
 		                      ({5{(in[21]  & (&(~(in[23:22]))))}} & 5'd2)  |
 		                      ({5{(in[20]  & (&(~(in[23:21]))))}} & 5'd3)  |
 		                      ({5{(in[19]  & (&(~(in[23:20]))))}} & 5'd4)  |
 		                      ({5{(in[18]  & (&(~(in[23:19]))))}} & 5'd5)  |
 		                      ({5{(in[17]  & (&(~(in[23:18]))))}} & 5'd6)  |
 		                      ({5{(in[16]  & (&(~(in[23:17]))))}} & 5'd7)  |
 		                      ({5{(in[15]  & (&(~(in[23:16]))))}} & 5'd8)  |
 		                      ({5{(in[14]  & (&(~(in[23:15]))))}} & 5'd9)  |
 		                      ({5{(in[13]  & (&(~(in[23:14]))))}} & 5'd10) |
 		                      ({5{(in[12]  & (&(~(in[23:13]))))}} & 5'd11) |
 		                      ({5{(in[11]  & (&(~(in[23:12]))))}} & 5'd12) |
 		                      ({5{(in[10]  & (&(~(in[23:11]))))}} & 5'd13) |
 		                      ({5{(in[9]   & (&(~(in[23:10]))))}} & 5'd14) |
 		                      ({5{(in[8]   & (&(~(in[23: 9]))))}} & 5'd15) |
 		                      ({5{(in[7]   & (&(~(in[23: 8]))))}} & 5'd16) |
 		                      ({5{(in[6]   & (&(~(in[23: 7]))))}} & 5'd17) |
 		                      ({5{(in[5]   & (&(~(in[23: 6]))))}} & 5'd18) |
 		                      ({5{(in[4]   & (&(~(in[23: 5]))))}} & 5'd19) |
 		                      ({5{(in[3]   & (&(~(in[23: 4]))))}} & 5'd20) |
 		                      ({5{(in[2]   & (&(~(in[23: 3]))))}} & 5'd21) |
 		                      ({5{(in[1]   & (&(~(in[23: 2]))))}} & 5'd22) |
 		                      ({5{(in[0]   & (&(~(in[23: 1]))))}} & 5'd23);

endmodule

module special_check #(parameter  exp_width = 8, parameter mant_width = 24)
(
  input  wire [(exp_width + mant_width-1):0] in,

  output wire [9:0] result
);

  wire is_pos_zero;
  wire is_neg_zero;
  wire is_pos_inf;
  wire is_neg_inf;
  wire is_pos_subnorm;
  wire is_neg_subnorm;
  wire is_pos_norm;
  wire is_neg_norm;
  wire is_qNaN;
  wire is_sNaN;

  wire                    sign;
  wire [exp_width-1:0]    exp;
  wire [(mant_width-2):0] sig;

  wire exp_zero, exp_one, sig_zero;

  assign {sign, exp, sig} = in;

  assign exp_zero         = |exp ? 1'b0 : 1'b1;
  assign sig_zero         = |sig ? 1'b0 : 1'b1;
  assign exp_one          = &exp ? 1'b1 : 1'b0;

  assign is_pos_zero      = !sign     & exp_zero  & sig_zero;
  assign is_neg_zero      =  sign     & exp_zero  & sig_zero;
  assign is_pos_subnorm   = !sign     & exp_zero  & !sig_zero;
  assign is_neg_subnorm   =  sign     & exp_zero  & !sig_zero;
  assign is_pos_inf       = !sign     & exp_one   & sig_zero;
  assign is_neg_inf       =  sign     & exp_one   & sig_zero;
  assign is_qNaN          =  exp_one  & !sig_zero & sig[22];
  assign is_sNaN          =  exp_one  & !sig_zero & !sig[22];

  assign is_pos_norm      = (is_pos_zero | is_neg_zero | is_pos_subnorm | is_neg_subnorm | is_pos_inf | is_neg_inf |
			                      is_qNaN | is_sNaN) ? 1'b0 : ~sign ? 1'b1 : 1'b0;

  assign is_neg_norm      = (is_pos_zero | is_neg_zero | is_pos_subnorm | is_neg_subnorm | is_pos_inf | is_neg_inf |
			                      is_qNaN | is_sNaN) ? 1'b0 : sign ? 1'b1 : 1'b0;

  assign result           = {is_qNaN, is_sNaN, is_pos_inf, is_pos_norm, is_pos_subnorm, is_pos_zero, is_neg_zero,
                            is_neg_subnorm, is_neg_norm, is_neg_inf};
endmodule

// The following module was generated by GPT-4o. Prompt:
//
// ```
// module fp_multiply (
//     input wire [31:0] a_operand,
//     input wire [31:0] b_operand,
//     output reg o_exception,
//     output reg o_overflow,
//     output reg o_underflow,
//     output reg [31:0] o_result
// );
//
// Implement IEEE-754 floating point multiplication.
// ```

`default_nettype none

module fp_multiply (
    input wire [31:0] a_operand,
    input wire [31:0] b_operand,
    output reg o_exception,
    output reg o_overflow,
    output reg o_underflow,
    output reg [31:0] o_result
);

  // Split the inputs into sign, exponent, and mantissa
  wire sign_a, sign_b, sign_result;
  wire [7:0] exponent_a, exponent_b, exponent_result;
  wire [23:0] mantissa_a, mantissa_b;

  assign sign_a = a_operand[31];
  assign sign_b = b_operand[31];
  assign exponent_a = a_operand[30:23];
  assign exponent_b = b_operand[30:23];
  assign mantissa_a = {1'b1, a_operand[22:0]};
  assign mantissa_b = {1'b1, b_operand[22:0]};

  // Calculate the result sign
  assign sign_result = sign_a ^ sign_b;

  // Calculate the result exponent
  assign exponent_result = exponent_a + exponent_b - 8'd127;

  // Multiply the mantissas
  wire [47:0] product_mantissa;
  assign product_mantissa = mantissa_a * mantissa_b;

  // Normalize the result mantissa and adjust the exponent if necessary
  wire [22:0] normalized_mantissa;
  wire [ 7:0] adjusted_exponent;

  assign adjusted_exponent = (product_mantissa[47]) ? (exponent_result + 1) : exponent_result;
  assign normalized_mantissa = (product_mantissa[47]) ? product_mantissa[46:24] : product_mantissa[45:23];

  // Handle special cases
  always @(*) begin
    o_exception = 0;
    o_overflow = 0;
    o_underflow = 0;
    o_result = 32'd0;

    // Check for NaN and Infinity
    if ((exponent_a == 8'd255 && mantissa_a[22:0] != 0) || (exponent_b == 8'd255 && mantissa_b[22:0] != 0)) begin
      // NaN
      o_exception = 1;
      o_result = 32'h7FC00000;  // Quiet NaN
    end else if (exponent_a == 8'd255 || exponent_b == 8'd255) begin
      // Infinity
      o_result = {sign_result, 8'd255, 23'd0};
    end else if (exponent_a == 0 || exponent_b == 0) begin
      // Zero
      o_result = {sign_result, 8'd0, 23'd0};
    end else begin
      // Normal case
      if (adjusted_exponent >= 8'd255) begin
        // Overflow
        o_overflow = 1;
        o_result   = {sign_result, 8'd255, 23'd0};
      end else if (adjusted_exponent <= 8'd0) begin
        // Underflow
        o_underflow = 1;
        o_result = {sign_result, 8'd0, 23'd0};
      end else begin
        o_result = {sign_result, adjusted_exponent[7:0], normalized_mantissa[22:0]};
      end
    end
  end

endmodule

/*
 * Copyright (c) 2024 Uri Shaked
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

/**
 * This module calculates one iteration of the Mandelbrot set function:
 * R = Z^2 + C
 *
 * In addition, it checks if the given input Z is not a member of the Mandelbrot set, setting
 * unbounded to 1 when |Z| > 2.
 */
module mandelbrot_func (
    input wire [31:0] Ci,
    input wire [31:0] Cr,
    input wire [31:0] Zr,
    input wire [31:0] Zi,
    output wire [31:0] Rr,
    output wire [31:0] Ri,
    output wire unbounded
);

  // ZrZi = Zr * Zi
  wire [31:0] ZrZi;
  fp_multiply m1 (
      .a_operand(Zr),
      .b_operand(Zi),
      .o_result(ZrZi),
      .o_exception(),
      .o_overflow(),
      .o_underflow()
  );

  // Zr_squared = Zr * Zr
  wire [31:0] Zr_squared;
  fp_multiply m2 (
      .a_operand(Zr),
      .b_operand(Zr),
      .o_result(Zr_squared),
      .o_exception(),
      .o_overflow(),
      .o_underflow()
  );

  // Zi_squared = Zi * Zi
  wire [31:0] Zi_squared;
  fp_multiply m3 (
      .a_operand(Zi),
      .b_operand(Zi),
      .o_result(Zi_squared),
      .o_exception(),
      .o_overflow(),
      .o_underflow()
  );

  wire [31:0] Z2r;
  wire [31:0] Z2i;

  // Z2r = Zr_squared - Zi_squared
  fp_add_sub sub1 (
      .a_operand(Zr_squared),
      .b_operand(Zi_squared),
      .op_subtract(1'b1),
      .o_result(Z2r)
  );

  // Z2i = 2 * Zr * Zi
  fp_add_sub add1 (
      .a_operand(ZrZi),
      .b_operand(ZrZi),
      .op_subtract(1'b0),
      .o_result(Z2i)
  );

  // Rr = Z2r + Cr
  fp_add_sub add2 (
      .a_operand(Z2r),
      .b_operand(Cr),
      .op_subtract(1'b0),
      .o_result(Rr)
  );

  // Ri = Z2i + Ci
  fp_add_sub add3 (
      .a_operand(Z2i),
      .b_operand(Ci),
      .op_subtract(1'b0),
      .o_result(Ri)
  );

  wire [31:0] Z_abs_squared;
  wire [31:0] four_ieee754 = 32'h40800020;  // ~4.0

  // |Z|^2 = Zr^2 + Zi^2
  fp_add_sub add4 (
      .a_operand(Zr_squared),
      .b_operand(Zi_squared),
      .op_subtract(1'b0),
      .o_result(Z_abs_squared)
  );

  wire [31:0] Z_minus_four;
  // calculate |Z|^2 - 4
  fp_add_sub sub3 (
      .a_operand(Z_abs_squared),
      .b_operand(four_ieee754),
      .op_subtract(1'b1),
      .o_result(Z_minus_four)
  );

  assign unbounded = ~Z_minus_four[31];  // |Z| > 2
endmodule


// SPDX-FileCopyrightText: © 2024 Uri Shaked <uri@wokwi.com>
// SPDX-License-Identifier: MIT

`default_nettype none

module tt_um_urish_spell (
    input  wire [7:0] ui_in,    // Dedicated inputs
    output wire [7:0] uo_out,   // Dedicated outputs
    input  wire [7:0] uio_in,   // IOs: Input path
    output wire [7:0] uio_out,  // IOs: Output path
    output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
    input  wire       ena,      // will go high when the design is enabled
    input  wire       clk,      // clock
    input  wire       rst_n     // reset_n - low to reset
);

  localparam StateFetch = 3'd0;
  localparam StateFetchData = 3'd1;
  localparam StateExecute = 3'd2;
  localparam StateStore = 3'd3;
  localparam StateDelay = 3'd4;
  localparam StateSleep = 3'd5;
  localparam StateStop = 3'd6;

  localparam REG_PC = 2'd0;
  localparam REG_SP = 2'd1;
  localparam REG_EXEC = 2'd2;
  localparam REG_STACK_TOP = 2'd3;

  wire o_sleep = state == StateSleep;
  wire o_stop = state == StateStop || state == StateSleep;
  wire o_wait_delay = state == StateDelay;
  reg o_shift_out;

  wire [7:0] porta_oe;
  wire [7:0] porta_out;
  assign uo_out = (
    (~porta_oe & {4'b0000, o_shift_out, o_wait_delay, o_stop, o_sleep})
    | (porta_oe & porta_out)
  );

  wire i_run = ui_in[0];
  wire i_step = ui_in[1];
  wire i_load = ui_in[2];
  wire i_dump = ui_in[3];
  wire i_shift_in = ui_in[4];
  wire [1:0] i_reg_sel = ui_in[6:5];

  reg past_i_run;
  reg [2:0] state;
  reg [7:0] pc;
  reg [4:0] sp;
  reg [7:0] opcode;
  reg [7:0] memory_input;
  reg [7:0] stack[31:0];

  wire [7:0] next_pc;
  wire [4:0] next_sp;
  wire [1:0] stack_write_count;
  wire [7:0] set_stack_top;
  wire [7:0] set_stack_belowtop;
  wire memory_write_en;
  wire memory_write_type_data;
  wire [7:0] memory_write_addr;
  wire [7:0] memory_write_data;
  wire [7:0] delay_amount;
  wire sleep;
  wire stop;

  // shift register for loading data / dumping debug info
  reg [7:0] shift_reg;

  // Out of order execution
  reg single_step;
  reg out_of_order_exec;

  wire [4:0] stack_top_index = sp - 1;
  wire [7:0] stack_top = stack[stack_top_index];

  // Memory related registers
  reg mem_select;
  reg mem_type_data;
  reg [7:0] mem_addr;
  reg [7:0] mem_write_value;
  reg mem_write_en;
  wire [7:0] mem_read_value;
  wire mem_data_ready;

  // Delay related registers
  reg [23:0] cycles_per_ms;
  reg [23:0] delay_cycles;
  reg [7:0] delay_counter;

  // Debug stuff
  reg [63:0] reg_name;
  reg [63:0] state_name;

  // To avoid linter warnings about unused wires
  wire _unused_ok = &{1'b0, ena, ui_in[7], state_name, reg_name, 1'b0};

  always @(*) begin
    case (state)
      StateFetch: state_name = "Fetch";
      StateFetchData: state_name = "FetchDat";
      StateExecute: state_name = "Execute";
      StateStore: state_name = "Store";
      StateDelay: state_name = "Delay";
      StateSleep: state_name = "Sleep";
      StateStop: state_name = "Stop";
      default: state_name = "Invalid";
    endcase
  end

  always @(*) begin
    case (i_reg_sel)
      REG_PC: reg_name = "PC";
      REG_SP: reg_name = "SP";
      REG_EXEC: reg_name = "Exec";
      REG_STACK_TOP: reg_name = "StackTop";
    endcase
  end

  spell_execute exec (
      .opcode(opcode),
      .pc(pc),
      .sp(sp),
      .stack_top(stack_top),
      .stack_belowtop(stack[sp-2]),
      .memory_input(memory_input),
      .next_pc(next_pc),
      .next_sp(next_sp),
      .out_of_order_exec(out_of_order_exec),
      .stack_write_count(stack_write_count),
      .set_stack_top(set_stack_top),
      .set_stack_belowtop(set_stack_belowtop),
      .memory_write_en(memory_write_en),
      .memory_write_type_data(memory_write_type_data),
      .memory_write_addr(memory_write_addr),
      .memory_write_data(memory_write_data),
      .delay_amount(delay_amount),
      .sleep(sleep),
      .stop(stop)
  );

  spell_mem mem (
      .rst_n(rst_n),
      .clk(clk),
      .select(mem_select),
      .addr(mem_addr),
      .data_in(mem_write_value),
      .memory_type_data(mem_type_data),
      .write(mem_write_en),
      .data_out(mem_read_value),
      .data_ready(mem_data_ready),
      // IO
      .porta_out(porta_out),
      .porta_oe(porta_oe),
      .portb_in(uio_in),
      .portb_out(uio_out),
      .portb_oe(uio_oe)
  );

  function is_data_opcode(input [7:0] opcode_to_test);
    is_data_opcode = (opcode_to_test == "?" || opcode_to_test == "r");
  endfunction

  integer j;

  // Main logic
  always @(posedge clk) begin
    if (~rst_n) begin
      state <= StateSleep;
      pc    <= 0;
      sp    <= 0;
      for (j = 0; j < 32; j++) stack[j] <= 0;
      opcode <= 0;
      mem_select <= 0;
      mem_write_en <= 0;
      single_step <= 0;
      out_of_order_exec <= 0;
      cycles_per_ms <= 24'd10000;  /* we assume a 10MHz clock */
      delay_cycles <= 0;
      shift_reg <= 0;
      o_shift_out <= 0;
      past_i_run <= 0;
    end else begin
      shift_reg   <= {shift_reg[6:0], i_shift_in};
      o_shift_out <= shift_reg[7];
      past_i_run  <= i_run;

      if (i_load) begin
        case (i_reg_sel)
          REG_PC: pc <= shift_reg;
          REG_SP: sp <= shift_reg[4:0];
          REG_EXEC: begin
            opcode <= shift_reg;
            state <= is_data_opcode(shift_reg) ? StateFetchData : StateExecute;
            single_step <= 1;
            out_of_order_exec <= 1;
          end
          REG_STACK_TOP: begin
            stack[sp] <= shift_reg;
            sp <= sp + 1;
          end
        endcase
      end

      if (i_dump) begin
        case (i_reg_sel)
          REG_PC: shift_reg <= pc;
          REG_SP: shift_reg <= {3'b000, sp};
          REG_EXEC: shift_reg <= opcode;
          REG_STACK_TOP: shift_reg <= stack[sp-1];
        endcase
      end

      if (i_run && !past_i_run && (state == StateSleep || state == StateStop)) begin
        single_step <= i_step;
        out_of_order_exec <= 0;
        state <= StateFetch;
      end

      case (state)
        StateFetch: begin
          // Read next instruction from code memory
          mem_select <= 1;
          mem_type_data <= 0;
          mem_addr <= pc;
          mem_write_en <= 0;
          if (mem_select && mem_data_ready) begin
            mem_select <= 0;
            opcode <= mem_read_value;
            state <= is_data_opcode(mem_read_value) ? StateFetchData : StateExecute;
          end
        end
        StateFetchData: begin
          // Read data for instruction from either code or data memory
          mem_select <= 1;
          mem_type_data <= (opcode == "r") ? 1'b1 : 1'b0;
          mem_addr <= stack_top;
          mem_write_en <= 0;
          if (mem_select && mem_data_ready) begin
            mem_select <= 0;
            memory_input <= mem_read_value;
            state <= StateExecute;
          end
        end
        StateExecute: begin
          // Execute a single instruction
          pc <= next_pc;
          sp <= next_sp;
          mem_type_data <= memory_write_type_data;
          mem_addr <= memory_write_addr;
          mem_write_value <= memory_write_data;
          if (stack_write_count == 1 || stack_write_count == 2) begin
            stack[next_sp-1] <= set_stack_top;
          end
          if (stack_write_count == 2) begin
            stack[next_sp-2] <= set_stack_belowtop;
          end
          if (memory_write_en) begin
            state <= StateStore;
          end else if (stop || single_step) begin
            state <= StateStop;
          end else if (sleep) begin
            state <= StateSleep;
          end else if (delay_amount != 8'b0 && cycles_per_ms != 24'b0) begin
            delay_counter <= delay_amount - 1;
            delay_cycles <= 0;
            state <= StateDelay;
          end else begin
            state <= StateFetch;
          end
        end
        StateStore: begin
          // Store data from instruction into either code or data memory
          mem_select   <= 1;
          mem_write_en <= 1;
          if (mem_data_ready) begin
            mem_select <= 0;
            mem_write_en <= 0;
            state <= single_step ? StateSleep : StateFetch;
          end
        end
        StateSleep: begin
          // The only way to leave this state is via external intervention.
        end
        StateStop: begin
          // The only way to leave this state is via external intervention.
        end
        StateDelay: begin
          if (delay_cycles + 1 >= cycles_per_ms) begin
            delay_counter <= delay_counter - 1;
            delay_cycles  <= 0;
            if (delay_counter == 0) begin
              state <= single_step ? StateSleep : StateFetch;
            end
          end else begin
            delay_cycles <= delay_cycles + 1;
          end
        end
        default: state <= 3'bx;
      endcase
    end
  end

`ifdef FORMAL
  reg f_init = 1;
  always @(posedge clk) begin
    if (f_init) assume (~rst_n);
    if (rst_n) begin
      assert (!sleep || !stop);
      assert(
        state == StateFetch ||
        state == StateFetchData ||
        state == StateExecute ||
        state == StateStore ||
        state == StateDelay ||
        state == StateSleep ||
        state == StateStop
      );
      if (state == StateDelay) begin
        assume (cycles_per_ms > 0);
        assert (delay_counter != 8'hff);
        assert (delay_cycles < cycles_per_ms);
      end
      if (state != StateFetch && state != StateFetchData && state != StateStore) begin
        assert (!mem_select);
      end
      if (state != StateStore) begin
        assert (!mem_write_en);
      end
    end
    f_init <= 0;
  end
`endif  /* FORMAL */

endmodule

// SPDX-FileCopyrightText: © 2021 Uri Shaked <uri@wokwi.com>
// SPDX-License-Identifier: MIT

`default_nettype none

module spell_execute (
    input wire [7:0] opcode,
    input wire [7:0] pc,
    input wire [4:0] sp,
    input wire [7:0] stack_top,
    input wire [7:0] stack_belowtop,
    input wire [7:0] memory_input,
    input wire out_of_order_exec,
    output reg [7:0] next_pc,
    output reg [4:0] next_sp,
    output reg [1:0] stack_write_count,
    output reg [7:0] set_stack_top,
    output reg [7:0] set_stack_belowtop,
    output reg memory_write_en,
    output reg memory_write_type_data,
    output reg [7:0] memory_write_data,
    output reg [7:0] memory_write_addr,
    output reg [7:0] delay_amount,
    output reg sleep,
    output reg stop
);

  always @(*) begin
    next_pc = out_of_order_exec ? pc : pc + 8'd1;
    next_sp = sp;
    stack_write_count = 2'd0;
    set_stack_top = 8'dx;
    set_stack_belowtop = 8'dx;
    memory_write_en = 1'b0;
    memory_write_type_data = 1'b0;
    memory_write_addr = 8'dx;
    memory_write_data = 8'dx;
    sleep = 0;
    stop = 0;
    delay_amount = 0;

    case (opcode)
      "+": begin
        set_stack_top = stack_belowtop + stack_top;
        stack_write_count = 1;
        next_sp = sp - 1;
      end
      "-": begin
        set_stack_top = stack_belowtop - stack_top;
        stack_write_count = 1;
        next_sp = sp - 1;
      end
      "&": begin
        set_stack_top = stack_belowtop & stack_top;
        stack_write_count = 1;
        next_sp = sp - 1;
      end
      "^": begin
        set_stack_top = stack_belowtop ^ stack_top;
        stack_write_count = 1;
        next_sp = sp - 1;
      end
      "|": begin
        set_stack_top = stack_belowtop | stack_top;
        stack_write_count = 1;
        next_sp = sp - 1;
      end
      ">": begin
        set_stack_top = {1'b0, stack_top[7:1]};
        stack_write_count = 1;
      end
      "<": begin
        set_stack_top = {stack_top[6:0], 1'b0};
        stack_write_count = 1;
      end
      "=": begin
        next_pc = stack_top;
        next_sp = sp - 1;
      end
      "@": begin
        if (stack_belowtop != 8'd0) begin
          next_pc = stack_top;
          next_sp = sp - 1;
          set_stack_top = stack_belowtop - 1;
          stack_write_count = 1;
        end else begin
          next_sp = sp - 2;
        end
      end
      ",": begin
        delay_amount = stack_top;
        next_sp = sp - 1;
      end
      "2": begin
        set_stack_top = stack_top;
        stack_write_count = 1;
        next_sp = sp + 1;
      end
      "!": begin
        memory_write_en = 1'b1;
        memory_write_type_data = 1'b0;
        memory_write_addr = stack_top;
        memory_write_data = stack_belowtop;
        next_sp = sp - 2;
      end
      "?", "r": begin
        set_stack_top = memory_input;
        stack_write_count = 1;
      end
      "w": begin
        memory_write_en = 1'b1;
        memory_write_type_data = 1'b1;
        memory_write_addr = stack_top;
        memory_write_data = stack_belowtop;
        next_sp = sp - 2;
      end
      "x": begin
        set_stack_top = stack_belowtop;
        set_stack_belowtop = stack_top;
        stack_write_count = 2;
      end
      "z":   sleep = 1'b1;
      8'hff: stop = 1'b1;
      default: begin
        set_stack_top = opcode;
        stack_write_count = 1;
        next_sp = sp + 1;
      end
    endcase
  end
endmodule

// SPDX-FileCopyrightText: © 2021 Uri Shaked <uri@wokwi.com>
// SPDX-License-Identifier: MIT

`default_nettype none

module spell_mem (
    input wire rst_n,
    input wire clk,
    input wire select,
    input wire [7:0] addr,
    input wire [7:0] data_in,
    input wire memory_type_data,
    input wire write,
    output reg [7:0] data_out,
    output wire data_ready,

    /* IO */
    output wire [7:0] porta_out,
    output wire [7:0] porta_oe, // out enable
    input  wire [7:0] portb_in,
    output wire [7:0] portb_out,
    output wire [7:0] portb_oe  // out enable
);

  wire code_select = select && !memory_type_data;

  wire data_select = select && memory_type_data;
  wire data_mem_select = data_select && (addr < 8'h20 || addr >= 8'h60);
  wire data_io_select = data_select && (addr >= 8'h20 && addr < 8'h60);

  wire mem_select = code_select || data_mem_select;

  wire io_data_ready;
  wire internal_data_ready;
  assign data_ready = io_data_ready | internal_data_ready;

  wire [7:0] io_data_out;
  wire [7:0] internal_data_out;

  spell_mem_io mem_io (
      .rst_n(rst_n),
      .clk(clk),
      .select(data_io_select),
      .addr(addr),
      .data_in(data_in),
      .write(write),
      .data_out(io_data_out),
      .data_ready(io_data_ready),

      /* IO */
      .porta_out(porta_out),
      .porta_oe(porta_oe),
      .portb_in (portb_in),
      .portb_out(portb_out),
      .portb_oe(portb_oe)
  );

  spell_mem_internal mem_internal (
      .rst_n(rst_n),
      .clk(clk),
      .select(mem_select),
      .addr(addr),
      .data_in(data_in),
      .memory_type_data(memory_type_data),
      .write(write),
      .data_out(internal_data_out),
      .data_ready(internal_data_ready)
  );

  always @(*) begin
    if (data_io_select) begin
      data_out = io_data_out;
    end else begin
      data_out = internal_data_out;
    end
  end

endmodule

// SPDX-FileCopyrightText: © 2021-2024 Uri Shaked <uri@wokwi.com>
// SPDX-License-Identifier: MIT

`default_nettype none

module spell_mem_internal (
    input wire rst_n,
    input wire clk,
    input wire select,
    input wire [7:0] addr,
    input wire [7:0] data_in,
    input wire memory_type_data,
    input wire write,
    output wire [7:0] data_out,
    output reg data_ready
);

  localparam data_mem_size = 32;

  wire code_mem_lo_sel = !memory_type_data && addr[7] == 1'b0;
  wire code_mem_hi_sel = !memory_type_data && addr[7] == 1'b1;

  reg code_mem_ready;
  reg [4:0] code_mem_init_addr;

  wire [4:0] code_mem_addr = code_mem_ready ? addr[6:2] : code_mem_init_addr;
  wire [31:0] code_mem_lo_do;
  wire [31:0] code_mem_hi_do;
  wire [31:0] code_mem_di = code_mem_ready ? {data_in, data_in, data_in, data_in} : 32'hffffffff;
  wire [31:0] code_mem_do = code_mem_lo_sel ? code_mem_lo_do : code_mem_hi_do;

  wire [4:0] word_index = {addr[1:0], 3'b000};
  wire [7:0] code_mem_out = code_mem_do[word_index+:8];
  reg [7:0] data_mem_out;
  wire [7:0] data_out_byte = memory_type_data ? data_mem_out : code_mem_out;
  assign data_out = data_ready ? data_out_byte : 8'bx;

  wire we = select && write;
  wire [3:0] we_sel = we ? (1 << addr[1:0]) : 0;
  wire [3:0] code_mem_we0 = code_mem_ready ? we_sel : 4'b1111;

  RAM32 code_mem_lo (
      .CLK(clk),
      .EN0(rst_n),
      .WE0(code_mem_lo_sel || !code_mem_ready ? code_mem_we0 : 4'b0000),
      .A0 (code_mem_addr),
      .Di0(code_mem_di),
      .Do0(code_mem_lo_do)
  );

  RAM32 code_mem_hi (
      .CLK(clk),
      .EN0(rst_n),
      .WE0(code_mem_hi_sel || !code_mem_ready ? code_mem_we0 : 4'b0000),
      .A0 (code_mem_addr),
      .Di0(code_mem_di),
      .Do0(code_mem_hi_do)
  );

  localparam data_mem_bits = $clog2(data_mem_size);
  reg [7:0] data_mem[data_mem_size-1:0];
  wire [data_mem_bits-1:0] data_addr = addr[data_mem_bits-1:0];

  reg [1:0] cycles;

  integer i;

  always @(posedge clk) begin
    if (~rst_n) begin
      cycles <= 0;
      data_ready <= 0;
      data_mem_out <= 0;
      for (i = 0; i < data_mem_size; i++) data_mem[i] <= 8'h00;
      code_mem_ready <= 0;
      code_mem_init_addr <= 0;
    end else begin
      if (!code_mem_ready) begin
        code_mem_init_addr <= code_mem_init_addr + 1;
        if (code_mem_init_addr == 5'b11111) begin
          code_mem_ready <= 1;
        end
      end else if (!select) begin
        data_ready <= 1'b0;
`ifdef SPELL_INTERNAL_MEM_DELAY
        cycles <= 2'b11;
`endif  /* SPELL_INTERNAL_MEM_DELAY */
      end else if (cycles > 0) begin
        cycles <= cycles - 1;
      end else begin
        data_ready <= 1'b1;
        if (write) begin
          if (memory_type_data && addr < data_mem_size) begin
            data_mem[data_addr] <= data_in;
          end
        end else begin
          data_mem_out <= 8'h00;
          if (memory_type_data && addr < data_mem_size) begin
            data_mem_out <= data_mem[data_addr];
          end
        end
      end
    end
  end
endmodule

// SPDX-FileCopyrightText: © 2021 Uri Shaked <uri@wokwi.com>
// SPDX-License-Identifier: MIT

`default_nettype none

module spell_mem_io (
    input wire rst_n,
    input wire clk,
    input wire select,
    input wire [7:0] addr,
    input wire [7:0] data_in,
    input wire write,
    output reg [7:0] data_out,
    output reg data_ready,

    /* porta */
    output reg [7:0] porta_out,
    output reg [7:0] porta_oe,   // out enable (active high)

    /* porta */
    input  wire [7:0] portb_in,
    output reg  [7:0] portb_out,
    output reg  [7:0] portb_oe    // out enable (active high)
);

  localparam REG_PINB = 8'h36;
  localparam REG_DDRB = 8'h37;
  localparam REG_PORTB = 8'h38;
  localparam REG_PINA = 8'h39;
  localparam REG_DDRA = 8'h3a;
  localparam REG_PORTA = 8'h3b;

  reg past_write;

  always @(posedge clk) begin
    if (~rst_n) begin
      porta_out  <= 8'b00000000;
      porta_oe   <= 8'b00000000;
      portb_out  <= 8'b00000000;
      portb_oe   <= 8'b00000000;
      data_out   <= 8'b0;
      data_ready <= 1'b0;
      past_write <= 1'b0;
    end else begin
      past_write <= select & write;
      if (select) begin
        data_out   <= 8'b0;
        data_ready <= 1'b1;

        case (addr)
          REG_PINB: begin
            if (write) begin
              if (~past_write) portb_out <= portb_out ^ data_in;
            end else begin
              data_out <= portb_in;
            end
          end
          REG_DDRB: begin
            if (write) begin
              portb_oe <= data_in;
            end else begin
              data_out <= portb_oe;
            end
          end
          REG_PORTB: begin
            if (write) begin
              portb_out <= data_in;
            end else begin
              data_out <= portb_out;
            end
          end
          REG_PINA: begin
            if (write) begin
              if (~past_write) porta_out <= porta_out ^ data_in;
            end else begin
              data_out <= 8'h00;
            end
          end
          REG_DDRA: begin
            if (write) begin
              porta_oe <= data_in;
            end else begin
              data_out <= porta_oe;
            end
          end
          REG_PORTA: begin
            if (write) begin
              porta_out <= data_in;
            end else begin
              data_out <= porta_out;
            end
          end
          default: begin
            if (~write) data_out <= 8'hff;
          end
        endcase
      end else data_ready <= 1'b0;
    end
  end
endmodule

/// sta-blackbox

`default_nettype none

module RAM32 (
`ifdef USE_POWER_PINS
    input VPWR,
    input VGND,
`endif
    input CLK,
    input [3:0] WE0,
    input EN0,
    input [4:0] A0,
    input [31:0] Di0,
    output reg [31:0] Do0
);
  reg [31:0] RAM[31:0];

  always @(posedge CLK)
    if (EN0) begin
      Do0 <= RAM[A0];
      if (WE0[0]) RAM[A0][7:0] <= Di0[7:0];
      if (WE0[1]) RAM[A0][15:8] <= Di0[15:8];
      if (WE0[2]) RAM[A0][23:16] <= Di0[23:16];
      if (WE0[3]) RAM[A0][31:24] <= Di0[31:24];
    end else begin
      Do0 <= 32'b0;
    end

endmodule


module tt_um_unisnano

(
	input wire [7:0] ui_in, // Dedicated inputs
	output wire [7:0] uo_out, // Dedicated outputs
	input wire [7:0] uio_in, // IOs: Input path
	output wire [7:0] uio_out, // IOs: Output path
	output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output)
	input wire ena, // always 1 when the design is powered, so you can ignore it
	input wire clk, // clock
	input wire rst_n // reset_n - low to reset

);

	reg output1,output2;
	wire rx,tx;
	assign rx=ui_in[3];
	assign uo_out[0]=1'b0;
	assign uo_out[1]=output1;
	assign uo_out[2]=output2;
	assign uo_out[3]=1'b0;
	assign uo_out[4]=tx;
	assign uo_out[5]=1'b0;
	assign uo_out[6]=1'b0;
	assign uo_out[7]=1'b0;

	assign uio_out = 8'b00000000;
	assign uio_oe = 8'b00000000;

//  wire rst_n;
//  assign rst_n=!reset;

   reg tx_start=0;

   wire tick, rx_done_tick, tx_done_tick;

   wire [7:0] rx_data_out;
   wire [7:0]  dout_rom;
   reg [29:0] counter=0;

   reg [3:0] state=0;

  reg[7:0] din;
  reg [9:0] addr,start_addr,stop_addr;

   mod_m_counter #(.M(27), .N(10)) baud_gen_unit
      (.clk(clk), .reset(!rst_n), .q(), .max_tick(tick));

   uart_rx #(.DBIT(8), .SB_TICK(16)) uart_rx_unit
      (.clk(clk), .reset(!rst_n), .rx(rx), .s_tick(tick),
       .rx_done_tick(rx_done_tick), .dout(rx_data_out));

   uart_tx #(.DBIT(8), .SB_TICK(16)) uart_tx_unit
      (.clk(clk), .reset(!rst_n), .tx_start(tx_start),
       .s_tick(tick), .din(din),
       .tx_done_tick(tx_done_tick), .tx(tx) );

    menu_rom menu_rom(
    .clk(clk), .addr(addr) ,.dout(dout_rom) ) ;

     always @(posedge clk)
        begin
            if(!rst_n)//reset is needed
                begin
                    state<=0;

                end
            else
                begin
                    case(state)//parse every incoming word in real time
                        4'd0://IDLE state, waiting here all the time
                            begin
                                counter<=0;
                                addr<=0;
                                state<=state+1;
                                start_addr<=0;
                                stop_addr<=97;
                            end
                        4'd1:
                            begin
                                state<=state+1;
                            end
                        4'd2:
                            begin
                                din<=dout_rom;
                                state<=state+1;

                            end
                        4'd3:
                            begin
                                tx_start<=1;
                                state<=state+1;

                            end
                        4'd4:
                            begin
                                state<=state+1;
                                counter<=0;
                            end

                        4'd5:
                            begin
                                tx_start<=0;
                                if(tx_done_tick)
                                    state<=state+1;
                                else if(counter<100000000)
                                    counter<=counter+1;
                                else
                                    state<=0;

                            end
                        4'd6:
                            begin
                                counter<=0;
                                if(addr>=stop_addr)
                                    begin
                                        state<=state+1;
                                    end
                                else
                                    begin
                                        state<=1;
                                        addr<=addr+1;
                                    end

                            end
                        4'd7:
                            begin
                                if(rx_done_tick)
                                    begin

                                        if(rx_data_out==49)// 1
                                            begin
                                                start_addr<=98;
                                                stop_addr<=105;
                                                state<=state+1;
                                            end
                                        else if(rx_data_out==50)// 2
                                            begin
                                                start_addr<=106;
                                                stop_addr<=162;
                                                state<=state+1;
                                            end
                                        else if(rx_data_out==51)// 3
                                            begin
                                                start_addr<=163;
                                                stop_addr<=192;
                                                state<=state+1;
                                            end

                                        else if(rx_data_out==52)// 4
                                            begin
                                                output1<=!output1;
                                                state<=0;
                                            end
                                        else if(rx_data_out==53)// 5
                                            begin
                                                output2<=!output2;
                                                state<=0;
                                            end
                                        else
                                            state<=0;
                                    end
                                end
                        4'd8:
                            begin
                                addr<=start_addr;
                                state<=state+1;
                            end
                        4'd9:
                            begin
                                state<=state+1;
                            end
                        4'd10:
                            begin
                                din<=dout_rom;
                                state<=state+1;

                            end
                        4'd11:
                            begin
                                tx_start<=1;
                                state<=state+1;

                            end
                        4'd12:
                            begin
                                state<=state+1;
                                counter<=0;
                            end

                        4'd13:
                            begin
                                tx_start<=0;
                                if(tx_done_tick)
                                    state<=state+1;
                                else if(counter<100000000)
                                    counter<=counter+1;
                                else
                                    state<=0;
                            end

                        4'd14:
                            begin
                                if(addr>=stop_addr)
                                    begin
                                        state<=0;
                                    end
                                else
                                    begin
                                        state<=9;
                                        addr<=addr+1;
                                    end

                            end
                        default:
                            begin
                                state<=0;
                            end
                    endcase
                end
        end

endmodule

module menu_rom(
input clk,
input [9:0] addr,
output reg [7:0] dout ) ;

always @ (posedge clk) begin
   case(addr)
         0 : begin
             dout <= 83;   end
         1 : begin
             dout <= 111;   end
         2 : begin
             dout <= 121;   end
         3 : begin
             dout <= 32;   end
         4 : begin
             dout <= 110;   end
         5 : begin
             dout <= 97;   end
         6 : begin
             dout <= 110;   end
         7 : begin
             dout <= 111;   end
         8 : begin
             dout <= 32;   end
         9 : begin
             dout <= 101;   end
         10 : begin
             dout <= 108;   end
         11 : begin
             dout <= 32;   end
         12 : begin
             dout <= 112;   end
         13 : begin
             dout <= 114;   end
         14 : begin
             dout <= 105;   end
         15 : begin
             dout <= 109;   end
         16 : begin
             dout <= 101;   end
         17 : begin
             dout <= 114;   end
         18 : begin
             dout <= 32;   end
         19 : begin
             dout <= 65;   end
         20 : begin
             dout <= 83;   end
         21 : begin
             dout <= 73;   end
         22 : begin
             dout <= 67;   end
         23 : begin
             dout <= 32;   end
         24 : begin
             dout <= 100;   end
         25 : begin
             dout <= 105;   end
         26 : begin
             dout <= 115;   end
         27 : begin
             dout <= 101;   end
         28 : begin
             dout <= 110;   end
         29 : begin
             dout <= 97;   end
         30 : begin
             dout <= 100;   end
         31 : begin
             dout <= 111;   end
         32 : begin
             dout <= 32;   end
         33 : begin
             dout <= 101;   end
         34 : begin
             dout <= 110;   end
         35 : begin
             dout <= 32;   end
         36 : begin
             dout <= 71;   end
         37 : begin
             dout <= 117;   end
         38 : begin
             dout <= 97;   end
         39 : begin
             dout <= 116;   end
         40 : begin
             dout <= 101;   end
         41 : begin
             dout <= 109;   end
         42 : begin
             dout <= 97;   end
         43 : begin
             dout <= 108;   end
         44 : begin
             dout <= 97;   end
         45 : begin
             dout <= 13;   end
         46 : begin
             dout <= 10;   end
         47 : begin
             dout <= 49;   end
         48 : begin
             dout <= 32;   end
         49 : begin
             dout <= 67;   end
         50 : begin
             dout <= 114;   end
         51 : begin
             dout <= 101;   end
         52 : begin
             dout <= 97;   end
         53 : begin
             dout <= 100;   end
         54 : begin
             dout <= 111;   end
         55 : begin
             dout <= 114;   end
         56 : begin
             dout <= 101;   end
         57 : begin
             dout <= 115;   end
         58 : begin
             dout <= 13;   end
         59 : begin
             dout <= 10;   end
         60 : begin
             dout <= 50;   end
         61 : begin
             dout <= 32;   end
         62 : begin
             dout <= 80;   end
         63 : begin
             dout <= 111;   end
         64 : begin
             dout <= 114;   end
         65 : begin
             dout <= 32;   end
         66 : begin
             dout <= 113;   end
         67 : begin
             dout <= 117;   end
         68 : begin
             dout <= 101;   end
         69 : begin
             dout <= 13;   end
         70 : begin
             dout <= 10;   end
         71 : begin
             dout <= 51;   end
         72 : begin
             dout <= 32;   end
         73 : begin
             dout <= 70;   end
         74 : begin
             dout <= 114;   end
         75 : begin
             dout <= 97;   end
         76 : begin
             dout <= 115;   end
         77 : begin
             dout <= 101;   end
         78 : begin
             dout <= 13;   end
         79 : begin
             dout <= 10;   end
         80 : begin
             dout <= 52;   end
         81 : begin
             dout <= 32;   end
         82 : begin
             dout <= 65;   end
         83 : begin
             dout <= 49;   end
         84 : begin
             dout <= 13;   end
         85 : begin
             dout <= 10;   end
         86 : begin
             dout <= 53;   end
         87 : begin
             dout <= 32;   end
         88 : begin
             dout <= 65;   end
         89 : begin
             dout <= 50;   end
         90 : begin
             dout <= 13;   end
         91 : begin
             dout <= 10;   end
         92 : begin
             dout <= 13;   end
         93 : begin
             dout <= 10;   end
         94 : begin
             dout <= 13;   end
         95 : begin
             dout <= 10;   end
         96 : begin
             dout <= 13;   end
         97 : begin
             dout <= 10;   end
         98 : begin
             dout <= 85;   end
         99 : begin
             dout <= 78;   end
         100 : begin
             dout <= 73;   end
         101 : begin
             dout <= 83;   end
         102 : begin
             dout <= 50;   end
         103 : begin
             dout <= 52;   end
         104 : begin
             dout <= 13;   end
         105 : begin
             dout <= 10;   end
         106 : begin
             dout <= 83;   end
         107 : begin
             dout <= 111;   end
         108 : begin
             dout <= 121;   end
         109 : begin
             dout <= 32;   end
         110 : begin
             dout <= 117;   end
         111 : begin
             dout <= 110;   end
         112 : begin
             dout <= 32;   end
         113 : begin
             dout <= 104;   end
         114 : begin
             dout <= 105;   end
         115 : begin
             dout <= 116;   end
         116 : begin
             dout <= 111;   end
         117 : begin
             dout <= 32;   end
         118 : begin
             dout <= 105;   end
         119 : begin
             dout <= 109;   end
         120 : begin
             dout <= 112;   end
         121 : begin
             dout <= 111;   end
         122 : begin
             dout <= 114;   end
         123 : begin
             dout <= 116;   end
         124 : begin
             dout <= 97;   end
         125 : begin
             dout <= 110;   end
         126 : begin
             dout <= 116;   end
         127 : begin
             dout <= 101;   end
         128 : begin
             dout <= 32;   end
         129 : begin
             dout <= 101;   end
         130 : begin
             dout <= 110;   end
         131 : begin
             dout <= 32;   end
         132 : begin
             dout <= 108;   end
         133 : begin
             dout <= 97;   end
         134 : begin
             dout <= 32;   end
         135 : begin
             dout <= 99;   end
         136 : begin
             dout <= 105;   end
         137 : begin
             dout <= 101;   end
         138 : begin
             dout <= 110;   end
         139 : begin
             dout <= 99;   end
         140 : begin
             dout <= 105;   end
         141 : begin
             dout <= 97;   end
         142 : begin
             dout <= 32;   end
         143 : begin
             dout <= 121;   end
         144 : begin
             dout <= 32;   end
         145 : begin
             dout <= 116;   end
         146 : begin
             dout <= 101;   end
         147 : begin
             dout <= 99;   end
         148 : begin
             dout <= 110;   end
         149 : begin
             dout <= 111;   end
         150 : begin
             dout <= 108;   end
         151 : begin
             dout <= 111;   end
         152 : begin
             dout <= 103;   end
         153 : begin
             dout <= 105;   end
         154 : begin
             dout <= 97;   end
         155 : begin
             dout <= 32;   end
         156 : begin
             dout <= 100;   end
         157 : begin
             dout <= 101;   end
         158 : begin
             dout <= 32;   end
         159 : begin
             dout <= 71;   end
         160 : begin
             dout <= 84;   end
         161 : begin
             dout <= 13;   end
         162 : begin
             dout <= 10;   end
         163 : begin
             dout <= 71;   end
         164 : begin
             dout <= 117;   end
         165 : begin
             dout <= 97;   end
         166 : begin
             dout <= 116;   end
         167 : begin
             dout <= 101;   end
         168 : begin
             dout <= 109;   end
         169 : begin
             dout <= 97;   end
         170 : begin
             dout <= 108;   end
         171 : begin
             dout <= 97;   end
         172 : begin
             dout <= 32;   end
         173 : begin
             dout <= 116;   end
         174 : begin
             dout <= 117;   end
         175 : begin
             dout <= 32;   end
         176 : begin
             dout <= 110;   end
         177 : begin
             dout <= 111;   end
         178 : begin
             dout <= 109;   end
         179 : begin
             dout <= 98;   end
         180 : begin
             dout <= 114;   end
         181 : begin
             dout <= 101;   end
         182 : begin
             dout <= 32;   end
         183 : begin
             dout <= 105;   end
         184 : begin
             dout <= 110;   end
         185 : begin
             dout <= 109;   end
         186 : begin
             dout <= 111;   end
         187 : begin
             dout <= 114;   end
         188 : begin
             dout <= 116;   end
         189 : begin
             dout <= 97;   end
         190 : begin
             dout <= 108;   end
         191 : begin
             dout <= 13;   end
         192 : begin
             dout <= 10;   end
endcase
 end
 endmodule

module mod_m_counter
   #(
    parameter N=27, // number of bits in counter
              M=100000000 // mod-M
   )
   (
    input wire clk, reset,
    output wire max_tick,
    output wire [N-1:0] q
   );

   //signal declaration
   reg [N-1:0] r_reg;
   wire [N-1:0] r_next;

   // body
   // register
   always @(posedge clk, posedge reset)
      if (reset)
         r_reg <= 0;
      else
         r_reg <= r_next;

   // next-state logic
   assign r_next = (r_reg==(M-1)) ? 0 : r_reg + 1;
   // output logic
   assign q = r_reg;
   assign max_tick = (r_reg==(M-1)) ? 1'b1 : 1'b0;

endmodule

module uart_rx
   #(
     parameter DBIT = 8,     // # data bits
               SB_TICK = 16  // # ticks for stop bits
   )
   (
    input wire clk, reset,
    input wire rx, s_tick,
    output reg rx_done_tick,
    output wire [7:0] dout
   );

   // symbolic state declaration
   localparam [1:0]
      idle  = 2'b00,
      start = 2'b01,
      data  = 2'b10,
      stop  = 2'b11;

   // signal declaration
   reg [1:0] state_reg, state_next;
   reg [3:0] s_reg, s_next;
   reg [2:0] n_reg, n_next;
   reg [7:0] b_reg, b_next;

   // body
   // FSMD state & data registers
   always @(posedge clk, posedge reset)
      if (reset)
         begin
            state_reg <= idle;
            s_reg <= 0;
            n_reg <= 0;
            b_reg <= 0;
         end
      else
         begin
            state_reg <= state_next;
            s_reg <= s_next;
            n_reg <= n_next;
            b_reg <= b_next;
         end

   // FSMD next-state logic
   always @*
   begin
      state_next = state_reg;
      rx_done_tick = 1'b0;
      s_next = s_reg;
      n_next = n_reg;
      b_next = b_reg;
      case (state_reg)
         idle:
            if (~rx)
               begin
                  state_next = start;
                  s_next = 0;
               end
         start:
            if (s_tick)
               if (s_reg==7)
                  begin
                     state_next = data;
                     s_next = 0;
                     n_next = 0;
                  end
               else
                  s_next = s_reg + 1;
         data:
            if (s_tick)
               if (s_reg==15)
                  begin
                     s_next = 0;
                     b_next = {rx, b_reg[7:1]};
                     if (n_reg==(DBIT-1))
                        state_next = stop ;
                      else
                        n_next = n_reg + 1;
                   end
               else
                  s_next = s_reg + 1;
         stop:
            if (s_tick)
               if (s_reg==(SB_TICK-1))
                  begin
                     state_next = idle;
                     rx_done_tick =1'b1;
                  end
               else
                  s_next = s_reg + 1;
      endcase
   end
   // output
   assign dout = b_reg;

endmodule

module uart_tx
   #(
     parameter DBIT = 8,     // # data bits
               SB_TICK = 16  // # ticks for stop bits
   )
   (
    input wire clk, reset,
    input wire tx_start, s_tick,
    input wire [7:0] din,
    output reg tx_done_tick,
    output wire tx
   );

   // symbolic state declaration
   localparam [1:0]
      idle  = 2'b00,
      start = 2'b01,
      data  = 2'b10,
      stop  = 2'b11;

   // signal declaration
   reg [1:0] state_reg, state_next;
   reg [3:0] s_reg, s_next;
   reg [2:0] n_reg, n_next;
   reg [7:0] b_reg, b_next;
   reg tx_reg, tx_next;

   // body
   // FSMD state & data registers
   always @(posedge clk, posedge reset)
      if (reset)
         begin
            state_reg <= idle;
            s_reg <= 0;
            n_reg <= 0;
            b_reg <= 0;
            tx_reg <= 1'b1;
         end
      else
         begin
            state_reg <= state_next;
            s_reg <= s_next;
            n_reg <= n_next;
            b_reg <= b_next;
            tx_reg <= tx_next;
         end

   // FSMD next-state logic & functional units
   always @*
   begin
      state_next = state_reg;
      tx_done_tick = 1'b0;
      s_next = s_reg;
      n_next = n_reg;
      b_next = b_reg;
      tx_next = tx_reg ;
      case (state_reg)
         idle:
            begin
               tx_next = 1'b1;
               if (tx_start)
                  begin
                     state_next = start;
                     s_next = 0;
                     b_next = din;
                  end
            end
         start:
            begin
               tx_next = 1'b0;
               if (s_tick)
                  if (s_reg==15)
                     begin
                        state_next = data;
                        s_next = 0;
                        n_next = 0;
                     end
                  else
                     s_next = s_reg + 1;
            end
         data:
            begin
               tx_next = b_reg[0];
               if (s_tick)
                  if (s_reg==15)
                     begin
                        s_next = 0;
                        b_next = b_reg >> 1;
                        if (n_reg==(DBIT-1))
                           state_next = stop ;
                        else
                           n_next = n_reg + 1;
                     end
                  else
                     s_next = s_reg + 1;
            end
         stop:
            begin
               tx_next = 1'b1;
               if (s_tick)
                  if (s_reg==(SB_TICK-1))
                     begin
                        state_next = idle;
                        tx_done_tick = 1'b1;
                     end
                  else
                     s_next = s_reg + 1;
            end
      endcase
   end
   // output
   assign tx = tx_reg;

endmodule


`default_nettype none

module tt_um_mini_aie_2x2 (
    input  wire [7:0] ui_in,    // Dedicated inputs - connected to the input switches
    output wire [7:0] uo_out,   // Dedicated outputs - connected to the 7 segment display
    /* verilator lint_off UNUSEDSIGNAL */
    input  wire [7:0] uio_in,   // IOs: Bidirectional Input path
    /* verilator lint_on UNUSEDSIGNAL */
    output wire [7:0] uio_out,  // IOs: Bidirectional Output path
    output wire [7:0] uio_oe,   // IOs: Bidirectional Enable path (active high: 0=input, 1=output)
    input  wire       ena,      // will go high when the design is enabled
    input  wire       clk,      // clock
    input  wire       rst_n     // reset_n - low to reset
);

  wire reset = !rst_n & ena;
  assign uio_oe = 8'b11111111;

  // intermediate regs between FIFOs and switches
  reg [7:0] switch_fifo_in[4];
  reg [7:0] switch_fifo_out[4];

  // intermediate regs between switches and PEs
  reg [7:0] switch_pe_out[4];
  reg [7:0] switch_pe_in[4];

  // intermediate regs between PEs
  reg [7:0] prev_pe_in[4];
  reg [7:0] next_pe_in[4];

  // control signals
  wire switch_fifo_rd_en[4];
  wire switch_fifo_wr_en[4];

  // generate noc and pe array
  generate
    genvar i;
    for (i = 0; i < 4; i = i + 1) begin : gen
      if (i == 0) begin
        synchronous_fifo #(
            .DEPTH(2),
            .DATA_WIDTH(8)
        ) fifo (
            .clk(clk),
            .rst_n(rst_n),
            .w_en(ena),
            .r_en(switch_fifo_rd_en[i]),
            .data_in(ui_in),
            .data_out(switch_fifo_in[i]),
            .full(),
            .empty()
        );
      end else begin
        synchronous_fifo #(
            .DEPTH(2),
            .DATA_WIDTH(8)
        ) fifo (
            .clk(clk),
            .rst_n(rst_n),
            .w_en(switch_fifo_wr_en[i-1]),
            .r_en(switch_fifo_rd_en[i]),
            .data_in(switch_fifo_out[i-1]),
            .data_out(switch_fifo_in[i]),
            .full(),
            .empty()
        );
      end

      switch #(
          .rank(i)
      ) switch (
          .clk(clk),
          .rst_n(rst_n),
          .switch_fifo_in(switch_fifo_in[i]),
          .switch_fifo_out(switch_fifo_out[i]),
          .pe_fifo_in(switch_pe_out[i]),
          .pe_fifo_out(switch_pe_in[i]),
          .rd_en(switch_fifo_rd_en[i]),
          .wr_en(switch_fifo_wr_en[i])
      );

      integer last = i == 3 ? 0 : i + 1;
      compute_tile pe (
          .clk(clk),
          .rst_n(rst_n),
          .switch_data_in(switch_pe_in[i]),
          .switch_data_out(switch_pe_out[i]),
          .prev_pe_data_in(next_pe_in[last]),
          .next_pe_data_out(next_pe_in[i]),
          .next_pe_data_in(prev_pe_in[last]),
          .prev_pe_data_out(prev_pe_in[i])
      );
    end

  endgenerate
  assign uo_out = switch_fifo_out[2];
  assign uio_out = 8'b00000000;

  always @(posedge clk or negedge rst_n) begin
    $display("------------");
    $display("uo_out: %d, uio_in[7]: %d", uo_out, uio_in[7]);
    $display("switch_fifo_out[0]: %b, switch_fifo_in[0]: %b", switch_fifo_out[0], switch_fifo_in[0]);
    $display("switch_fifo_out[1]: %b, switch_fifo_in[1]: %b", switch_fifo_out[1], switch_fifo_in[1]);
    $display("switch_fifo_out[2]: %b, switch_fifo_in[2]: %b", switch_fifo_out[2], switch_fifo_in[2]);
    $display("switch_fifo_out[3]: %b, switch_fifo_in[3]: %b", switch_fifo_out[3], switch_fifo_in[3]);
  end

endmodule

module synchronous_fifo #(parameter DEPTH=2, DATA_WIDTH=8) (
  input wire clk,
  input wire rst_n,
  input wire w_en,
  input wire r_en,
  input [DATA_WIDTH-1:0] data_in,
  output reg [DATA_WIDTH-1:0] data_out,
  output wire full, empty
);

  reg [$clog2(DEPTH)-1:0] w_ptr, r_ptr;
  reg [DATA_WIDTH-1:0] fifo[DEPTH];

  // Set Default values on reset.
  always@(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      w_ptr <= 0; r_ptr <= 0;
      data_out <= 0;

    end else begin
      if (w_en & !full) begin
        fifo[w_ptr] <= data_in;
        w_ptr <= w_ptr + 1;
      end

      if (r_en & !empty) begin
        data_out <= fifo[r_ptr];
        r_ptr <= r_ptr + 1;
      end
    end
  end

  assign full = ((w_ptr+1'b1) == r_ptr);
  assign empty = (w_ptr == r_ptr);

endmodule

// data_in[7:6] -> state
//   state = 00: CONFIG(WEIGHT)
//   state = 01: CONFIG(NEXT_PE, OP)
//   state = 10: START(OPERAND)
//   state = 11: END(OUTPUT)

module compute_tile
(
    input wire clk,
    input wire rst_n,
    input [7:0] switch_data_in,
    output reg [7:0] switch_data_out,
    output reg [7:0] next_pe_data_out,
    input [7:0] next_pe_data_in,
    input [7:0] prev_pe_data_in,
    output reg [7:0] prev_pe_data_out
);

    reg op_type;
    reg [3:0] weight;

    reg has_next_core;
    reg next_core_offset;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            op_type <= 1'b0;
            weight <= 4'b0000;
            next_pe_data_out <= 8'b00000000;
            prev_pe_data_out <= 8'b00000000;

            has_next_core <= 1'b0;
            next_core_offset <= 1'b0;
            switch_data_out <= 8'b00000000;

        end else if (switch_data_in[7:6] == 2'b00) begin
            // $display("compute_tile config weight: %d", switch_data_in);
            weight <= switch_data_in[3:0];

        end else if (switch_data_in[7:6] == 2'b01) begin
            has_next_core <= 1'b1;
            next_core_offset <= switch_data_in[4];
            op_type <= switch_data_in[0];

        end else begin
            if (has_next_core) begin
                next_pe_data_out <= switch_data_in;
                // case (next_core_offset)
                //     1'b0: next_pe_data_out <= switch_data_in;
                //     1'b1: prev_pe_data_out <= switch_data_in;
                // endcase
            end else begin
                switch_data_out <= weight + prev_pe_data_in + next_pe_data_in;
                // case (op_type)
                //     1'b0: switch_data_out <= weight + prev_pe_data_in + next_pe_data_in;
                //     1'b1: switch_data_out <= weight & prev_pe_data_in & next_pe_data_in;
                // endcase
            end
        end
    end
endmodule

module switch
#(parameter rank = 0)
(
    input wire clk, rst_n,
    input [7:0] switch_fifo_in,
    output reg [7:0] switch_fifo_out,
    input [7:0] pe_fifo_in,
    output reg [7:0] pe_fifo_out,
    output wire rd_en,
    output wire wr_en
);

reg [7:0] prev_switch_fifo_in;
reg [7:0] prev_pe_fifo_in;

wire pe_rd, fifo_rd;
assign pe_rd = pe_fifo_in != prev_pe_fifo_in;
assign fifo_rd = switch_fifo_in != prev_switch_fifo_in;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        switch_fifo_out <= 8'b00000000;
        pe_fifo_out <= 8'b00000000;
        prev_pe_fifo_in <= 8'b00000000;
        prev_switch_fifo_in <= 8'b00000001;

    end else begin
        if (pe_rd) begin
            prev_pe_fifo_in <= pe_fifo_in;
            switch_fifo_out <= {4'b1111, pe_fifo_in[3:0]};

        end else if (fifo_rd) begin
            prev_switch_fifo_in <= switch_fifo_in;
            if (switch_fifo_in[5:4] == rank) begin
                pe_fifo_out <= switch_fifo_in;
            end else begin
                switch_fifo_out <= switch_fifo_in;
            end
        end
    end
end

assign rd_en = 1'b1;
assign wr_en = 1'b1;

endmodule