`default_nettype none

module tt_um_btflv_subleq (
	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 in_miso = ui_in[0];

	assign uio_out     = data;
	assign uio_oe      = 8'b11111111;
	assign uo_out[0]   = out_mosi;
	assign uo_out[1]   = out_sck;
	assign uo_out[2]   = out_cs;
	assign uo_out[3]   = tx;
	assign uo_out[4]   = ctx;
	assign uo_out[7:5] = 3'b111;

	assign char_out     = data_a[7:0];
	assign char_valid   = char_output_flag;
	assign result       = data_b - data_a;

	reg signed [15:0] ir_a, ir_b, ir_c;
	reg signed [15:0] data_a, data_b;
	reg        [ 5:0] state, next_state;
	reg        [ 7:0] data            ;
	reg        [15:0] pc              ;
	reg               char_output_flag;
	reg               halted_reg      ;
	reg signed [15:0] data_to_ram     ;
	reg               ram_we          ;
	reg        [15:0] ram_addr        ;
	reg               tx_start        ;
	reg               ramstart        ;

	wire               ramdone      ;
	wire        [ 7:0] char_out     ;
	wire               char_valid   ;
	wire        [15:0] data_from_ram;
	wire               tx_busy      ;
	wire               ctx          ;
	wire               tx           ;
	wire signed [15:0] result       ;
	wire               in_miso      ;
	wire               out_mosi     ;
	wire               out_sck      ;
	wire               out_cs       ;

	SPI_FRAM_Interface ram (
		.clk     (clk          ),
		.rst_n   (rst_n        ),
		.addr    (ram_addr     ),
		.spi_miso(in_miso      ),
		.spi_mosi(out_mosi     ),
		.spi_sck (out_sck      ),
		.spi_cs  (out_cs       ),
		.data_in (data_to_ram  ),
		.we      (ram_we       ),
		.start   (ramstart     ),
		.done    (ramdone      ),
		.data_out(data_from_ram)
	);

	UART_Credits CreditsTX (
		.clk  (clk  ),
		.rst_n(rst_n),
		.tx   (ctx  )
	);

	UART_Transmitter uart_tx (
		.clk     (clk     ),
		.rst_n   (rst_n   ),
		.tx_start(tx_start),
		.tx_data (char_out),
		.tx      (tx      ),
		.tx_busy (tx_busy )
	);

	localparam START	 = 6'd0,
		FETCH_A0  = 6'd1,
		FETCH_A1  = 6'd2,
		FETCH_A2  = 6'd3,
		FETCH_B0  = 6'd4,
		FETCH_B1  = 6'd5,
		FETCH_B2  = 6'd6,
		FETCH_C0  = 6'd7,
		FETCH_C1  = 6'd8,
		FETCH_C2  = 6'd9,
		DECODE_A0 = 6'd10,
		DECODE_A1 = 6'd11,
		DECODE_A2 = 6'd12,
		DECODE_B0 = 6'd13,
		DECODE_B1 = 6'd14,
		DECODE_B2 = 6'd15,
		WRITE_B0 = 6'd16,
		WRITE_B1 = 6'd17,
		WRITE_B2 = 6'd18,
		HALT     = 6'd19;

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n)
			state <= START;
		else
			state <= next_state;
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			pc               <= 15'd0;
			ir_a             <= 16'b0;
			ir_b             <= 16'b0;
			ir_c             <= 16'b0;
			data_a           <= 16'b0;
			data_b           <= 16'b0;
			char_output_flag <= 1'b0;
			halted_reg       <= 1'b0;
			data_to_ram      <= 16'b0;
			ram_addr         <= 15'b0;
			ram_we           <= 1'b0;
		end else begin
			case (state)

				START : begin
					ram_addr <= pc;
					ramstart <= 1'b1;
				end

				// Fetch A B C
				FETCH_A0 : begin
					ram_addr <= pc;
					ramstart <= 1'b1;
				end

				FETCH_A1 : begin
					ramstart <= 1'b0;
				end

				FETCH_A2 : begin
					ir_a <= data_from_ram;
				end

				FETCH_B0 : begin
					ram_addr <= pc + 1;
					ramstart <= 1'b1;
				end

				FETCH_B1 : begin
					ramstart <= 1'b0;
				end

				FETCH_B2 : begin
					ir_b <= data_from_ram;
				end

				FETCH_C0 : begin
					ram_addr <= pc + 2;
					ramstart <= 1'b1;
				end

				FETCH_C1 : begin
					ramstart <= 1'b0;
				end

				FETCH_C2 : begin
					ir_c <= data_from_ram;
				end

				// Decode A B
				DECODE_A0 : begin
					ram_addr <= ir_a;
					ramstart <= 1'b1;
				end

				DECODE_A1 : begin
					ramstart <= 1'b0;
				end

				DECODE_A2 : begin
					data_a <= data_from_ram;
				end

				DECODE_B0 : begin
					if(ir_b == -1) begin
						char_output_flag <= 1'b1;
						data             <= char_out;
						ram_addr         <= 16'b0;
						ramstart         <= 1'b1;
					end else begin
						ram_addr <= ir_b;
						ramstart <= 1'b1;
					end
					if(ir_c < 0) begin
						halted_reg <= 1'b1;
					end
				end

				DECODE_B1 : begin
					ramstart         <= 1'b0;
					char_output_flag <= 1'b0;
				end

				DECODE_B2 : begin
					if(ir_b == -1) begin
						data_b <= 16'b0;
					end else begin
						data_b <= data_from_ram;
					end
				end

				// Write B-A to address B
				WRITE_B0 : begin
					if(ir_b == -1) begin
						ram_addr <= 16'b0;
						ramstart <= 1'b1;
						ram_we   <= 1'b0;
					end else begin
						ram_addr    <= ir_b;
						ramstart    <= 1'b1;
						ram_we      <= 1'b1;
						data_to_ram <= result;
					end
				end

				WRITE_B1 : begin
					ramstart <= 1'b0;
				end

				WRITE_B2 : begin
					pc     <= (result > 0 || ir_b == -1) ? pc + 3 : ir_c;
					ram_we <= 1'b0;
				end

				HALT : begin
					halted_reg <= 1'b1;
				end

			endcase
		end
	end

	always @(*) begin
		if(halted_reg)
			next_state = HALT;
		else
			case (state)
				START     : next_state = FETCH_A0;
				FETCH_A0  : next_state = FETCH_A1;
				FETCH_A1  : next_state = ramdone ? FETCH_A2 : FETCH_A1;
				FETCH_A2  : next_state = FETCH_B0;
				FETCH_B0  : next_state = FETCH_B1;
				FETCH_B1  : next_state = ramdone ? FETCH_B2 : FETCH_B1;
				FETCH_B2  : next_state = FETCH_C0;
				FETCH_C0  : next_state = FETCH_C1;
				FETCH_C1  : next_state = ramdone ? FETCH_C2 : FETCH_C1;
				FETCH_C2  : next_state = DECODE_A0;
				DECODE_A0 : next_state = DECODE_A1;
				DECODE_A1 : next_state = ramdone ? DECODE_A2 : DECODE_A1;
				DECODE_A2 : next_state = DECODE_B0;
				DECODE_B0 : next_state = DECODE_B1;
				DECODE_B1 : next_state = ramdone ? DECODE_B2 : DECODE_B1;
				DECODE_B2 : next_state = !tx_busy ? WRITE_B0 : DECODE_B2;
				WRITE_B0  : next_state = WRITE_B1;
				WRITE_B1  : next_state = ramdone ? WRITE_B2 : WRITE_B1;
				WRITE_B2  : next_state = FETCH_A0;
				HALT      : next_state = HALT;
			endcase
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			tx_start <= 1'b0;
		end else begin
			if (char_valid && !tx_busy) begin
				tx_start <= 1'b1;
			end else begin
				tx_start <= 1'b0;
			end
		end
	end

endmodule

module SPI_FRAM_Interface (
    input  wire            clk     ,
    input  wire            rst_n   ,
    input                  spi_miso,
    output      reg        spi_mosi,
    output      reg        spi_sck ,
    output      reg        spi_cs  ,
    input  wire     [15:0] addr    ,
    input  wire     [15:0] data_in ,
    input  wire            we      ,
    input  wire            start   ,
    output      reg [15:0] data_out,
    output      reg        done
);

    assign write_data_l = data_in[7:0];
    assign write_data_h = data_in[15:8];
    assign address      = (addr[14:0] << 1)  + !hbyte;
    assign write_enable = we;

    reg [7:0] temp_data  ;
    reg [4:0] state      ;
    reg [4:0] bit_counter;
    reg [5:0] spi_clk    ;
    reg       clk_out    ;
    reg       hbyte      ;

    wire [ 7:0] write_data_l;
    wire [ 7:0] write_data_h;
    wire        write_enable;
    wire [15:0] address     ;

    parameter CMD_READ  = 8'h03;
    parameter CMD_WRITE = 8'h02;
    parameter CMD_WREN  = 8'h06;
    parameter CMD_WRDI  = 8'h04;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            spi_clk <= 6'd0;
            clk_out <= 1'b0;
        end else begin
            if (spi_clk == 6'd7) begin
                spi_clk <= 6'd0;
                clk_out <= ~clk_out;
            end else begin
                spi_clk <= spi_clk + 1;
            end
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            spi_cs      <= 1;
            spi_sck     <= 0;
            spi_mosi    <= 0;
            state       <= 0;
            bit_counter <= 0;
            hbyte       <= 0;
        end else begin
            case (state)
                0 : begin
                    done <= 0;
                    if (start && write_enable) begin
                        state    <= 6;
                        spi_mosi <= CMD_WREN[7];
                    end else if (start || hbyte) begin
                        state    <= 1;
                        spi_cs   <= 0;
                        spi_mosi <= 0;
                        spi_sck  <= 0;
                    end
                end
                1 : begin // Send read command
                    if (bit_counter < 8) begin
                        spi_mosi <= CMD_READ[7 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= address[15];
                        spi_sck     <= 0;
                        state       <= 2;
                    end
                end
                2 : begin // Send address (16 bits)
                    if (bit_counter < 16) begin
                        spi_mosi <= address[15 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= 0;
                        spi_sck     <= 0;
                        state       <= 3;
                    end
                end
                3 : begin // Wait
                    if (bit_counter < 8) begin
                        bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        state       <= 4;
                    end
                end
                4 : begin // Read data byte
                    if (bit_counter < 8) begin
                        spi_sck <= ~spi_sck;
                        if (~spi_sck) temp_data[7 - bit_counter] <= spi_miso;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        if(hbyte)
                            begin
                                data_out[15:8] <= temp_data;
                            end
                        else
                            begin
                                data_out[7:0] <= temp_data;
                            end
                        bit_counter <= 0;
                        spi_mosi    <= 0;
                        spi_sck     <= 0;
                        state       <= 5;
                        hbyte       <= ~hbyte;
                    end
                end
                5 : begin // End communication
                    spi_cs <= 1;
                    if(hbyte)
                        begin
                            state <= 0;
                        end
                    else
                        begin
                            state <= 16;
                        end
                end
                6 : begin // Send Write Enable (WREN) command
                    spi_cs <= 0;
                    if (bit_counter < 8) begin
                        spi_mosi <= CMD_WREN[7 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= 0;
                        spi_sck     <= 0;
                        state       <= 7;
                    end
                end
                7 : begin // End WREN command
                    spi_cs <= 1;
                    state  <= 8;
                end
                8 : begin // Wait
                    if (bit_counter < 8) begin
                        bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        state       <= 9;
                        spi_mosi    <= CMD_WRITE[7];
                    end
                end
                9 : begin // Send write command
                    spi_cs <= 0;
                    if (bit_counter < 8) begin
                        spi_mosi <= CMD_WRITE[7 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= address[15];
                        spi_sck     <= 0;
                        state       <= 10;
                    end
                end
                10 : begin // Send address (16 bits) for writing
                    if (bit_counter < 16) begin
                        spi_mosi <= address[15 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_sck     <= 0;
                        state       <= 11;
                        if(hbyte) begin
                            spi_mosi <= write_data_h[7];
                        end else begin
                            spi_mosi <= write_data_l[7];
                        end
                    end
                end
                11 : begin // Write data byte
                    if (bit_counter < 8) begin
                        if(hbyte) begin
                            spi_mosi <= write_data_h[7 - bit_counter];
                        end else begin
                            spi_mosi <= write_data_l[7 - bit_counter];
                        end
                        spi_sck <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= 0;
                        spi_sck     <= 0;
                        state       <= 12;
                        hbyte       <= ~hbyte;
                    end
                end
                12 : begin // End write communication
                    spi_cs <= 1;
                    state  <= 13;
                end
                13 : begin // Wait
                    if (bit_counter < 8) begin
                        bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        state       <= 14;
                        spi_mosi    <= CMD_WRDI[7];
                    end
                end
                14 : begin // Send Write Disable (WRDI) command
                    spi_cs <= 0;
                    if (bit_counter < 8) begin
                        spi_mosi <= CMD_WRDI[7 - bit_counter];
                        spi_sck  <= ~spi_sck;
                        if (!spi_sck) bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        spi_mosi    <= 0;
                        spi_sck     <= 0;
                        state       <= 15;
                    end
                end
                15 : begin // End WRDI command
                    spi_cs <= 1;
                    if(hbyte)
                        begin
                            state <= 6;
                        end
                    else
                        begin
                            state <= 16;
                        end
                end
                16 : begin // Wait
                    if (bit_counter < 8) begin
                        bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        state       <= 0;
                        done        <= 1;
                    end
                end
            endcase
        end
    end
endmodule

module UART_Credits (
    input  wire     clk  ,
    input  wire     rst_n,
    output      reg tx
);

    reg [ 1:0] state              = INIT       ;
    reg [31:0] clk_counter        = 0          ;
    reg [ 3:0] bit_counter        = 0          ;
    reg [ 3:0] char_counter       = 0          ;
    reg [ 7:0] tx_shift_reg       = 8'b11111111;
    reg [31:0] idle_counter       = 0          ;
    reg [ 7:0] MESSAGE     [0:10]              ;

    parameter CLK_FREQ   = 10000000; // 10 MHz
    parameter BAUD_RATE  = 115200  ; // 115200 baud
    parameter BIT_PERIOD = 87      ; // CLK_FREQ / BAUD_RATE
    parameter BIT_COUNT  = 10      ; // 1 start bit, 8 data bits, 1 stop bit
    parameter IDLE_COUNT = 8700    ; // Delay

    localparam [7:0] CHAR_P     = 8'd80 ;
    localparam [7:0] CHAR_h     = 8'd104;
    localparam [7:0] CHAR_i     = 8'd105;
    localparam [7:0] CHAR_l     = 8'd108;
    localparam [7:0] CHAR_i2    = 8'd105;
    localparam [7:0] CHAR_p     = 8'd112;
    localparam [7:0] CHAR_space = 8'd32 ;
    localparam [7:0] CHAR_M     = 8'd77 ;
    localparam [7:0] CHAR_o     = 8'd111;
    localparam [7:0] CHAR_h2    = 8'd104;
    localparam [7:0] CHAR_r     = 8'd114;

    localparam INIT = 0, IDLE = 1, START = 2, TRANSMIT = 3;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            clk_counter  <= 0;
            bit_counter  <= 0;
            char_counter <= 0;
            tx_shift_reg <= 8'b11111111;
            idle_counter <= 0;
            tx           <= 1'b1;
            state        <= INIT;
        end else begin
            case (state)
                INIT : begin
                    MESSAGE[0]  <= CHAR_P;
                    MESSAGE[1]  <= CHAR_h;
                    MESSAGE[2]  <= CHAR_i;
                    MESSAGE[3]  <= CHAR_l;
                    MESSAGE[4]  <= CHAR_i2;
                    MESSAGE[5]  <= CHAR_p;
                    MESSAGE[6]  <= CHAR_space;
                    MESSAGE[7]  <= CHAR_M;
                    MESSAGE[8]  <= CHAR_o;
                    MESSAGE[9]  <= CHAR_h2;
                    MESSAGE[10] <= CHAR_r;
                    state       <= START;
                end
                IDLE : begin
                    if (idle_counter < IDLE_COUNT) begin
                        idle_counter <= idle_counter + 1;
                    end else begin
                        idle_counter <= 0;
                        state        <= START;
                    end
                end
                START : begin
                    char_counter <= 0;
                    tx_shift_reg <= MESSAGE[0];
                    state        <= TRANSMIT;
                end
                TRANSMIT : begin
                    if (clk_counter < BIT_PERIOD) begin
                        clk_counter <= clk_counter + 1;
                    end else begin
                        clk_counter <= 0;
                        if (bit_counter < BIT_COUNT) begin
                            bit_counter <= bit_counter + 1;
                            case (bit_counter)
                                0 : tx <= 1'b0;
                                1 : tx <= tx_shift_reg[0];
                                2 : tx <= tx_shift_reg[1];
                                3 : tx <= tx_shift_reg[2];
                                4 : tx <= tx_shift_reg[3];
                                5 : tx <= tx_shift_reg[4];
                                6 : tx <= tx_shift_reg[5];
                                7 : tx <= tx_shift_reg[6];
                                8 : tx <= tx_shift_reg[7];
                                9 : tx <= 1'b1;
                            endcase
                        end else begin
                            bit_counter <= 0;
                            if (char_counter < 10) begin
                                char_counter <= char_counter + 1;
                                tx_shift_reg <= MESSAGE[char_counter + 1];
                            end else begin
                                char_counter <= 0;
                                state        <= IDLE;
                            end
                        end
                    end
                end
            endcase
        end
    end
endmodule

module UART_Transmitter (
    input  wire           clk     ,
    input  wire           rst_n   ,
    input  wire           tx_start,
    input  wire     [7:0] tx_data ,
    output      reg       tx      ,
    output      reg       tx_busy
);

    reg [15:0] baud_counter;
    reg [ 3:0] bit_counter ;
    reg [ 7:0] shift_reg   ;
    reg        transmitting;

    localparam CLK_FREQ   = 10000000; // 10 MHz
    localparam BAUD_RATE  = 115200  ; // 115200 baud
    localparam BIT_PERIOD = 87      ; // CLK_FREQ / BAUD_RATE

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx           <= 1'b1;
            tx_busy      <= 1'b0;
            transmitting <= 1'b0;
            baud_counter <= 0;
            bit_counter  <= 0;
            shift_reg    <= 0;
        end else begin
            if (tx_start && !transmitting) begin
                transmitting <= 1'b1;
                tx_busy      <= 1'b1;
                shift_reg    <= tx_data;
                bit_counter  <= 0;
                baud_counter <= 0;
                tx           <= 1'b0;
            end else if (transmitting) begin
                if (baud_counter < BIT_PERIOD - 1) begin
                    baud_counter <= baud_counter + 1;
                end else begin
                    baud_counter <= 0;
                    if (bit_counter < 8) begin
                        tx          <= shift_reg[0];
                        shift_reg   <= shift_reg >> 1;
                        bit_counter <= bit_counter + 1;
                    end else if (bit_counter == 8) begin
                        tx          <= 1'b1;
                        bit_counter <= bit_counter + 1;
                    end else begin
                        tx_busy      <= 1'b0;
                        transmitting <= 1'b0;
                        bit_counter  <= 0;
                    end
                end
            end
        end
    end
endmodule


/*
 * Copyright (c) 2024 Your Name
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

module tt_um_lfsr (
    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
);

    wire rst = ! rst_n;
    assign uio_oe = 8'b00000000;
    assign uio_out = 8'b00000000;
    assign uo_out[7:3] = 5'b00000;

    wb_lfsr wb_lfsr(
      .i_clk(clk),
      .i_reset(rst),
      .i_wb_cyc(uio_in[0]),
      .i_wb_stb(uio_in[1]),
      .i_wb_we(uio_in[2]),
      .i_wb_addr(uio_in[5:3]),
      .i_wb_data(ui_in),
      .o_wb_stall(uo_out[0]),
      .o_wb_data(uo_out[1]),
      .o_wb_ack(uo_out[2])
);

endmodule

module linear_feedback_shift_register(out, clock, reset, seed, load_seed);
	output reg out;
	input [31:0] seed;
	input load_seed;
	input reset;
	input clock;
	wire [31:0] flip_flop_outputs;
	wire [31:0] flip_flop_inputs;
	wire feedback1, feedback2, feedback3;
	flip_flop flip_flop_instance[31:0] (flip_flop_outputs, clock, reset, flip_flop_inputs);
	one_bit_mux muxes[31:0] (flip_flop_inputs, load_seed, seed, {flip_flop_outputs[30:0], feedback3});
	xor feedback_gate_one(feedback1, flip_flop_outputs[6], flip_flop_outputs[31]);
	xor feedback_gate_two(feedback2, feedback1, flip_flop_outputs[5]);
	xor feedback_gate_three(feedback3, feedback2, flip_flop_outputs[1]);
	assign out = feedback3;
endmodule

module flip_flop(out, clock, reset, in);
	input clock;
	input reset;
	input in;
	output reg out;
	always @(posedge clock or posedge reset)
	begin

	if (reset)
		out = 0;
	else
		out = in;
	end
endmodule

module one_bit_mux(out, control, input_a, input_b);
	output reg out;
	input control, input_a, input_b;
	wire not_control;
	always @(control or not_control or input_a or input_b)
		out = (control & input_a) | (not_control & input_b);
		not (not_control, control);
endmodule

module wb_lfsr(i_clk, i_reset, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, o_wb_stall, o_wb_data, o_wb_ack);
	input i_clk;
	input i_reset;
	input i_wb_cyc;
	input i_wb_stb;
	input i_wb_we;
	input [2:0] i_wb_addr;
	input[7:0] i_wb_data;

	output reg o_wb_stall;
	output reg o_wb_ack;
	output reg o_wb_data;

	reg [31:0] seed;
    reg lfsr_reset;
	reg load_seed;
	wire lfsr_out;

	linear_feedback_shift_register FLSR_instance(lfsr_out, i_clk, lfsr_reset, seed, load_seed);

	assign	o_wb_stall = 1'b0;

	always @(posedge i_clk)
	if ((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall)&&(i_wb_cyc))
	begin
		case (i_wb_addr)
			3'b000 :	seed[7:0] <= i_wb_data;
			3'b001 :	seed[15:8] <= i_wb_data;
			3'b010 :	seed[23:16] <= i_wb_data;
			3'b011 :	seed[31:24] <= i_wb_data;
			3'b100 :
			begin
			    lfsr_reset <= i_wb_data[0];
        		load_seed <= i_wb_data[1];
        	end
			default:	seed <= 0;
		endcase
	end

	always @(posedge i_clk)
	if ((i_wb_stb)&&(!i_wb_we)&&(!o_wb_stall)&&(i_wb_cyc))
	begin
        o_wb_data <= lfsr_out;
	end

	initial	o_wb_ack = 1'b0;

	always @(posedge i_clk)
	if (i_reset) begin
		o_wb_ack <= 1'b0;
	end else
		o_wb_ack <= ((i_wb_stb) && (!o_wb_stall));

endmodule


module machine (
    input wire clk,
    input wire reset,
    input wire changes,
    input wire finished,
    output reg [1:0] state
);

parameter state_data_IN = 2'b00;
parameter state_data_BUFF = 2'b01;
parameter state_data_OUT = 2'b10;

//Definir current_state y next_state
reg [1:0] current_state, next_state;

always@(posedge clk) begin
    if (reset)
        current_state <= state_data_IN;
    else
        current_state <= next_state;
end

always @(*) begin
    next_state = current_state;
    case (current_state)
        state_data_IN: if (changes) begin
                        next_state = state_data_BUFF;
                        end
        state_data_BUFF: if (changes) begin
                        next_state = state_data_OUT;
                        end
        state_data_OUT: if (changes) begin
                        next_state = state_data_BUFF;
                        end else if (finished) begin
                        next_state = state_data_IN; //show the results
                        end
        default: if (changes) begin
                next_state=current_state;
                end
    endcase
end

always@(posedge clk) begin
    if (reset)
        state <= state_data_IN;
    else
        state <= current_state;
end
endmodule

module perceptron(
    input [7:0] in0,
    input [7:0] in1,
    input [7:0] in2,
    input [7:0] in3,

    input [7:0] weight0, //aaa
    input [7:0] weight1,
    input [7:0] weight2,
    input [7:0] weight3,
    input [7:0] bias,
    input [7:0] threshold,

    output reg [7:0] out
);

reg [7:0] sum_p;
reg [7:0] res;

always@(*)
begin

    sum_p=(in0<<weight0)+(in1<<weight1)+(in2<weight2)+(in3<<weight3)+bias;
    res = sum_p - threshold;
    if (res[7])
        out = 8'b00000000;
    else
        out = sum_p;
end

endmodule

module register_parameters(
    input wire clk,
    input wire reset,
    input wire [7:0] data_in,
    input [1:0] selector,

    //output reg [7:0] th3,
    output reg [7:0] b3,
    output reg [7:0] w33,
    output reg [7:0] w32,
    output reg [7:0] w31,
    output reg [7:0] w30,
    //output reg [7:0] th2,
    output reg [7:0] b2,
    output reg [7:0] w23,
    output reg [7:0] w22,
    output reg [7:0] w21,
    output reg [7:0] w20,
    //output reg [7:0] th1,
    output reg [7:0] b1,
    output reg [7:0] w13,
    output reg [7:0] w12,
    output reg [7:0] w11,
    output reg [7:0] w10,
    //output reg [7:0] th0,
    output reg [7:0] b0,
    output reg [7:0] w03,
    output reg [7:0] w02,
    output reg [7:0] w01,
    output reg [7:0] w00
);

always @(posedge clk)
begin
    if (reset) begin
    w00<=8'd0;
    w01<=8'd0;
    w02<=8'd0;
    w03<=8'd0;
    b0<=8'd0;
    //th0<=8'd0;
    w10<=8'd0;
    w11<=8'd0;
    w12<=8'd0;
    w13<=8'd0;
    b1<=8'd0;
    //th1<=8'd0;
    w20<=8'd0;
    w21<=8'd0;
    w22<=8'd0;
    w23<=8'd0;
    b2<=8'd0;
    //th2<=8'd0;
    w30<=8'd0;
    w31<=8'd0;
    w32<=8'd0;
    w33<=8'd0;
    b3<=8'd0;
    //th3<=8'd0;
    end

    else begin
        case(selector)
            2'b00   :   begin
                        w00<=w00;
                        w01<=w01;
                        w02<=w02;
                        w03<=w03;
                        b0<=b0;
                        //th0<=th0;
                        w10<=w11;
                        w11<=w11;
                        w12<=w12;
                        w13<=w13;
                        b1<=b1;
                        //th1<=th1;
                        w20<=w21;
                        w21<=w21;
                        w22<=w22;
                        w23<=w23;
                        b2<=b2;
                        //th2<=th2;
                        w30<=w31;
                        w31<=w31;
                        w32<=w32;
                        w33<=w33;
                        b3<=b3;
                        //th3<=th3;
                        end
            2'b01   :   begin
                        w00<=w01;
                        w01<=w02;
                        w02<=w03;
                        w03<=b0;
                        //b0<=th0;
                        b0<=w10;
                        //th0<=w10;
                        w10<=w11;
                        w11<=w12;
                        w12<=w13;
                        w13<=b1;
                        //b1<=th1;
                        b1<=w20;
                        //th1<=w20;
                        w20<=w21;
                        w21<=w22;
                        w22<=w23;
                        w23<=b2;
                        //b2<=th2;
                        b2<=w30;
                        //th2<=w30;
                        w30<=w31;
                        w31<=w32;
                        w32<=w33;
                        w33<=b3;
                        //b3<=th3;
                        b3<=data_in;
                        //th3<=data_in;
                        end
            2'b10   :   begin
                        w00<=w00;
                        w01<=w01;
                        w02<=w02;
                        w03<=w03;
                        b0<=b0;
                        //th0<=th0;
                        w10<=w11;
                        w11<=w11;
                        w12<=w12;
                        w13<=w13;
                        b1<=b1;
                        //th1<=th1;
                        w20<=w21;
                        w21<=w21;
                        w22<=w22;
                        w23<=w23;
                        b2<=b2;
                        //th2<=th2;
                        w30<=w31;
                        w31<=w31;
                        w32<=w32;
                        w33<=w33;
                        b3<=b3;
                        //th3<=th3;
                        end
            default :   begin
                        w00<=w00;
                        w01<=w01;
                        w02<=w02;
                        w03<=w03;
                        b0<=b0;
                        //th0<=th0;
                        w10<=w11;
                        w11<=w11;
                        w12<=w12;
                        w13<=w13;
                        b1<=b1;
                        //th1<=th1;
                        w20<=w21;
                        w21<=w21;
                        w22<=w22;
                        w23<=w23;
                        b2<=b2;
                        //th2<=th2;
                        w30<=w31;
                        w31<=w31;
                        w32<=w32;
                        w33<=w33;
                        b3<=b3;
                        //th3<=th3;
                        end
            endcase
    end
end

endmodule

module shift_register_inputs(
    input clk,rstn,
    input [7:0] data_in,
    input [1:0] selector,  // From the state machine. Determines where do the neuron inputs come From
    //input [1:0] selector_output,

    // The outputs of the neurons0..3 are inputs to the shift register, so it can use them as inputs to the next layer
    input [7:0] neuron0_output, // The output of the neuron0, is an input to the shift register
    input [7:0] neuron1_output, // The output of the neuron1, is an input to the shift register
    input [7:0] neuron2_output, // The output of the neuron2, is an input to the shift register
    input [7:0] neuron3_output, // The output of the neuron3, is an input to the shift register

    // These 4 outputs are the inputs0..3 to each neuron.
    output reg [7:0] neuron_input0, // Input 0 for all neurons on the current layer
    output reg [7:0] neuron_input1, // Input 1 for all neurons on the current layer
    output reg [7:0] neuron_input2, // Input 2 for all neurons on the current layer
    output reg [7:0] neuron_input3, // Input 3 for all neurons on the current layer

    // Once all layers have passed, when we feed 4 new inputs to the network, the outputs of the network
    // will appear here, from neuron3_output to neuron0_output
    output reg [7:0] network_outputs
);

always@(posedge clk)
begin

    if(rstn)
    begin
        neuron_input0 <= 8'b00000000;
        neuron_input1 <= 8'b00000000;
        neuron_input2 <= 8'b00000000;
        neuron_input3 <= 8'b00000000;
        network_outputs <= 8'b00000000;
    end

    else
    begin
        case(selector)
            2'b00   :   begin                                   // Receives new data
                            neuron_input0 <= data_in;           // and pases it thorugh the neurons.
                            neuron_input1 <= neuron_input0;
                            neuron_input2 <= neuron_input1;
                            neuron_input3 <= neuron_input2;
                            network_outputs <= neuron_input3;

                        end
            2'b01   :   begin
                            neuron_input0 <= neuron_input0;
                            neuron_input1 <= neuron_input1;
                            neuron_input2 <= neuron_input2;
                            neuron_input3 <= neuron_input3;
                            network_outputs <= network_outputs;
                        end
            2'b10   :   begin
                            neuron_input0 <= neuron0_output;
                            neuron_input1 <= neuron1_output;
                            neuron_input2 <= neuron2_output;
                            network_outputs <= network_outputs;
                        end
            default :   begin                               // The default state keeps everything as it was
                            neuron_input0 <= neuron_input0;
                            neuron_input1 <= neuron_input1;
                            neuron_input2 <= neuron_input2;
                            neuron_input3 <= neuron_input3;
                            network_outputs <= network_outputs;
                        end

        endcase
    end
/*
    begin
        case(selector_output)
            2'b00   :   begin
                            network_outputs <= neuron_input0;
            end
            2'b01   :   begin
                            network_outputs <= neuron_input1;
            end
            2'b10   :   begin
                            network_outputs <= neuron_input2;
            end
            2'b11   :   begin
                            network_outputs <= neuron_input3;
            end
            default: begin
                    network_outputs <= network_outputs;
            end
        endcase
    end
    */
end

endmodule

module tt_um_neural_network (
    input wire clk,
    input wire rst_n,
    input wire [7:0] uio_in,
    input wire [7:0] ui_in,

    output wire [7:0] uo_out,
    output wire [7:0] uio_oe,
    output wire [7:0] uio_out,

    input wire ena
);
assign uio_oe = 0;
assign uio_out = 0;
// Señales internas
wire [1:0] state;
wire [7:0] neuron_input0, neuron_input1, neuron_input2, neuron_input3;
wire [7:0] neuron0_output, neuron1_output, neuron2_output, neuron3_output;
wire [7:0] w00, w01, w02, w03, b0;
wire [7:0] w10, w11, w12, w13, b1;
wire [7:0] w20, w21, w22, w23, b2;
wire [7:0] w30, w31, w32, w33, b3;

// Instanciando los módulos
machine machine_inst (
    .clk(clk),
    .reset(~rst_n),
    .changes(uio_in[1]),
    .state(state),
    .finished(uio_in[0])
);

shift_register_inputs shift_reg_inst (
    .clk(clk),
    .rstn(~rst_n),
    .data_in(ui_in),
    .selector(state),
    //.selector_output(uio_in[1:0]),
    .neuron0_output(neuron0_output),
    .neuron1_output(neuron1_output),
    .neuron2_output(neuron2_output),
    .neuron3_output(neuron3_output),
    .neuron_input0(neuron_input0),
    .neuron_input1(neuron_input1),
    .neuron_input2(neuron_input2),
    .neuron_input3(neuron_input3),
    .network_outputs(uo_out)
);

perceptron perceptron0 (
    .in0(neuron_input0),
    .in1(neuron_input1),
    .in2(neuron_input2),
    .in3(neuron_input3),
    .weight0(w00),
    .weight1(w01),
    .weight2(w02),
    .weight3(w03),
    .bias(b0),
    .threshold(8'd0),
    .out(neuron0_output)
);

perceptron perceptron1 (
    .in0(neuron_input0),
    .in1(neuron_input1),
    .in2(neuron_input2),
    .in3(neuron_input3),
    .weight0(w10),
    .weight1(w11),
    .weight2(w12),
    .weight3(w13),
    .bias(b1),
    .threshold(8'd0),
    .out(neuron1_output)
);

perceptron perceptron2 (
    .in0(neuron_input0),
    .in1(neuron_input1),
    .in2(neuron_input2),
    .in3(neuron_input3),
    .weight0(w20),
    .weight1(w21),
    .weight2(w22),
    .weight3(w23),
    .bias(b2),
    .threshold(8'd0),
    .out(neuron2_output)
);

perceptron perceptron3 (
    .in0(neuron_input0),
    .in1(neuron_input1),
    .in2(neuron_input2),
    .in3(neuron_input3),
    .weight0(w30),
    .weight1(w31),
    .weight2(w32),
    .weight3(w33),
    .bias(b3),
    .threshold(8'd0),
    .out(neuron3_output)
);

register_parameters reg_params_inst (
    .clk(clk),
    .reset(~rst_n),
    .data_in(ui_in),
    .selector(state),
    //.th3(th3),
    .b3(b3),
    .w33(w33),
    .w32(w32),
    .w31(w31),
    .w30(w30),
    //.th2(th2),
    .b2(b2),
    .w23(w23),
    .w22(w22),
    .w21(w21),
    .w20(w20),
    //.th1(th1),
    .b1(b1),
    .w13(w13),
    .w12(w12),
    .w11(w11),
    .w10(w10),
    //.th0(th0),
    .b0(b0),
    .w03(w03),
    .w02(w02),
    .w01(w01),
    .w00(w00)
);

endmodule


module tt_um_TD4_Assy_KosugiSubaru (
    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
);

wire [7:0] data;
wire [3:0] port_i, port_o, addres, ALU_to_reg, sel_to_ALU, regA_to_sel, regB_to_sel, load, op, im;
wire [1:0] select;
wire carry, cf, co_from_pc;

reg [3:0] uio_oe_reg;

assign uio_oe [4] = 1'b0;
assign uio_out [4] = 1'b0;
assign uio_oe [7:5] = 3'b111;
assign uio_oe [3:0] = uio_oe_reg [3:0];

assign port_i [3:0] = uio_in [3:0];
assign uio_out [3:0] = port_o [3:0];

assign uo_out [3:0] = addres[3:0];
assign data [7:0] = ui_in [7:0];
assign op = data[7:4], im = data[3:0];

assign uo_out [4] = cf;
assign uo_out [7:5] = ALU_to_reg [2:0];
assign uio_out [5] = ALU_to_reg [3];
assign uio_out [7:6] = select [1:0];

always @(uio_in [4]) begin
    if (uio_in [4] == 1'b0) begin
        uio_oe_reg [3:0] <= 4'b0000;
    end else if (uio_in [4] == 1'b1) begin
        uio_oe_reg [3:0] <= 4'b1111;
    end
end

register_ff_4bit reg_A (.in(ALU_to_reg), .out(regA_to_sel), .ld(~load [0]), .clk(clk), .rst(rst_n));
register_ff_4bit reg_B (.in(ALU_to_reg), .out(regB_to_sel), .ld(~load[1]), .clk(clk), .rst(rst_n));
register_ff_4bit OUT   (.in(ALU_to_reg), .out(port_o), .ld(~load [2]), .clk(clk), .rst(rst_n));

ALU_adder_4bit ALU0    (.in_a(sel_to_ALU), .in_b(im), .out(ALU_to_reg), .ci(1'b0), .co(carry));

pc       pc0  (.in(ALU_to_reg), .out(addres), .ld(~load [3]), .clk(clk), .rst(rst_n), .co(co_from_pc));

decoder  dec0 (.op(op), .c_n(~cf), .s(select), .ld_n(load));

selector sel0 (.in_a(regA_to_sel), .in_b(regB_to_sel), .in_c(port_i), .in_d(4'b0000), .s(select), .out(sel_to_ALU));

ff_1bit  cf0  (.in(carry), .out(cf), .clk(clk), .rst(rst_n), .pr(1'b1));

endmodule

module register_ff_4bit(in, out, ld, clk, rst);
    input [3:0] in;
    input ld, clk, rst;
    output [3:0] out;

    reg [3:0] out;
    always @(posedge clk or negedge rst) begin
        if (rst ==1'b0) begin
            out <= 4'b0000;
        end else begin
            if (ld == 1'b1) begin
                out <= in;
            end else if (ld == 1'b0) begin
                out <= out;
            end
        end
    end
endmodule

module ff_1bit(in, out, clk, rst, pr);
    input in;
    input clk, rst, pr;
    output out;

    reg out;
    always @(posedge clk or negedge rst) begin
        if (rst == 1'b0) begin
            out <= 0;
        end else begin
            if (pr == 1'b0) begin
                out <= 1;
            end else begin
                out <= in;
            end
        end
    end
endmodule

module pc(in, out, ld, clk, rst, co);
    input [3:0] in;
    input ld, clk, rst;
    output [3:0] out;
    output co;

    reg [3:0] out;
    reg co;
    always @(posedge clk or negedge rst) begin
        if (rst == 1'b0) begin
            out <= 4'b0000;
            co <= 1'b0;
        end else if (out == 4'b1111) begin
            co <= 1'b1;
        end else begin
            if (ld == 1'b1) begin
                out <= in;
            end else if (ld == 1'b0) begin
                out <= out + 4'b0001;
            end
        end
    end
endmodule

module ALU_adder_4bit(in_a, in_b, out, ci, co);
    input [3:0] in_a, in_b;
    input ci;
    output [3:0] out;
    output co;

    reg [3:0] out;
    reg co;
    always @(in_a or in_b or ci) begin
        {co, out} <= in_a + in_b + ci;
    end
endmodule

module decoder(op, c_n, s, ld_n);
    input [3:0] op;
    input c_n;
    output [1:0] s;
    output [3:0] ld_n;

    reg [1:0] s;
    reg [3:0] ld_n;
    always @(op or c_n) begin
        s [1] <= op[1];
        s [0] <= op[0] | op[3];
        ld_n[0] <= op[2] | op[3];
        ld_n[1] <= op[3] | ~(op[2] & 1 & 1);
        ld_n[2] <= ~(1 & ~(op[2] & 1 & 1) & op[3]);
        ld_n[3] <= ~(op[2] & op[3] & (op[0] | c_n));
    end
endmodule

module selector(in_a, in_b, in_c, in_d, s, out);
    input [3:0] in_a, in_b, in_c, in_d;
    input [1:0] s;
    output [3:0] out;

    reg [3:0] out;
    always @(in_a or in_b or in_c or in_d or s) begin
        case (s)
            2'b00: out <= in_a;
            2'b01: out <= in_b;
            2'b10: out <= in_c;
            2'b11: out <= in_d;
            default: out <= 4'bxxxx;
        endcase
    end
endmodule


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Design Name: Top
// Module Name: Top
// Project Name: TT05 Modulacion PS-PWM para 3L-FCC
//////////////////////////////////////////////////////////////////////////////////

module tt_um_PS_PWM(
    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
);

    assign uio_oe = 8'b00000000; //todos input
    assign uio_out = 8'b00000000;

/**************** Conexiones ****************/

    wire RST;
    assign RST= ~rst_n;

    wire [5:0] d1;
    wire [5:0] d2;
    assign d1 = ui_in[5:0];
    assign d2 = {ui_in[7:6], uio_in[3:0]};

    wire CLK_SR;
    wire Data_SR;
    assign CLK_SR = uio_in[4];
    assign Data_SR = uio_in[5];

    wire CLK_INT;
    wire CLK_EXT;
    assign CLK_INT= clk;
    assign CLK_EXT= uio_in[6];

    wire PMOS1;      // Senal de control del transistor PMOS 1
    wire NMOS2;      // Senal de control del transistor NMOS 2
    wire PMOS2;     // Senal de control del transistor PMOS 2
    wire NMOS1;      // Senal de control del transistor NMOS 1
    wire CLK_OUTPUT;

/**************** ETAPA DE SHIFT REGISTER ****************/
wire CLK_SELECTOR;
wire [4:0] dt;
wire [1:0] SELECTOR_SIGNAL_GENERATOR_1; // escoge el desfase que tendra la triangular 1
wire [1:0] SELECTOR_SIGNAL_GENERATOR_2; // escoge el desfase que tendra la triangular 2
wire ENABLE_OUTPUT;

Shift_Register Shift_Register_Inst(
    CLK_SR,
    RST,
    Data_SR,
    {ENABLE_OUTPUT, CLK_SELECTOR, SELECTOR_SIGNAL_GENERATOR_2[1],SELECTOR_SIGNAL_GENERATOR_2[0],SELECTOR_SIGNAL_GENERATOR_1[1],SELECTOR_SIGNAL_GENERATOR_1[0], dt[4], dt[3], dt[2], dt[1], dt[0]}
);

// El orden para meter los datos es (de primero a ultimo):
// dt[0]
// dt[1]
// dt[2]
// dt[3]
// dt[4]
// SELECTOR_SIGNAL_GENERATOR_1[0]
// SELECTOR_SIGNAL_GENERATOR_1[1]
// SELECTOR_SIGNAL_GENERATOR_2[0]
// SELECTOR_SIGNAL_GENERATOR_2[1]
// CLK_SELECTOR
// ENABLE_OUTPUT

/**************** ETAPA DE MUX CLK ****************/

wire clk_in;

// Si CLK_SELECTOR es 0 -> se usa clk del PLL, si es 1 -> se usa el clk del pin externo
assign clk_in = CLK_SELECTOR ? CLK_EXT : CLK_INT;
assign CLK_OUTPUT= clk;

/**************** ETAPA DE TRIANGULARES ****************/

// Fases de la triangular 1
wire [5:0] triangular_1_0;
Signal_Generator_0phase Signal_Generator_1_0phase_inst(
    clk_in,
    RST,
    triangular_1_0
);

wire [5:0] triangular_1_90;
Signal_Generator_90phase Signal_Generator_1_90phase_inst(
    clk_in,
    RST,
    triangular_1_90
);

wire [5:0] triangular_1_180;
Signal_Generator_180phase Signal_Generator_1_180phase_inst(
    clk_in,
    RST,
    triangular_1_180
);

wire [5:0] triangular_1_270;
Signal_Generator_270phase Signal_Generator_1_270phase_inst(
    clk_in,
    RST,
    triangular_1_270
);

// Fases de la triangular 2
wire [5:0] triangular_2_0;
Signal_Generator_0phase Signal_Generator_2_0phase_inst(
    clk_in,
    RST,
    triangular_2_0
);

wire [5:0] triangular_2_90;
Signal_Generator_90phase Signal_Generator_2_90phase_inst(
    clk_in,
    RST,
    triangular_2_90
);

wire [5:0] triangular_2_180;
Signal_Generator_180phase Signal_Generator_2_180phase_inst(
    clk_in,
    RST,
    triangular_2_180
);

wire [5:0] triangular_2_270;
Signal_Generator_270phase Signal_Generator_2_270phase_inst(
    clk_in,
    RST,
    triangular_2_270
);

/**************** ETAPA DE MUX Triangulares ****************/

// Triangular 1
reg [5:0] triangular_1;
always @(*) begin

    // SELECTOR_SIGNAL_GENERATOR_2 selecciona la fase de la triangular_1
    case(SELECTOR_SIGNAL_GENERATOR_1)
           2'd0  : triangular_1 = triangular_1_0;
           2'd1  : triangular_1 = triangular_1_90;
           2'd2  : triangular_1 = triangular_1_180;
           2'd3  : triangular_1 = triangular_1_270;
           default : triangular_1 = 0;
       endcase

end

// Triangular 2
reg [5:0] triangular_2;
always @(*) begin

    // SELECTOR_SIGNAL_GENERATOR_2 selecciona la fase de la triangular_2
    case(SELECTOR_SIGNAL_GENERATOR_2)
           2'd0  : triangular_2 = triangular_2_0;
           2'd1  : triangular_2 = triangular_2_90;
           2'd2  : triangular_2 = triangular_2_180;
           2'd3  : triangular_2 = triangular_2_270;
           default : triangular_2 = 0;
       endcase

end

/**************** ETAPA DE COMPARACION ****************/

wire Output_Comparison_1;
Comparator Comparator_Inst_1(
    d1,
    triangular_1,
    Output_Comparison_1
);

wire Output_Comparison_2;
Comparator Comparator_Inst_2(
    d2,
    triangular_2,
    Output_Comparison_2
);

/**************** ETAPA DE DEAD-TIME GENERATOR ****************/

wire pmos1_int;
Dead_Time_Generator Dead_Time_Generator_inst_1(
    clk_in,
    dt,
    Output_Comparison_1,
    pmos1_int
);

wire Not_Output_Comparison_1;
wire nmos2_int;
assign Not_Output_Comparison_1 = ~Output_Comparison_1;
Dead_Time_Generator Dead_Time_Generator_inst_2(
    clk_in,
    dt,
    Not_Output_Comparison_1,
    nmos2_int
);

wire pmos2_int;
Dead_Time_Generator Dead_Time_Generator_inst_3(
    clk_in,
    dt,
    Output_Comparison_2,
    pmos2_int
);

wire Not_Output_Comparison_2;
wire nmos1_int;
assign Not_Output_Comparison_2 = ~Output_Comparison_2;
Dead_Time_Generator Dead_Time_Generator_inst_4(
    clk_in,
    dt,
    Not_Output_Comparison_2,
    nmos1_int
);

/**************** ETAPA DE MUX OUTPUT INTERNA ****************/

wire PMOS1_prev, NMOS2_prev, PMOS2_prev, NMOS1_prev;

// Si OUTPUT_SELECTOR_INTERNAL es 0 -> salidas vienen del modulador , si es 1 -> vienen offchip
assign PMOS1_prev = ~pmos1_int; // TIENE EL NEGADOR POR EL PMOS
assign NMOS2_prev = nmos2_int;
assign PMOS2_prev = ~pmos2_int; // TIENE EL NEGADOR POR EL PMOS
assign NMOS1_prev = nmos1_int;

/**************** ETAPA ENABLE OUTPUTs ****************/

// Si ENABLE_OUTPUT es 0 -> los 4 transistores cmos estan en corte, si es 1 -> las salidas de los 4 cmos viene dada segun su logica previa,
assign PMOS1 = ENABLE_OUTPUT ? PMOS1_prev : 1;
assign NMOS2 = ENABLE_OUTPUT ? NMOS2_prev : 0;
assign PMOS2 = ENABLE_OUTPUT ? PMOS2_prev : 1;
assign NMOS1 = ENABLE_OUTPUT ? NMOS1_prev : 0;

    assign uo_out[0]= PMOS1;
    assign uo_out[1]= NMOS2;
    assign uo_out[2]= PMOS2;
    assign uo_out[3]= NMOS1;
    assign uo_out[4]= clk_in;
    assign uo_out[7:5]= 3'b000;

endmodule

module Comparator(in1, in2, comparison);
  parameter WIDTH_IN1 = 6;
  parameter WIDTH_IN2 = 6;

  input [WIDTH_IN1-1:0] in1;
  input [WIDTH_IN2-1:0] in2;
  output comparison;
  reg comparison;

  always @ (in1 or in2)
  begin
      comparison = (in1 >= in2);
  end
endmodule

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: POWERLAB, DEPARTAMENTO DE ELECTRONICA, UTFSM
// Engineer: GONZALO CARRASCO REYES
//
// Create Date:    12:24:55 10/09/2007
// Design Name:
// Module Name:    Dead_Time_Geneartr
// Project Name:
// Target Devices:
// Tool versions:
// Description:	El modulo recibe una senal digital de 1 bit, y entrega la misma
//						senal de entrada con un retardo en el canto de subida. Este
//						retardo llamado tiempo muerto, es configurable con una palabra
//						de 10 bits, que permite fijar tiempos en pasos de un periodo del
//						reloj del reloj de 150MHz, que tambien debe recibir.
//
// Dependencies:	Depende de una senal de reloj, la configuracion del tiempo
//						muerto y de la senal de entrada.
//							clk	- Entrada de 150MHz
//							dt		- Tiempo muerto
//							gi		- Senal de entrada
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module Dead_Time_Generator(clk, dt, gi, go);

	(* clock_signal = "yes" *)
	input               	clk;        //Reloj principal de 150MHz
	input   	[4:0]   	dt;         //Configuracion de tiempos muertos
	input               	gi;         //Senal a retardar (tiempo muerto)
	output reg          	go;         //Senal retardada (tiempo muerto)
	//----------------------------------------------------------------------------
	//Variables internas
	wire             		dt_end;    	//Senal de fin del tiempo muerto
	reg     [4:0]       	count_dt;   //Contador para el tiempo  muerto
	///////////////////////////////////////////////////////////////////////////////

	//Comparacion asincronica de el contador de tiempo muerto
	assign dt_end = ( count_dt >= dt ) ? 1 : 0;

	//Conteo para retardar el canto de subida, tiempo muerto
	always @(posedge clk) begin
	  if (gi == 0)
			count_dt = 0;
	  else
			if (!dt_end)
				 count_dt = count_dt + 1;
	end

	//----------------------------------------------------------------------------
	//Generacion de senal de salida
	always @(posedge clk)begin
	  if (gi == 0)
			go = 0;
	  else
			if (dt_end)
				 go = 1;
			else
				 go = 0;
	end
	///////////////////////////////////////////////////////////////////////////////
endmodule

`timescale 1ns / 1ps

module Shift_Register(
  input wire CLK_SR,
  input wire RST,
  input wire data_in,
  output wire [10:0] data_out
);

  reg [10:0] internal_data [0:10];
  reg [3:0] shift_state;
  integer i;

  always @(posedge CLK_SR or posedge RST) begin
    if (RST) begin
      // Reiniciar todas las variables internas a 0 cuando se activa el reset
      for (i = 0; i < 5'd11; i = i + 1) begin
        internal_data[i] <= 0;
      end
      shift_state <= 0;
    end else
        if (shift_state < 5'd11) begin
          // Almacenar el valor de entrada en la variable interna correspondiente
          internal_data[shift_state] <= data_in;
          // Cambiar al siguiente estado del registro
          shift_state <= shift_state + 1;
        end
end
assign data_out[0] = internal_data[0]; // dt[0]
assign data_out[1] = internal_data[1]; // dt[1]
assign data_out[2] = internal_data[2]; // dt[2]
assign data_out[3] = internal_data[3]; // dt[3]
assign data_out[4] = internal_data[4]; // dt[4]
assign data_out[5] = internal_data[5]; // SELECTOR_SIGNAL_GENERATOR_1[0]
assign data_out[6] = internal_data[6]; // SELECTOR_SIGNAL_GENERATOR_1[1]
assign data_out[7] = internal_data[7]; // SELECTOR_SIGNAL_GENERATOR_2[0]
assign data_out[8] = internal_data[8]; // SELECTOR_SIGNAL_GENERATOR_2[1]
assign data_out[9] = internal_data[9]; // OUTPUT_SELECTOR_EXTERNAL[0]
assign data_out[10] = internal_data[10]; // OUTPUT_SELECTOR_EXTERNAL[1]

endmodule

`timescale 1ns / 1ps

module Signal_Generator_0phase #(parameter WIDTH_TRIANG = 6)(
    input clk,        // Reloj de entrada
    input rst,        // Senal de reinicio
    output reg [WIDTH_TRIANG-1:0] count // Salida triangular de 12 bits
);

//reg [WIDTH_TRIANG-1:0] count; // contador de 12 bits
reg direction;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        count <= 0;
        direction <= 1;
    end
    else begin
        if (direction) begin
            if (count == 6'b111111) begin // 64 en binario
                direction <= 0;
                count <= 6'b111110 ; end
            else
                count <= count + 1;
        end
        else begin
            if (count == 6'b000000) begin// 0 en binario
                direction <= 1;
                count <= 6'b000001; end
            else
                count <= count - 1;
        end
    end
end

//assign triangular_out = count;

endmodule

`timescale 1ns / 1ps

module Signal_Generator_90phase #(parameter WIDTH_TRIANG = 6)(
    input clk,        // Reloj de entrada
    input rst,        // Senal de reinicio
    output reg [WIDTH_TRIANG-1:0] count // Salida triangular de 12 bits
);

//reg [WIDTH_TRIANG-1:0] count; // contador de 12 bits
reg direction;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        count <= 6'b00011111;
        direction <= 1;
    end
    else begin
        if (direction) begin
            if (count == 6'b111111) begin // 4095 en binario
                direction <= 0;
                count <= 6'b111110 ; end
            else
                count <= count + 1;
        end
        else begin
            if (count == 6'b000000) begin// 0 en binario
                direction <= 1;
                count <= 6'b000001; end
            else
                count <= count - 1;
        end
    end
end

//assign triangular_out = count;

endmodule

`timescale 1ns / 1ps

module Signal_Generator_180phase #(parameter WIDTH_TRIANG = 6)(
    input clk,        // Reloj de entrada
    input rst,        // Senal de reinicio
    output reg [WIDTH_TRIANG-1:0] count // Salida triangular de 12 bits
);

//reg [WIDTH_TRIANG-1:0] count; // contador de 12 bits
reg direction;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        count <= 6'b111111;
        direction <= 1;
    end
    else begin
        if (direction) begin
            if (count == 6'b111111) begin // 4095 en binario
                direction <= 0;
                count <= 6'b111110 ; end
            else
                count <= count + 1;
        end
        else begin
            if (count == 6'b000000) begin// 0 en binario
                direction <= 1;
                count <= 6'b000001; end
            else
                count <= count - 1;
        end
    end
end

//assign triangular_out = count;

endmodule

`timescale 1ns / 1ps

module Signal_Generator_270phase #(parameter WIDTH_TRIANG = 6)(
    input clk,        // Reloj de entrada
    input rst,        // Senal de reinicio
    output reg [WIDTH_TRIANG-1:0] count // Salida triangular de 12 bits
);

//reg [WIDTH_TRIANG-1:0] count; // contador de 12 bits
reg direction;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        count <= 6'b00100000;
        direction <= 0;
    end
    else begin
        if (direction) begin
            if (count == 6'b111111) begin // 4095 en binario
                direction <= 0;
                count <= 6'b111110 ; end
            else
                count <= count + 1;
        end
        else begin
            if (count == 6'b000000) begin// 0 en binario
                direction <= 1;
                count <= 6'b000001; end
            else
                count <= count - 1;
        end
    end
end

//assign triangular_out = count;

endmodule


/*
 * Copyright (c) 2024 Your Name
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

module tt_um_revenantx86_tinytpu
(
    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
);
  //
  wire rst;
  wire data_in_x;
  wire data_in_y;
  wire load_en;
  wire init;
  wire data_out_z;
  wire tx_ready;
  //
  assign rst = ~rst_n;
  //
  /*
    Assign IO
  */
  // Input Assign
  assign data_in_x = ui_in[0];
  assign data_in_y = ui_in[1];
  assign load_en   = ui_in[2];
  assign init      = ui_in[3];
  // Output Assign
  assign uo_out[0] = data_out_z;
  assign uo_out[1] = tx_ready;
  assign uo_out[2] = 0;
  assign uo_out[3] = 0;
  assign uo_out[4] = 0;
  assign uo_out[5] = 0;
  assign uo_out[6] = 0;
  assign uo_out[7] = 0;
  //
  assign uio_oe = 8'b00;
  assign uio_out = 8'b0;
  //
  tinytpu_top #(.D_W(8), .N(2), .WORD(2))
      tinytpu_top_inst (
                          .clk(clk),
                          .rst(rst),
                          .data_in_x(data_in_x),
                          .data_in_y(data_in_y),
                          .load_en(load_en),
                          .init(init),
                          .data_out_z(data_out_z),
                          .tx_ready(tx_ready)
                       );

endmodule

module dff_mem #
(
    parameter D_W = 8,
    parameter WORD = 8
)
(
    input wire clk,
    input wire rst,
    // Read Logic
    input wire rd_en,
    input wire [$clog2(WORD)-1:0] addr,
    // Write logic
    input wire wr_en,
    // IO
    input wire [D_W-1:0] data_in,
    output reg [D_W-1:0] data_out
);
//
// Memory Array
reg [D_W-1:0] mem [WORD-1:0];
//
// Write Logic
always @(posedge clk) begin
    if(wr_en) begin
        mem[addr] <= data_in;
    end
end

// Read Logic
always @(*) begin
    if(rd_en) begin
        data_out <= mem[addr];  // Update output only if rd_en is high
    end
    else if(!rd_en) begin
        data_out <= 0;  // Update output only if rd_en is high
    end
    else if(rst) begin
        data_out <= 0;  // Optionally reset the output to 0 on reset
    end
end

endmodule

module input_control #
(
    parameter D_W = 8,
    parameter N = 2,
    parameter WORD = 8
)
(
    input wire clk,
    input wire rst,
    input wire data_in_x,
    input wire data_in_y,
    input wire load_en,
    input wire init,
    output reg [(N*D_W)-1:0] out_x_flat,
    output reg [(N*D_W)-1:0] out_y_flat,
    output reg out_init
);
//
/*
    State Machine
*/
//enum {IDLE,LOAD,TRANSFER} STATE;
localparam IDLE     = 2'b00;
localparam LOAD     = 2'b01;
localparam TRANSFER = 2'b10;
reg [1:0] STATE;
//
wire [D_W-1:0] out_x [N-1:0];
wire [D_W-1:0] out_y [N-1:0];
/*
    Generate Memory Block
*/
reg  [$clog2(WORD)-1:0] addr_x_ram     [N-1:0];
reg                     rd_en_x_ram    [N-1:0];
reg                     wr_en_x_ram    [N-1:0];
reg  [D_W-1:0]          data_in_x_reg;
//
reg  [$clog2(WORD)-1:0] addr_y_ram     [N-1:0];
reg                     rd_en_y_ram    [N-1:0];
reg                     wr_en_y_ram    [N-1:0];
reg  [D_W-1:0]          data_in_y_reg;
//
genvar n;
generate
    for(n=0;n<N;n=n+1) begin
        //Initalize memory X & Y
        dff_mem #( .D_W(D_W), .WORD(WORD))
                mem_x (
                        .clk(clk),
                        .rst(rst),
                        .addr(addr_x_ram[n]),
                        .rd_en(rd_en_x_ram[n]),
                        .wr_en(wr_en_x_ram[n]),
                        .data_in(data_in_x_reg),
                        .data_out(out_x[n])
                    );
        dff_mem #( .D_W(D_W), .WORD(WORD))
                mem_y (
                        .clk(clk),
                        .rst(rst),
                        .addr(addr_y_ram[n]),
                        .rd_en(rd_en_y_ram[n]),
                        .wr_en(wr_en_y_ram[n]),
                        .data_in(data_in_y_reg),
                        .data_out(out_y[n])
                    );
    end
endgenerate

always @(posedge clk) begin
    if(rst) begin
        STATE <= IDLE;
        out_init <= 0;
    end
    else begin
        case(STATE)
        IDLE: begin
            out_init <= 0;
            if(load_en)
                STATE <= LOAD;
            else if(init)begin
                out_init <= 1;
                STATE <= TRANSFER;
            end
        end
        LOAD: begin
            if(!load_en)
                STATE <= IDLE;
        end
        TRANSFER: begin
            out_init <= 0;
            if(rd_en_x_ram[N-1]) begin
                STATE <= IDLE;
            end
        end
        endcase
    end
end

reg [$clog2(D_W)-1:0]       bit_counter;
reg [$clog2(N)-1:0]         addr_counter;
reg [$clog2(N)-1:0]         ram_counter;

integer i;
always @(posedge clk) begin
    if(rst) begin
        //
        bit_counter   <= 0;
        addr_counter  <= 0;
        ram_counter   <= 0;
        data_in_x_reg <= 0;
        data_in_y_reg <= 0;
        //
        wr_en_x_ram[0] <= 0;
        rd_en_x_ram[0] <= 0;
        wr_en_y_ram[0] <= 0;
        rd_en_y_ram[0] <= 0;
        for(i=0;i<N;i=i+1) begin
            // Reset X
            addr_x_ram[i]  <= 0;
            // Reset Y
            addr_y_ram[i]  <= 0;
        end
        //
    end
    else begin
        case(STATE)
            IDLE : begin
                addr_counter <= 0;
                // X Row
                wr_en_x_ram[N-1] <= 0;
                // Y Column
                wr_en_y_ram[N-1] <= 0;
            end
            LOAD: begin
                //
                bit_counter <= bit_counter + 1;
                // Load data for X row
                wr_en_x_ram[ram_counter] <= 0;
                data_in_x_reg <= {data_in_x,data_in_x_reg[D_W-1:1]};
                //
                // Load data for Y Column
                wr_en_y_ram[ram_counter] <= 0;
                data_in_y_reg <= {data_in_y,data_in_y_reg[D_W-1:1]};
                //
                if(bit_counter == D_W-1) begin
                    // X Row
                    wr_en_x_ram[ram_counter] <= 1;
                    // Y Column
                    wr_en_y_ram[ram_counter] <= 1;
                end
                //
                if(wr_en_x_ram[ram_counter] == 1) begin
                    // X Row
                    addr_x_ram[ram_counter]  <= addr_x_ram[ram_counter] + 1;
                    // X Row
                    addr_y_ram[ram_counter]  <= addr_y_ram[ram_counter] + 1;
                end
                if(addr_x_ram[ram_counter] == N-1 && wr_en_x_ram[ram_counter]) begin
                    // X Row & Y Row common ram word block counter
                    ram_counter <= ram_counter + 1;
                end
            end
            TRANSFER : begin
                // X & Y Common
                wr_en_x_ram[ram_counter] <= 0;
                //
                if(rd_en_x_ram[N-1]) begin
                    rd_en_x_ram[0] <= 0;
                    rd_en_y_ram[0] <= 0;
                end
                else begin // Initalize transfer
                    rd_en_x_ram[0] <= 1;
                    rd_en_y_ram[0] <= 1;
                end
                addr_counter <= addr_counter + 1;
                // Assign updated addres X & Y
                addr_x_ram[0] <= addr_counter;
                addr_y_ram[0] <= addr_counter;
            end
        endcase
    end
end

// Assign reading
genvar x;
for (x=1;x<N;x=x+1)
begin
    always@(posedge clk)
    begin
        if (rst) begin
            addr_x_ram[x]  <= 0;
            rd_en_x_ram[x] <= 0;
            // Assign for Y column
            addr_y_ram[x] <= 0;
            rd_en_y_ram[x] <= 0;
        end
        else if(STATE == TRANSFER || STATE == IDLE) begin
            // Assign for X row
            addr_x_ram[x]  <= addr_x_ram[x-1];
            rd_en_x_ram[x] <= rd_en_x_ram[x-1];
            // Assign for Y column
            addr_y_ram[x] <= addr_y_ram[x-1];
            rd_en_y_ram[x] <= rd_en_y_ram[x-1];
        end
        //else if (STATE == IDLE) begin
        //    rd_en_x_ram[N-1] <= 0;
        //    rd_en_y_ram[N-1] <= 0;
        //end
    end
end

// Flatten output signal
integer r;
// Packing the 2D array 'z' into the flat wire 'data_core_z_flat'
always @(*) begin
    for (r = 0; r < N; r = r + 1) begin
        // Compute the starting index of each signal in the flat wire
        out_x_flat[(r+1)*D_W-1 -: D_W] = out_x[r];
        out_y_flat[(r+1)*D_W-1 -: D_W] = out_y[r];
    end
end
endmodule

module mac #
(
    parameter D_W = 16
)
(
    input  wire               clk,         // Clock
    input  wire               rst,         // reset
    input  wire               init,        // Initalize first op
    input  wire [D_W-1:0]     in_x,        // Input from X axis
    input  wire [D_W-1:0]     in_y,        // Input from Y axis
    output reg  [D_W-1:0]     out_x,       // x axis input passthrough
    output reg  [D_W-1:0]     out_y,       // y axis input passthrough
    output reg  [(2*D_W-1):0] out_z,       // Output for the sum
    output reg                out_init     // Init passthrough
);
//
always @(posedge clk) begin
    //
    out_x <= in_x;
    out_y <= in_y;
    out_init <= init;
    //
    if(rst) begin                       // Reset
        out_z <= 0;
        out_y <= 0;
        out_x <= 0;
        out_init <= 0;
    end
    //
    else if(init)
        out_z <= in_x * in_y;           // Multiply & Override
    //
    else
        out_z <= out_z + (in_x* in_y);  // Multiply & Accumulate
    //
end
//
endmodule

module output_control #
(
    parameter D_W = 8,
    parameter N = 2
)
(
    // Global Signal
    input  wire                   clk,
    input  wire                   rst,
    // Systolic Output
    input  wire [(N*N*2*D_W)-1:0] core_out_z,
    input  wire                   init,
    // IO Output
    output reg                    data_out_z,
    output reg                    tx_ready
);
/*
    STATE Machine
*/
//
localparam IDLE = 1'b0;
localparam TX   = 1'b1;
reg        STATE;
//enum {IDLE,TX} STATE;
//
// Counters
reg [$clog2(2*D_W)-1:0] bit_counter;
reg [$clog2(N)-1:0]     column_counter;
reg [$clog2(N)-1:0]     row_counter;
//
wire [2*D_W-1:0]        data_tx_reg;
//
//
assign data_tx_reg = core_out_z[(row_counter*N + column_counter)*2*D_W +: 2*D_W];
//
reg init_delay [N-1:0];
//
always @(posedge clk) begin
    if(rst) begin
        STATE <= IDLE;
    end
    else begin
        init_delay[0] <= init;
        case(STATE)
            IDLE: begin
                if(init_delay[N-1])
                    STATE <= TX;
            end
            TX: begin
                if(row_counter == N-1 && column_counter == N-1 && bit_counter == 2*D_W-1)
                    STATE <= IDLE;
            end
        endcase
    end
end

integer x,r,c;
always @(posedge clk) begin
    //
    if(rst) begin
        //
        bit_counter <= 0;
        column_counter <= 0;
        row_counter <= 0;
        tx_ready <= 0;
        //
    end
    //
    else begin
        case(STATE)
            IDLE: begin
                //
                bit_counter     <= 0;
                column_counter  <= 0;
                row_counter     <= 0;
                tx_ready        <= 0;
                data_out_z      <= 0;
                //
            end
            TX: begin
                //
                tx_ready    <= 1;
                bit_counter <= bit_counter + 1;
                data_out_z  <=  data_tx_reg[bit_counter];
                //
                if(bit_counter == (2*D_W-1)) begin
                    column_counter <= column_counter + 1;
                    bit_counter    <= 0;
                end
                if(column_counter == N-1 && bit_counter == (2*D_W-1)) begin
                    column_counter <= 0;
                    row_counter   <= row_counter + 1;
                end
                //
            end
        endcase
    end
end

// Assign delay counter
genvar i;
for (i=1;i<N;i=i+1) begin
    always @(posedge clk) begin
        init_delay[i] <= init_delay[i-1];
    end
end

endmodule

module systolic #
(
    parameter D_W = 8,
    parameter N = 2
)
(
    input   wire             clk,                   // Clock Input
    input   wire             rst,                   // Reset cores & Regs
    input   wire             init,                  // Init process
    //input   wire [D_W-1:0]   x        [N-1:0],       // Row input
    //input   wire [D_W-1:0]   y        [N-1:0],       // Column input
    //output  reg  [2*D_W-1:0] z        [N-1:0][N-1:0] // MAC unit indv outputs
    input wire [(N*D_W)-1:0] x_flat,  // Flat wire to hold the packed array
    input wire [(N*D_W)-1:0] y_flat,  // Flat wire to hold the packed array
    output reg [(N*N*2*D_W)-1:0] z_flat // Flat wire to hold the packed array
);
//
// Initlize wires
//
wire [D_W-1:0]   in_x     [N-1:0][N-1:0];
wire [D_W-1:0]   in_y     [N-1:0][N-1:0];
wire [D_W-1:0]   out_x    [N-1:0][N-1:0];
wire [D_W-1:0]   out_y    [N-1:0][N-1:0];
wire [2*D_W-1:0] out_z    [N-1:0][N-1:0];
wire             init_in  [N-1:0][N-1:0];
wire             init_out [N-1:0][N-1:0];
// wire assign to output
//assign z = out_z;
//
// flatten-unflatten
reg  [D_W-1:0]   x       [N-1:0];        // Row input
reg  [D_W-1:0]   y       [N-1:0];        // Column input
reg  [2*D_W-1:0] z       [N-1:0][N-1:0]; // MAC unit indv outputs
//

integer r, c;
// Assuming data_core_z_flat is already declared
always @(*) begin
    for (r = 0; r < N; r = r + 1) begin
        x[r] = x_flat[(r+1)*D_W-1 -: D_W];
        y[r] = y_flat[(r+1)*D_W-1 -: D_W];
        for (c = 0; c < N; c = c + 1) begin
            // Compute the starting index of each signal in the flat wire
            z_flat[((r * N + c) * (2 * D_W)) +: (2 * D_W)] = out_z[r][c];
        end
    end
end
//
//initalize counter
//
// Initalize MAC module COL >> ROW
assign init_in[0][0] = init;
//
// Loop Generate MAC matrix
genvar i,j,k;
generate
    //
    for(i=0; i<N; i=i+1) begin
        //
        // Assign initialization wire to first row
        if(i==0) begin
            for(k=0; k<N-1;k++) begin
                assign init_in[0][k+1] = init_out[0][k];
            end
        end
        //
        for(j=0; j<N; j = j + 1) begin
            //
            // Assign wires to mac modules
            for(k=0;k<N-1;k=k+1) begin
                assign in_x[i][k+1] = out_x[i][k];
                assign in_y[k+1][j] = out_y[k][j];
            end
            //
            // Asssign initalization signal to columns
            if(i==0) begin
                for (k=0;k<N-1;k++) begin
                    assign init_in[k+1][j]=init_out[k][j];
                end
            end
            //
            // Input Columns & Row Assignment
            if(j==0) assign in_x[i][0] = x[i];
            if(i==0) assign in_y[0][j] = y[j];
            //
            mac #(.D_W(D_W)) mac_inst
                (
                    .clk      (clk),
                    .rst      (rst),
                    .init     (init_in[i][j]),
                    .out_init (init_out[i][j]),
                    .in_x     (in_x[i][j]),
                    .in_y     (in_y[i][j]),
                    .out_x    (out_x[i][j]),
                    .out_y    (out_y[i][j]),
                    .out_z    (out_z[i][j])
                );
            //
        end
    end
    //
endgenerate
//
endmodule

module tinytpu_top #
(
    parameter D_W = 8,
    parameter N = 2,
    parameter WORD = 2
)
(
    input  wire clk,
    input  wire rst,
    input  wire data_in_x,
    input  wire data_in_y,
    input  wire load_en,
    input  wire init,
    output wire data_out_z,
    output reg  tx_ready
);
//
// REG & WIRES
wire [(N*D_W)-1:0]     out_x_flat;
wire [(N*D_W)-1:0]     out_y_flat;
wire [(N*N*2*D_W)-1:0] out_z_flat;
wire out_init;
reg tx_ready_reg;

always @(posedge clk) begin
    tx_ready <= tx_ready_reg;
end
//wire  [D_W-1:0]   data_core_x  [N-1:0];
//wire  [D_W-1:0]   data_core_y  [N-1:0];
//wire  [2*D_W-1:0] data_core_z   [N-1:0][N-1:0];
//
/*
    Input Control Module
*/
input_control #(.D_W(D_W), .N(N), .WORD(WORD))
        input_control_inst (
                        .clk(clk),
                        .rst(rst),
                        .data_in_x(data_in_x),
                        .data_in_y(data_in_y),
                        .load_en(load_en),
                        .init(init),
                        .out_x_flat(out_x_flat),
                        .out_y_flat(out_y_flat),
                        .out_init(out_init)
                     );
/*
    Output Control Module
*/
output_control #(.D_W(D_W), .N(N))
        output_control_inst (
                                .clk(clk),
                                .rst(rst),
                                .core_out_z(out_z_flat),
                                .init(init),
                                .data_out_z(data_out_z),
                                .tx_ready(tx_ready_reg)
                            );
/*
    Systolic Core
*/
systolic #(.D_W(D_W), .N(N))
        systolic_inst (
                        .clk(clk),
                        .rst(rst),
                        .init(out_init),
                        .x_flat(out_x_flat),
                        .y_flat(out_y_flat),
                        .z_flat(out_z_flat)
                      );
//
//
//
endmodule


/*
 * Copyright (c) 2024 Your Name
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

module tt_um_serdes (
    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
);

  // All output pins must be assigned. If not used, assign to 0.

    wire ser_in = uio_in[0];
    wire ser_out = uio_out[0];
    wire data_en = uio_in[2];
    wire par_en = uio_in[3];
    wire ser_en = uio_in[4];

    serdes_top serdes_inst (
        .clk(clk),
        .rst_n(rst_n),
        .data_en(data_en),
        .ser_in(ser_in),
        .par_en(par_en),
        .ser_en(ser_en),
        .ser_out(ser_out),
        .data_out(uo_out),
        .data_8b_in(ui_in)
    );
      assign uio_oe = 8'b0;
    assign uio_out[7:1] = 7'b0;
endmodule

module serdes_top(
    input           clk,
    input           rst_n,
    input           data_en,
    input           ser_in,
    input           par_en,
    input           ser_en,
    output wire      ser_out,
    output wire [7:0]    data_out,
    input [7:0]     data_8b_in
);

    // Serializer module
    wire [7:0]       data_8b_latched;
    wire [9:0]       data_10b_encoded;
    wire [9:0]       data_10b_latched;

    latch_8bit latch_8b_inst (
        .clk(clk),
        .rst_n(rst_n),
        .data_8b_in(data_8b_in),
        .data_en(data_en),
        .data_8b_out(data_8b_latched)
    );

    encoder_8b_10b encoder_inst (
        .clk(clk),
        .rst_n(rst_n),
        .data_8b_in(data_8b_latched),
        .ser_en(ser_en),
        .data_10b_out(data_10b_encoded)
    );

    latch_10bit latch_10b_inst (
        .clk(clk),
        .rst_n(rst_n),
        .data_10b_in(data_10b_encoded),
        .data_en(data_en),
        .data_10b_out(data_10b_latched)
    );

    piso_10bit piso_inst (
        .clk(clk),
        .rst_n(rst_n),
        .par_in(data_10b_latched),
        .load_en(ser_en),
        .ser_out(ser_out)
    );

    // Deserializer module
    wire [9:0]       data_10b;
    wire[9:0]	    data_10b_latched_d;
    wire [7:0]       data_8b_decoded;

    // Instantiate the SIPO module
    sipo_10bit sipo_inst_d (
        .clk(clk),
        .rst_n(rst_n),
        .ser_in(ser_in),
        .shift_en(par_en),
        .par_out(data_10b)
    );

    // Instantiate the 10-bit latch
    latch_10bit latch_10b_inst_d (
        .clk(clk),
        .rst_n(rst_n),
        .data_10b_in(data_10b),
        .data_en(data_en),
        .data_10b_out(data_10b_latched_d)
    );

    // Instantiate the 10b/8b decoder
    decoder_10b_8b decoder_inst_d (
        .clk(clk),
        .rst_n(rst_n),
        .data_10b_in(data_10b_latched_d),
        .par_en(par_en),
        .data_8b_out(data_8b_decoded)
    );

    // Instantiate the 8-bit latch
    latch_8bit latch_8b_inst_d (
        .clk(clk),
        .rst_n(rst_n),
        .data_8b_in(data_8b_decoded),
        .data_en(data_en),
        .data_8b_out(data_out)
    );
endmodule

module latch_8bit(
    input           clk,
    input           rst_n,
    input [7:0]     data_8b_in,
    input           data_en,
    output reg [7:0]    data_8b_out
);

    always @ ( posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_8b_out <= 8'h00;
        end
        else begin
            if (data_en) begin
                data_8b_out <= data_8b_in;
            end
            else data_8b_out <= data_8b_out;
        end
    end

endmodule

module encoder_8b_10b(
    input           clk,
    input           rst_n,
    input [7:0]     data_8b_in,
    input           ser_en,
    output reg[9:0]    data_10b_out
);

    reg [3:0]       temp_4b;
    reg [5:0]       temp_6b;

    always @ (posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            temp_4b <= 4'b0000;
            temp_6b <= 6'b000000;
        end
        else begin
            if (!ser_en) begin
                case (data_8b_in[7:5])
                3'b000: temp_4b <= 4'b0100;
                3'b001: temp_4b <= 4'b1001;
                3'b010: temp_4b <= 4'b0101;
                3'b011: temp_4b <= 4'b0011;
                3'b100: temp_4b <= 4'b0010;
                3'b101: temp_4b <= 4'b1010;
                3'b110: temp_4b <= 4'b0110;
                3'b111: temp_4b <= 4'b0001;
                default: temp_4b <= 4'b0000;
                endcase
                case (data_8b_in[4:0])
                5'b00000: temp_6b <= 6'b011000;
                5'b00001: temp_6b <= 6'b011101;
                5'b00010: temp_6b <= 6'b010010;
                5'b00011: temp_6b <= 6'b110001;
                5'b00100: temp_6b <= 6'b110101;
                5'b00101: temp_6b <= 6'b101001;
                5'b00110: temp_6b <= 6'b011001;
                5'b00111: temp_6b <= 6'b111000;
                5'b01000: temp_6b <= 6'b111001;
                5'b01001: temp_6b <= 6'b100101;
                5'b01010: temp_6b <= 6'b010101;
                5'b01011: temp_6b <= 6'b110100;
                5'b01100: temp_6b <= 6'b001101;
                5'b01101: temp_6b <= 6'b101100;
                5'b01110: temp_6b <= 6'b011100;
                5'b01111: temp_6b <= 6'b010111;
                5'b10000: temp_6b <= 6'b011011;
                5'b10001: temp_6b <= 6'b100011;
                5'b10010: temp_6b <= 6'b010011;
                5'b10011: temp_6b <= 6'b110010;
                5'b10100: temp_6b <= 6'b001011;
                5'b10101: temp_6b <= 6'b101010;
                5'b10110: temp_6b <= 6'b011010;
                5'b10111: temp_6b <= 6'b111010;
                5'b11000: temp_6b <= 6'b110011;
                5'b11001: temp_6b <= 6'b100110;
                5'b11010: temp_6b <= 6'b010110;
                5'b11011: temp_6b <= 6'b110110;
                5'b11100: temp_6b <= 6'b001110;
                5'b11101: temp_6b <= 6'b101110;
                5'b11110: temp_6b <= 6'b011110;
                5'b11111: temp_6b <= 6'b101011;
                default: temp_6b <= 6'b000000;
                endcase
            end
            else data_10b_out <= {temp_4b, temp_6b};
        end
    end
endmodule

module latch_10bit(
    input           clk,
    input           rst_n,
    input [9:0]     data_10b_in,
    input           data_en,
    output reg [9:0]    data_10b_out
);
    always @ (posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_10b_out <= 10'h00;
        end
        else begin
            if (data_en) begin
                data_10b_out <= data_10b_in;
            end
            else data_10b_out <= data_10b_out;
        end
    end
endmodule

module piso_10bit(
    input           clk,
    input           rst_n,
    input [9:0]     par_in,
    input           load_en,
    output reg      ser_out
);

    reg [9:0]       shift_reg;

    always @(posedge clk) begin
        if (!rst_n) begin
            shift_reg <= 10'b0000000000;
        end
        else begin
            if (load_en) begin
                shift_reg <= par_in;
            end
            else begin
                ser_out <= shift_reg[0];
                shift_reg <= {1'b0, shift_reg[9:1]};
            end
        end
    end

endmodule

module sipo_10bit(
    input           clk,
    input           rst_n,
    input           ser_in,
    input           shift_en,
    output reg [9:0]    par_out
);

    reg [9:0]       shift_reg;

    always @ (posedge clk)begin
        if (!rst_n) begin
            shift_reg <= 10'b0000000000;
        end
        else begin
            if (shift_en) begin
                par_out <= shift_reg;
            end
            else shift_reg <= {shift_reg[8:0], ser_in};
        end
    end

endmodule

module decoder_10b_8b(
    input           clk,
    input           rst_n,
    input [9:0]     data_10b_in,
    input           par_en,
    output reg [7:0]    data_8b_out
);

    reg [2:0]       temp_3b;
    reg [4:0]       temp_5b;

    always @ (posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            temp_3b <= 3'b000;
            temp_5b <= 5'b00000;
        end
        else begin
            if (!par_en) begin
                case (data_10b_in[9:6])
                4'b0100: temp_3b <= 3'b000;
                4'b1001: temp_3b <= 3'b001;
                4'b0101: temp_3b <= 3'b010;
                4'b0011: temp_3b <= 3'b011;
                4'b0010: temp_3b <= 3'b100;
                4'b1010: temp_3b <= 3'b101;
                4'b0110: temp_3b <= 3'b110;
                4'b0001: temp_3b <= 3'b111;
                default: temp_3b <= 3'b000;
                endcase
                case (data_10b_in[5:0])
                6'b011000: temp_5b <= 5'b00000;
                6'b011101: temp_5b <= 5'b00001;
                6'b010010: temp_5b <= 5'b00010;
                6'b110001: temp_5b <= 5'b00011;
                6'b110101: temp_5b <= 5'b00100;
                6'b101001: temp_5b <= 5'b00101;
                6'b011001: temp_5b <= 5'b00110;
                6'b111000: temp_5b <= 5'b00111;
                6'b111001: temp_5b <= 5'b01000;
                6'b100101: temp_5b <= 5'b01001;
                6'b010101: temp_5b <= 5'b01010;
                6'b110100: temp_5b <= 5'b01011;
                6'b001101: temp_5b <= 5'b01100;
                6'b101100: temp_5b <= 5'b01101;
                6'b011100: temp_5b <= 5'b01110;
                6'b010111: temp_5b <= 5'b01111;
                6'b011011: temp_5b <= 5'b10000;
                6'b100011: temp_5b <= 5'b10001;
                6'b010011: temp_5b <= 5'b10010;
                6'b110010: temp_5b <= 5'b10011;
                6'b001011: temp_5b <= 5'b10100;
                6'b101010: temp_5b <= 5'b10101;
                6'b011010: temp_5b <= 5'b10110;
                6'b111010: temp_5b <= 5'b10111;
                6'b110011: temp_5b <= 5'b11000;
                6'b100110: temp_5b <= 5'b11001;
                6'b010110: temp_5b <= 5'b11010;
                6'b110110: temp_5b <= 5'b11011;
                6'b001110: temp_5b <= 5'b11100;
                6'b101110: temp_5b <= 5'b11101;
                6'b011110: temp_5b <= 5'b11110;
                6'b101011: temp_5b <= 5'b11111;
                default: temp_5b <= 5'b00000;
                endcase
            end
            else data_8b_out <= {temp_3b,temp_5b};
        end
    end
endmodule