// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

// Authors:
// - Thomas Benz <tbenz@iis.ee.ethz.ch>

`include "idma/typedef.svh"

/// The driver interface for the iDMA backend
interface IDMA_DV #(
    parameter int unsigned DataWidth = 0,
    parameter int unsigned AddrWidth = 0,
    parameter int unsigned UserWidth = 0,
    parameter int unsigned AxiIdWidth = 0,
    parameter int unsigned TFLenWidth = 0
) (
    input logic clk_i
);

    // derived parameters
    localparam int unsigned StrbWidth   = DataWidth / 8;
    localparam int unsigned OffsetWidth = $clog2(StrbWidth);

    // derived types
    typedef logic [AddrWidth-1:0]   addr_t;
    typedef logic [DataWidth-1:0]   data_t;
    typedef logic [StrbWidth-1:0]   strb_t;
    typedef logic [UserWidth-1:0]   user_t;
    typedef logic [AxiIdWidth-1:0]  id_t;
    typedef logic [OffsetWidth-1:0] offset_t;
    typedef logic [TFLenWidth-1:0]  tf_len_t;

    // iDMA request / response types
    `IDMA_TYPEDEF_FULL_REQ_T(idma_req_t, id_t, addr_t, tf_len_t)
    `IDMA_TYPEDEF_FULL_RSP_T(idma_rsp_t, addr_t)

    // the signals
    idma_req_t              req;
    logic                   req_valid;
    logic                   req_ready;

    idma_rsp_t              rsp;
    logic                   rsp_valid;
    logic                   rsp_ready;

    idma_pkg::idma_eh_req_t eh_req;
    logic                   eh_req_valid;
    logic                   eh_req_ready;

    // used for the driver
    modport Master (
        output req, req_valid,       input  req_ready,
        input  rsp, rsp_valid,       output rsp_ready,
        output eh_req, eh_req_valid, input  eh_req_ready
    );

    // currently not needed
    modport Slave (
        input  req, req_valid,       output req_ready,
        output rsp, rsp_valid,       input  rsp_ready,
        input  eh_req, eh_req_valid, output eh_req_ready
    );

endinterface : IDMA_DV

/// The driver interface for the iDMA with the ND-midend attached
interface IDMA_ND_DV #(

    parameter int unsigned AddrWidth   = 0,
    parameter int unsigned UserWidth   = 0,
    parameter int unsigned AxiIdWidth  = 0,
    parameter int unsigned TFLenWidth  = 0,
    parameter int unsigned NumDim      = 0,
    parameter int unsigned RepWidth    = 0,
    parameter int unsigned StrideWidth = 0
) (
    input logic clk_i
);

    // derived parameters
    localparam int unsigned StrbWidth   = DataWidth / 8;
    localparam int unsigned OffsetWidth = $clog2(StrbWidth);

    // derived types
    typedef logic [AddrWidth-1:0]   addr_t;
    typedef logic [DataWidth-1:0]   data_t;
    typedef logic [StrbWidth-1:0]   strb_t;
    typedef logic [UserWidth-1:0]   user_t;
    typedef logic [AxiIdWidth-1:0]  id_t;
    typedef logic [OffsetWidth-1:0] offset_t;
    typedef logic [TFLenWidth-1:0]  tf_len_t;
    typedef logic [RepWidth-1:0]    reps_t;
    typedef logic [StrideWidth-1:0] strides_t;

    // iDMA request / response types
    `IDMA_TYPEDEF_FULL_REQ_T(idma_req_t, id_t, addr_t, tf_len_t)
    `IDMA_TYPEDEF_FULL_RSP_T(idma_rsp_t, addr_t)

    // iDMA ND request
    `IDMA_TYPEDEF_FULL_ND_REQ_T(idma_nd_req_t, idma_req_t, reps_t, strides_t)

    // the signals
    idma_nd_req_t           req;
    logic                   req_valid;
    logic                   req_ready;

    idma_rsp_t              rsp;
    logic                   rsp_valid;
    logic                   rsp_ready;

    idma_pkg::idma_eh_req_t eh_req;
    logic                   eh_req_valid;
    logic                   eh_req_ready;

    // used for the driver
    modport Master (
        output req, req_valid,       input  req_ready,
        input  rsp, rsp_valid,       output rsp_ready,
        output eh_req, eh_req_valid, input  eh_req_ready
    );

    // currently not needed
    modport Slave (
        input  req, req_valid,       output req_ready,
        output rsp, rsp_valid,       input  rsp_ready,
        input  eh_req, eh_req_valid, output eh_req_ready
    );

endinterface : IDMA_ND_DV


//==============================================================================
//
// cl_util.svh (v0.6.1)
//
// The MIT License (MIT)
//
// Copyright (c) 2013, 2014, 2015, 2016 ClueLogic, LLC
// http://cluelogic.com/
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//==============================================================================

`ifndef CL_UTIL_SVH
`define CL_UTIL_SVH

//------------------------------------------------------------------------------
// Class: util
//   (VIRTUAL) Provides several utility functions.
//
// See Also:
//   <putil>
//------------------------------------------------------------------------------

virtual class util;

   //---------------------------------------------------------------------------
   // Function: num_oct_digits
   //   (STATIC) Returns the number of octal digits required to represent the
   //   specified binary digits.
   //
   // Argument:
   //   num_bin_digits - The number of binary digits.
   //
   // Returns:
   //   The number of octal digits required to represent *num_bin_digits*.
   //
   // Example:
   // | assert( util::num_oct_digits( 3 ) == 1 ); //  3'b111 -> 1'o7
   // | assert( util::num_oct_digits( 4 ) == 2 ); // 4'b1111 -> 2'o17
   //---------------------------------------------------------------------------

   static function int unsigned num_oct_digits( int unsigned num_bin_digits );
      return ( num_bin_digits + 2 ) / 3;
   endfunction: num_oct_digits

   //---------------------------------------------------------------------------
   // Function: num_dec_digits
   //   (STATIC) Returns the number of decimal digits required to represent the
   //   specified binary digits.
   //
   // Argument:
   //   num_bin_digits - The number of binary digits.
   //
   // Returns:
   //   The number of decimal digits required to represent *num_bin_digits*.
   //
   // Example:
   // | assert( util::num_dec_digits( 3 ) == 1 ); //  3'b111 -> 1'd7
   // | assert( util::num_dec_digits( 4 ) == 2 ); // 4'b1111 -> 2'd15
   //---------------------------------------------------------------------------

   static function int unsigned num_dec_digits( int unsigned num_bin_digits );
      if ( num_bin_digits == 0 ) return 0;

      // log10( 2 ^ n ) + 1 = n * log10( 2 ) + 1
      // $rtoi converts real values to an 'integer' type by truncating the real
      // value.                                             ^^^^^^^^^^

      return $rtoi( num_bin_digits * $log10( 2 ) ) + 1;
   endfunction: num_dec_digits

   //---------------------------------------------------------------------------
   // Function: num_hex_digits
   //   (STATIC) Returns the number of hexadecimal digits required to represent
   //   the specified binary digits.
   //
   // Argument:
   //   num_bin_digits - The number of binary digits.
   //
   // Returns:
   //   The number of hexadecimal digits required to represent *num_bin_digits*.
   //
   // Example:
   // | assert( util::num_hex_digits( 3 ) == 1 ); //  3'b111 -> 1'h7
   // | assert( util::num_hex_digits( 4 ) == 1 ); // 4'b1111 -> 1'hF
   //---------------------------------------------------------------------------

   static function int unsigned num_hex_digits( int unsigned num_bin_digits );
      return ( num_bin_digits + 3 ) / 4;
   endfunction: num_hex_digits

   //---------------------------------------------------------------------------
   // Function normalize
   //---------------------------------------------------------------------------

   static function void normalize( int len,
				   ref int start_pos,
				   ref int end_pos );
      if ( len == 0 ) begin
	 start_pos = 0;
	 end_pos   = 0;
	 return;
      end
      if ( start_pos < 0 ) start_pos += len;
      if ( start_pos < 0 ) start_pos = 0;
      if ( end_pos < 0 )    end_pos += len;

if ( end_pos >= len ) end_pos = len - 1;


    }
) else $error("Assertion:TOP: invalid microop for MEM_FU");

// Only a sample of pseudo FPU instructions are present, mainly used for performance evaluations
assert property (@(posedge clk) disable iff(!rst_n) (valid_in & ~instr_in.reconfigure & instr_in.fu == `FP_FU)  |->
    instr_in.microop inside {7'b0000001, 7'b0000010, 7'b0000011}
) else $error("Assertion:TOP: invalid microop for FP_FU");

assert property (@(posedge clk) disable iff(!rst_n) (valid_in & ~instr_in.reconfigure & instr_in.fu == `INT_FU)  |->
    instr_in.microop inside {
                     7'b0000001,
                     7'b0000010,
                     7'b0000011,
                     7'b0000100,
                     7'b0000101,
                     7'b0000110,
                     7'b0000111,
                     7'b0001000,
                     7'b0001001,
                     7'b0001010,
                     7'b0001011,
                     7'b0001100,
                     7'b0001101,
                     7'b0001110,
                     7'b0001111,
                     7'b0010000,
                     7'b0010001,
                     7'b0010010,
                     7'b0010011,
                     7'b0010100,
                     7'b0010101,
                     7'b0010110,
                     7'b0010111,
                     7'b0011000,
                     7'b0011001,
                     7'b0011010,
                     7'b0011011,
                     7'b0011100,
                     7'b0011101,
                     7'b0011110,
                     7'b0100000,
                     7'b0100001,
                     7'b0100010,
                     7'b0100011,
                     7'b1000000,
                     7'b1000001,
                     7'b1000010,
                     7'b1000011
    }
) else $error("Assertion:TOP: invalid microop for INT_FU");

assert property (@(posedge clk) disable iff(!rst_n) valid_in |-> (instr_in.fu != `FXP_FU)
) else $error("Assertion:TOP: Fixed Point is current not supported");

assert property (@(posedge clk) disable iff(!rst_n) (valid_in & (instr_in.fu == `FXP_FU)) |-> (instr_in.microop != 7'b1111111)
) else $error("Assertion:TOP: Illegal encoding: This is used to denote a bubble cycle in the simulator, should not have reached the datapath");


  input  logic                 invalidate_i,
                               clean_i,

  output logic                 req_o,
  output logic                 rreq_o,
  output biu_size_t            size_o,
  output logic                 lock_o,
  output biu_prot_t            prot_o,
  output logic                 we_o,
  output logic [XLEN     -1:0] q_o,
  output logic                 invalidate_o,
                               clean_o,

  output logic [IDX_BITS -1:0] idx_o
);

  //////////////////////////////////////////////////////////////////
  //
  // Variables
  //
  logic                flush_dly;

  logic [IDX_BITS-1:0] adr_idx,
                       adr_idx_dly;

  logic                invalidate_hold,
                       clean_hold;

  //////////////////////////////////////////////////////////////////
  //
  // Module Body
  //

  /*delay flush signals
   */
  always @(posedge clk_i)
    flush_dly <= flush_i;

  /* Hold invalidate/clean signals
   */
  always @(posedge clk_i, negedge rst_ni)
    if      (!rst_ni ) invalidate_hold <= 1'b0;
    else if (!stall_i) invalidate_hold <= 1'b0;
    else               invalidate_hold <= invalidate_i | invalidate_hold;

  always @(posedge clk_i, negedge rst_ni)
    if      (!rst_ni ) clean_hold <= 1'b0;
    else if (!stall_i) clean_hold <= 1'b0;
    else               clean_hold <= clean_i | clean_hold;

  /*feed input signals to next stage
   */
  always @(posedge clk_i, negedge rst_ni)
    if      (!rst_ni ) req_o <= 1'b0;
    else if ( flush_i) req_o <= 1'b0;
    else if (!stall_i) req_o <= req_i;

  /* Latch signals
   */
  always @(posedge clk_i)
    if (!stall_i)
    begin
        size_o       <= size_i;
        lock_o       <= lock_i;
        prot_o       <= prot_i;
        we_o         <= we_i;
        q_o          <= d_i;
    end

  always @(posedge clk_i, negedge rst_ni)
    if (!rst_ni)
    begin
        invalidate_o <= 1'b0;
        clean_o      <= 1'b0;
    end
    else if (!stall_i)
    begin
        invalidate_o <= invalidate_i | invalidate_hold;
        clean_o      <= clean_i      | clean_hold;
    end

  /* Read-Request
   * Used to push writebuffer into Cache-memory
   * Same delay as adr_idx
   */
  assign rreq_o = req_i & ~we_i;

  /* TAG and DATA index
   * Output asynchronously, registered by memories
   */
  assign adr_idx = adr_i[BLK_OFFS_BITS +: IDX_BITS];

  always @(posedge clk_i)
    if (!stall_i || flush_dly) adr_idx_dly <= adr_idx;

  assign idx_o = stall_i /*&& !flush_dly*/ ? adr_idx_dly : adr_idx;
endmodule


// Copyright (c) 2020 Sonal Pinto
// SPDX-License-Identifier: Apache-2.0

/*

8-bit Full-Duplex SPI Master with RX/TX Queue

- Wishbone slave, 8b data bus
- Control and status
    * CPOL/CPHA (All SPI Modes Supported)
    * SPI clock rate = clk/(2 * prescaler+1)
    * clear TX/RX Queue
    * Size of the TX/RX Queue
- Peek at RX Queue without reading it
- No Chip Select - implementation left to the Host

Wishbone slave interface
    - Registered Feedback Bus Cycle, Advanced

*/

module wb_spi_master #(
    parameter BUFFER=32,
    parameter PRESCALER_WIDTH = 16
)(
    input  logic        clk,
    input  logic        rstz,
    // SPI PHY
    output logic        sclk,
    output logic        mosi,
    input  logic        miso,
    // Control and Status
    input  logic [PRESCALER_WIDTH-1:0]  prescaler,
    input  logic                        cpol,
    input  logic                        cpha,
    input  logic                        tx_clear,
    input  logic                        rx_clear,
    output logic [$clog2(BUFFER):0]     tx_size,
    output logic [$clog2(BUFFER):0]     rx_size,
    // data interface
    input  logic [7:0]  dat_i,
    output logic [7:0]  dat_o,
    input  logic        we_i,
    input  logic        stb_i,
    output logic        ack_o
);

logic txq_ack;

logic txq_full, txq_empty;
logic [$clog2(BUFFER):0] txq_size;

logic txq_din_vld, txq_din_rdy;
logic txq_dout_vld, txq_dout_rdy;
logic [7:0] txq_din, txq_dout;

logic rxq_ack;

logic rxq_full, rxq_empty;
logic [$clog2(BUFFER):0] rxq_size;

logic rxq_din_vld, rxq_din_rdy;
logic rxq_dout_vld, rxq_dout_rdy;
logic [7:0] rxq_din, rxq_dout;

// ============================================================
// TX Queue
// Shove data straight into the TX fifo
// If the fifo is full, the new data is quietly dropped

fifo #(
    .WIDTH(8     ),
    .DEPTH(BUFFER)
) u_txq (
    .clk     (clk         ),
    .rstz    (rstz        ),
    .clear   (tx_clear    ),
    .size    (txq_size    ),

    .din_vld (txq_din_vld ),
    .din_rdy (txq_din_rdy ),
    .dout    (txq_dout    ),
    .dout_vld(txq_dout_vld),
    .dout_rdy(txq_dout_rdy)
);

assign txq_din = dat_i;
assign txq_din_vld = stb_i & we_i & txq_ack;

// ============================================================
// RX Queue
// Send out data from the RX fifo when requested
// If the fifo is empty, the send out zeros

fifo #(
    .WIDTH(8     ),
    .DEPTH(BUFFER)
) u_rxq (
    .clk     (clk         ),
    .rstz    (rstz        ),
    .clear   (rx_clear    ),
    .size    (rxq_size    ),
    .full    (rxq_full    ),
    .empty   (rxq_empty   ),
    .din     (rxq_din     ),
    .din_vld (rxq_din_vld ),
    .din_rdy (rxq_din_rdy ),
    .dout    (rxq_dout    ),
    .dout_vld(rxq_dout_vld),
    .dout_rdy(rxq_dout_rdy)
);

assign dat_o = rxq_dout;
assign rxq_dout_rdy = stb_i & ~we_i & rxq_ack;

// ============================================================
// Host Interface

// register the Read/Write ACK, always ack
always_ff @(posedge clk or negedge rstz) begin
    if (~rstz) begin
        txq_ack <= 1'b0;
        rxq_ack <= 1'b0;
    end
    else begin
        txq_ack <= stb_i & we_i;
        rxq_ack <= stb_i & ~we_i;
    end
end

// size is already registered in the fifo
assign tx_size = txq_size;
assign rx_size = rxq_size;

// Advanced synchronous terminated burst
assign ack_o = stb_i & (txq_ack | rxq_ack);

// ============================================================
// SPI Master Phy

spi_master #(
    .PRESCALER_WIDTH(PRESCALER_WIDTH)
)u_spim (
    .clk      (clk         ),
    .rstz     (rstz        ),
    .sclk     (sclk        ),
    .mosi     (mosi        ),
    .miso     (miso        ),
    .prescaler(prescaler   ),
    .cpol     (cpol        ),
    .cpha     (cpha        ),
    .din      (txq_dout    ),
    .din_vld  (txq_dout_vld),
    .din_rdy  (txq_dout_rdy),
    .dout     (rxq_din     ),
    .dout_vld (rxq_din_vld )
);

// ------------------------------------------------------------
`ifdef verilator
logic _unused = &{1'b0
    , txq_din_rdy
    , txq_full
    , txq_empty
    , rxq_dout_vld
    , rxq_din_rdy
    , rxq_full
    , rxq_empty
};
`endif

endmodule

    .full    (txq_full    ),
    .empty   (txq_empty   ),


// в модуле сначало идет заполнение памяти fill_data     ()
//                            затем чтение requset_read  () 3 раза
//                           затем  запись requset_write () 3 раза

module bram_dp_simple_2clk_tb();
  localparam int RAM_WIDTH     = 16;
  localparam int RAM_ADDR_BITS = 3;
  localparam int RAM_DEPTH     = 2**RAM_ADDR_BITS;

  logic                           clk_a_i;
  logic                           clk_b_i;
  logic [ RAM_ADDR_BITS - 1 : 0 ] addr_a_i;
  logic [ RAM_ADDR_BITS - 1 : 0 ] addr_b_i;
  logic [ RAM_WIDTH     - 1 : 0 ] data_a_i;
  logic [ RAM_WIDTH     - 1 : 0 ] data_b_o;
  logic                           we_a_i;
  logic                           en_b_i;

  bram_dp_simple_2clk # (
    .RAM_WIDTH     ( RAM_WIDTH     ),

  )
  bram_dp_simple_2clk_inst (
    .clk_a_i  ( clk_a_i  ),
    .clk_b_i  ( clk_b_i  ),
    .addr_a_i ( addr_a_i ),
    .addr_b_i ( addr_b_i ),
    .data_a_i ( data_a_i ),
    .we_a_i   ( we_a_i   ),
    .en_b_i   ( en_b_i   ),
    .data_b_o ( data_b_o )
  );
//------------------------------------------------- clk_a_i и clk_b_i
initial begin
  clk_a_i = 1'b0;
  forever begin
    clk_a_i = ~clk_a_i; #5;
  end
end

initial begin
  clk_b_i = 1'b1; #2.5;
  forever begin
    clk_b_i = ~clk_b_i; #5;
  end
end
//------------------------------------------------- WRITE MODULE
int p;
event end_of_fill;

initial begin
  #10;
  p    = 0;
  we_a_i = '0;

  repeat(RAM_DEPTH)begin      // заполнение памяти
    fill_data(); #5;
    p = p + 1;
  end

  ->end_of_fill;
  wait(end_of_read.triggered)// ждем окончания чтения
  #10;
  requset_write();           // запись в память
  #30;
  requset_write();
  #30;
  requset_write();
  #30;
  $finish;
end

task fill_data();
  @(posedge clk_a_i)
    we_a_i   = '1;
    addr_a_i = p;
    data_a_i = $urandom_range(0, 2**RAM_WIDTH - 1);
  #5; @(posedge clk_a_i)
    we_a_i = '0;
endtask

task requset_write();
  @(posedge clk_a_i)
    addr_a_i = $urandom_range(0, RAM_DEPTH - 1);
    we_a_i   = '1;
    data_a_i = $urandom_range(0, 2**RAM_WIDTH - 1);
  #5; @(posedge clk_a_i)
    we_a_i = '0;
endtask
//------------------------------------------------- READ MODULE
event end_of_read;
initial begin
  wait(end_of_fill.triggered) // ждем окончания заполнения памяти
  #10;                        // чтение памяти
  requset_read();
  #30;
  requset_read();
  #30;
  requset_read();
  #30;
  ->end_of_read;
end

task requset_read();
  @(posedge clk_b_i)
    en_b_i   = 1'b1;
    addr_b_i = $urandom_range(0, RAM_DEPTH - 1);
  #5; @(posedge clk_b_i);
    en_b_i   = 1'b0;
endtask

endmodule


// generates 1024x768 (actually 512x768) @ 60Hz, from a 32.5MHz input clock
module videoTimer(
	input clk,
	input clk8_en_n,
	input clk8_en_p,
	input videoCycle,
	input vid_alt,
	output [20:0] videoAddr,
	output reg hsync,
	output reg vsync,
	output _hblank,
	output _vblank,
	output loadPixels,
	output refresh
);

	// timing data from http://tinyvga.com/vga-timing/1024x768@60Hz
   // horizontal timing is doubled due to video clock being 32Mhz
	localparam 	kTotalWidth = 704,
			kVisibleWidth = 512,
			kHsyncStart = 526,
			kHsyncEnd = 529,
			kTotalHeight = 370,
			kVisibleHeight = 342,
			kVsyncStart = 342,
			kVsyncEnd = 346;

	wire endline = (xpos == kTotalWidth-1);

        // reserve one video cycle per scan line for refresh
        assign refresh = (xpos[9:3] == 7'h54);

	always @(posedge clk) begin
		if (clk8_en_n || clk8_en_p) begin
			if (endline)
				xpos <= 0;
			else if (xpos == 0 && !videoCycle)
				// hold xpos at 0, until xpos and videoCycle are in phase
				xpos <= 0;
			else
				xpos <= xpos + 1'b1;

			if (endline) begin
				if (ypos == kTotalHeight-1)
					ypos <= 0;
				else
					ypos <= ypos + 1'b1;
			end

			hsync <= ~(xpos >= kHsyncStart && xpos <= kHsyncEnd);
			vsync <= ~(ypos >= kVsyncStart && ypos <= kVsyncEnd);
		end
	end

	assign _hblank = ~(xpos >= kVisibleWidth);
	assign _vblank = ~(ypos >= kVisibleHeight);

	assign videoAddr = 21'h1FD380 - (vid_alt ? 16'h0 : 16'h4000) + { ypos[8:0], xpos[8:4] };
	assign loadPixels = _vblank && xpos < kVisibleWidth && videoCycle & !xpos[3];

endmodule


/* -----------------------------------------------------------------------------
* Project Name   : Architectures of Processor Systems (APS) lab work
* Organization   : National Research University of Electronic Technology (MIET)
* Department     : Institute of Microdevices and Control Systems
* Author(s)      : Andrei Solodovnikov
* Email(s)       : hepoh@org.miet.ru

See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------
*/

package bluster_pkg;
  localparam INIT_MSG_SIZE  = 41;
  localparam FLASH_MSG_SIZE = 57;
  localparam ACK_MSG_SIZE   = 4;

/* -----------------------------------------------------------------------------
    Lab_15 tb tasks
*  -----------------------------------------------------------------------------
*/

  task automatic send_data(input byte mem[$], ref logic clk_i, tx_valid, tx_busy, ref logic [7:0] tx_data);
    for(int i = mem.size()-1; i >=0; i--) begin
      tx_data = mem[i];
      tx_valid = 1'b1;
      @(posedge clk_i);
      tx_valid = 1'b0;
      @(posedge clk_i);
      while(tx_busy) @(posedge clk_i);
    end
  endtask

  task automatic rcv_data(input int size, ref logic clk_i, rx_valid, tx_o, ref logic [7:0] rx_data);
    automatic logic [0:FLASH_MSG_SIZE-1][7:0] str;
    automatic logic [3:0][7:0] size_val;
    for(int i = 0; i < size; i++) begin
      @(posedge clk_i);
      while(!rx_valid)@(posedge clk_i);
      str[i] = rx_data;
      size_val[3-i] = rx_data;
    end
    case(size)
      INIT_MSG_SIZE: begin
        $display("%s", str[0:INIT_MSG_SIZE-2]);
        assert(str[0:INIT_MSG_SIZE-10] == "ready for flash starting from 0x")begin end

    wait(tx_o);
  endtask

  task automatic program_region(input string fname, ref logic clk_i, tx_valid, rx_valid, tx_o, tx_busy, reset, ref logic [7:0] rx_data, tx_data);
    automatic int fd, start_addr;
    automatic logic [31:0] data;
    automatic byte mem[$];
    automatic byte str [4];
    automatic logic [3:0][7:0] size;
    $display("\n%0t. Start programming %s", $time, fname);
    fd = $fopen(fname, "r");
    assert(fd)
    else $fatal(1, "Can't open file %s", fname);
    void'($fscanf(fd, "@%x\w", start_addr));
    start_addr <<=2;
    while(!$feof(fd)) begin
      $fscanf(fd, "%x\w", data);
      mem.push_back(data[ 7: 0]);
      mem.push_back(data[15: 8]);
      mem.push_back(data[23:16]);
      mem.push_back(data[31:24]);
    end
    $fclose(fd);
    size = mem.size();
    str = {start_addr[7:0],start_addr[15:8],start_addr[23:16],start_addr[31:24]};
    send_data(str, clk_i, tx_valid, tx_busy, tx_data);
    rcv_data(INIT_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    str = {size[0],size[1],size[2],size[3]};
    send_data(str, clk_i, tx_valid, tx_busy, tx_data);
    rcv_data(ACK_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    send_data(mem, clk_i, tx_valid, tx_busy, tx_data);
    rcv_data(FLASH_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    $display("%0t. Region has been programmed", $time);
    assert(reset)
    else $error("Reset must be equal 1 while flashing is not done.");
  endtask

  task automatic finish_programming(ref logic clk_i, tx_valid, tx_busy, reset, ref logic [7:0] tx_data);
    automatic byte mem[$];
    mem.push_back(8'hff);
    mem.push_back(8'hff);
    mem.push_back(8'hff);
    mem.push_back(8'hff);
    send_data(mem, clk_i, tx_valid, tx_busy, tx_data);
    $display("Flashing is complete");
    assert(!reset)
    else $error("Reset must be equal 0 after flashing is complete.");
  endtask

  task automatic dummy_programming(ref logic clk_i, tx_valid, rx_valid, tx_o, tx_busy, reset, ref logic [7:0] rx_data, tx_data);
    automatic byte str [4] = {8'd0, 8'd0, 8'd0, 8'd0};
    rcv_data(INIT_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    send_data(str, clk_i, tx_valid, tx_busy, tx_data);
    rcv_data(ACK_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    rcv_data(FLASH_MSG_SIZE, clk_i, rx_valid, tx_o, rx_data);
    finish_programming(clk_i, tx_valid, tx_busy, reset, rx_data);
  endtask
endpackage

        else $error("Init message format is incorrect. Should be \"ready for flash starting from 0xADDR\"");
      end
      FLASH_MSG_SIZE: begin
        $display("%s", str[0:FLASH_MSG_SIZE-2]);
        assert((str[0:16] == "finished write 0x") && (str[25+:23] == " bytes starting from 0x"))begin end


// 注意：这个模块的查询是立即的，但更新需要两个周期

module cam_cmp_lutram #(parameter int PACKS_OF_5_BITS = 4) (
  input  logic                            clk            ,
  input  logic                            rst_n          ,
  input  logic                            update_i       , // 注意，update 之后两个周期才可以生效
  input  logic [PACKS_OF_5_BITS-1:0][4:0] set_key_i      ,
  input  logic                            set_key_valid_i,
  input  logic [PACKS_OF_5_BITS-1:0][4:0] cmp_key_i      ,
  output logic                            hit_o          ,
  output logic [PACKS_OF_5_BITS-1:0][4:0] key_o
);
  logic[PACKS_OF_5_BITS - 1 :0] sub_hit;
  assign hit_o = &sub_hit;

      refill_q <= set_key_valid_i;
      refill_v_q <= '1;
    end else begin
      refill_q <= '0;
      refill_v_q <= '0;
    end
  end

  for(genvar i = 0 ; i < PACKS_OF_5_BITS ; i ++) begin
    dpram_32x1  dpram_32x1_inst (
      .CLK(clk),
      .RST(~rst_n),
      .WEN(update_i | refill_v_q),
      .A(cmp_key_i[i]),
      .AW(w_addr_q[i]),
      .DI(refill_q),
      .DO(sub_hit[i])
    );
  end

endmodule

  logic [PACKS_OF_5_BITS-1:0][4:0] w_addr_q;
  // 注意：若 w_addr_q 被清零，则出现错误，每次 UPDATE 时一定要清零上次的结果
  assign key_o = w_addr_q;
  logic refill_v_q, refill_q;


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

`timescale 1 ns / 1 ns
module CornersAndDescriptorsTestbench();

`include "../etc/HCTestbenchGen/HarrisCornersTestbenchData.inc.sv"

localparam COORD_BITS = 10;
localparam PARALLEL_MODULES = 4;
localparam INV_RATE = 1;

logic clk;
int cursor = 0;
int out_cursor = 0;
int error_count = 0;

logic in_valid;
logic [LUMA_BITS-1:0] in_pixel;
logic [COORD_BITS-1:0] in_x, in_y;
logic in_consume, in_frame_type, reset;
logic in_begin_frame_reset;
logic out_modeswitch, out_mode;
logic [255:0] out_descriptor;
logic out_valid;
logic [COORD_BITS-1:0] out_feature_x, out_feature_y;
integer rate_ctr;

BufferedCornersAndDescriptors #(
	.LUMA_BITS(LUMA_BITS),
	.MATRIX_BITS(MATRIX_BITS),
	.MAX_IMAGE_WIDTH(2048),
	.MAX_IMAGE_HEIGHT(2048),
	.COORD_BITS(COORD_BITS),
	.NONMAX_SCORE_BITS(SCORE_BITS),
	.PARALLEL_MODULES(PARALLEL_MODULES), //stall when all processing
	.BUFFERING_STRATEGY(1),
	.THRESHOLD(200)
) test_mod (
	.clk(clk),
	.reset(reset),
	.in_begin_frame_reset(in_begin_frame_reset),
	.out_frame_reset_complete(out_frame_reset_complete),
	.r_width(IMAGE_WIDTH),
	.r_height(IMAGE_HEIGHT),
	.in_valid(in_valid),
	.in_pixel(in_pixel),
	.in_x(in_x),
	.in_y(in_y),
	.in_consume(in_consume),
	.in_mask(0),
	.out_descriptor(out_descriptor),
	.out_valid(out_valid),
	.out_feature_x(out_feature_x),
	.out_feature_y(out_feature_y)
);

assign in_valid = rate_ctr == 0;
assign in_pixel = image_data[cursor];
assign in_x = cursor % IMAGE_WIDTH;
assign in_y = cursor / IMAGE_WIDTH;
assign in_consume = 1'b1;
assign reset = rate_ctr == -3;
assign in_begin_frame_reset = rate_ctr == -2;

initial begin
	$display("Testing CornersAndDescriptors");
	error_count = 0;
	rate_ctr = -4;
end

always begin
	clk = 0;
	#5;
	clk = 1;
	#5;
end

always @(posedge clk) begin
	if (in_valid) begin
		cursor <= cursor + 1;
	end

	if (rate_ctr == INV_RATE - 1) begin
		rate_ctr <= 0;
	end else begin

	end

	if (out_valid/* && !out_modeswitch*/) begin
		while (
			out_feature_y > corner_list[out_cursor].y ||
			out_feature_y === corner_list[out_cursor].y && out_feature_x > corner_list[out_cursor].x
		) begin
			$display("Missed feature at (%d, %d)", corner_list[out_cursor].x, corner_list[out_cursor].y);
			error_count++;
			out_cursor++;
		end

		if (
			out_feature_x !== corner_list[out_cursor].x ||
			out_feature_y !== corner_list[out_cursor].y
		) begin
			$display("Unexpected feature at (%d, %d)", out_feature_x, out_feature_y);
			error_count++;
		end else if (
			out_descriptor !== corner_list[out_cursor].descriptor
		) begin
			$display("Error!! at (%d,%d) (%h) *****", out_feature_x, out_feature_y, out_descriptor);
			$display("Expected descriptor  - (%h)", corner_list[out_cursor].descriptor);
			error_count++;
			out_cursor++;
		end else begin
			$display("Correct at (%d,%d) (%h)", out_feature_x, out_feature_y, out_descriptor);
			out_cursor++;
		end
	end
end

endmodule


// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

// Authors:
//  - Tim Fischer <fischeti@iis.ee.ethz.ch>

`include "axi/typedef.svh"
`include "axi/assign.svh"
`include "idma/typedef.svh"

/// An endpoint node with a DMA master port and a Simulation Memory Slave port
module floo_dma_test_node  #(
  parameter time         TA                   = 1ns,
  parameter time         TT                   = 9ns,
  parameter int unsigned BufferDepth          = 16,
  parameter int unsigned NumAxInFlight        = 16,
  parameter floo_test_pkg::slave_type_e SlaveType = floo_test_pkg::FastSlave,
  parameter floo_pkg::axi_cfg_t AxiCfg = '{default:0},
  parameter type axi_req_t                    = logic,
  parameter type axi_rsp_t                    = logic,
  parameter type axi_in_req_t                 = axi_req_t,
  parameter type axi_in_rsp_t                 = axi_rsp_t,
  parameter type axi_out_req_t                = axi_req_t,
  parameter type axi_out_rsp_t                = axi_rsp_t,
  parameter int unsigned TFLenWidth           = 32,
  parameter int unsigned MemSysDepth          = 0,
  parameter logic [AxiCfg.AddrWidth-1:0] MemBaseAddr = 32'h0,
  parameter logic [AxiCfg.AddrWidth-1:0] MemSize     = 32'h10000,
  parameter bit          MaskInvalidData      = 1,
  parameter bit          RAWCouplingAvail     = 1,
  parameter bit          HardwareLegalizer    = 1,
  parameter bit          RejectZeroTransfers  = 1,
  parameter bit          ErrorHandling        = 0,
  parameter int          JobId                = -1,
  parameter bit          EnableDebug          = 0
) (
  input logic  clk_i,
  input logic  rst_ni,
  input axi_in_req_t  axi_in_req_i,
  output axi_in_rsp_t axi_in_rsp_o,
  output axi_out_req_t axi_out_req_o,
  input axi_out_rsp_t axi_out_rsp_i,
  output logic end_of_sim_o
);

  import idma_pkg::*;

  // debug
  localparam bit Debug         = 1'b0;
  localparam bit ModelOutput   = 1'b0;
  localparam bit PrintFifoInfo = 1'b0;

  // dependent parameters
  localparam int unsigned StrbWidth       = AxiCfg.DataWidth / 8;
  localparam int unsigned OffsetWidth     = $clog2(StrbWidth);

  // parse error handling caps
  localparam idma_pkg::error_cap_e ErrorCap = ErrorHandling ? idma_pkg::ERROR_HANDLING :
                                                            idma_pkg::NO_ERROR_HANDLING;

  // static types
  typedef logic [7:0] byte_t;

  // dependent typed
  typedef logic [AxiCfg.AddrWidth-1:0]     addr_t;
  typedef logic [AxiCfg.DataWidth-1:0]     data_t;
  typedef logic [StrbWidth-1:0]     strb_t;
  typedef logic [AxiCfg.UserWidth-1:0]     user_t;
  typedef logic [AxiCfg.InIdWidth-1:0]  id_in_t;
  typedef logic [AxiCfg.OutIdWidth-1:0] id_out_t;
  typedef logic [OffsetWidth-1:0]   offset_t;
  typedef logic [TFLenWidth-1:0]    tf_len_t;

  // AXI typedef
  `AXI_TYPEDEF_ALL(axi_xbar, addr_t, id_out_t, data_t, strb_t, user_t)

  typedef struct packed {
    axi_xbar_ar_chan_t ar_chan;
  } axi_xbar_read_meta_channel_t;
  typedef union packed {
    axi_xbar_read_meta_channel_t axi;
  } read_meta_channel_t;
  typedef struct packed {
    axi_xbar_aw_chan_t aw_chan;
  } axi_xbar_write_meta_channel_t;
  typedef union packed {
    axi_xbar_write_meta_channel_t axi;
  } write_meta_channel_t;

  // iDMA request / response types

  `IDMA_TYPEDEF_FULL_RSP_T(idma_rsp_t, addr_t)

  //--------------------------------------
  // DMA Signals
  //--------------------------------------

  // dma request
  idma_req_t idma_req;
  logic req_valid;
  logic req_ready;

  // dma response
  idma_rsp_t idma_rsp;
  logic rsp_valid;
  logic rsp_ready;

  // error handler
  idma_eh_req_t idma_eh_req;
  logic eh_req_valid;
  logic eh_req_ready;

  // AXI Xbar signals
  axi_xbar_req_t [0:0] xbar_in_req;
  axi_xbar_resp_t [0:0] xbar_in_rsp;
  axi_xbar_req_t [1:0] xbar_out_req;
  axi_xbar_resp_t [1:0]  xbar_out_rsp;

  // AXI4 master
  axi_xbar_req_t axi_dma_req, axi_dma_read_req, axi_dma_write_req;
  axi_xbar_resp_t axi_dma_rsp, axi_dma_read_rsp, axi_dma_write_rsp;

  // AXI4 slave
  axi_xbar_req_t axi_mem_req;
  axi_xbar_resp_t axi_mem_rsp;

  // busy signal
  idma_busy_t busy;

  typedef struct packed {
    logic [31:0] idx;
    logic [AxiCfg.AddrWidth-1:0] start_addr;
    logic [AxiCfg.AddrWidth-1:0] end_addr;
  } xbar_rule_t;

  xbar_rule_t [0:0] XbarAddrMap;
  assign XbarAddrMap = '{
    '{ idx: 0, start_addr: MemBaseAddr, end_addr: MemBaseAddr + MemSize }
  };

  localparam axi_pkg::xbar_cfg_t XbarCfg = '{
    NoSlvPorts:         1,
    NoMstPorts:         2,
    MaxSlvTrans:        128,
    MaxMstTrans:        128,
    FallThrough:        1,
    LatencyMode:        axi_pkg::CUT_ALL_PORTS,
    AxiIdWidthSlvPorts: AxiCfg.OutIdWidth,
    AxiIdUsedSlvPorts:  AxiCfg.OutIdWidth,
    UniqueIds:          0,
    AxiAddrWidth:       AxiCfg.AddrWidth,
    AxiDataWidth:       AxiCfg.DataWidth,
    NoAddrRules:        1,
    PipelineStages:     0
    // NoMulticastPorts:   0,
    // NoMulticastRules:   0
  };

  //--------------------------------------
  // DMA
  //--------------------------------------
  idma_backend_rw_axi #(
    .DataWidth           ( AxiCfg.DataWidth       ),
    .AddrWidth           ( AxiCfg.AddrWidth       ),
    .AxiIdWidth          ( AxiCfg.OutIdWidth      ),
    .UserWidth           ( AxiCfg.UserWidth       ),
    .TFLenWidth          ( TFLenWidth             ),
    .MaskInvalidData     ( MaskInvalidData        ),
    .BufferDepth         ( BufferDepth            ),
    .RAWCouplingAvail    ( RAWCouplingAvail       ),
    .HardwareLegalizer   ( HardwareLegalizer      ),
    .RejectZeroTransfers ( RejectZeroTransfers    ),
    .ErrorCap            ( ErrorCap               ),
    .PrintFifoInfo       ( PrintFifoInfo          ),
    .NumAxInFlight       ( NumAxInFlight          ),
    .MemSysDepth         ( MemSysDepth            ),
    .idma_req_t          ( idma_req_t             ),
    .idma_rsp_t          ( idma_rsp_t             ),
    .idma_eh_req_t       ( idma_eh_req_t          ),
    .idma_busy_t         ( idma_busy_t            ),
    .axi_req_t           ( axi_xbar_req_t         ),
    .axi_rsp_t           ( axi_xbar_resp_t        ),
    .write_meta_channel_t( write_meta_channel_t   ),
    .read_meta_channel_t ( read_meta_channel_t    )
  ) i_idma_backend (
    .clk_i          ( clk_i           ),
    .rst_ni         ( rst_ni          ),
    .testmode_i     ( 1'b0            ),
    .idma_req_i     ( idma_req        ),
    .req_valid_i    ( req_valid       ),
    .req_ready_o    ( req_ready       ),
    .idma_rsp_o     ( idma_rsp        ),
    .rsp_valid_o    ( rsp_valid       ),
    .rsp_ready_i    ( rsp_ready       ),
    .idma_eh_req_i  ( idma_eh_req     ),
    .eh_req_valid_i ( eh_req_valid    ),
    .eh_req_ready_o ( eh_req_ready    ),
    .axi_read_req_o (axi_dma_read_req ),
    .axi_read_rsp_i (axi_dma_read_rsp ),
    .axi_write_req_o(axi_dma_write_req),
    .axi_write_rsp_i(axi_dma_write_rsp),
    .busy_o         ( busy            )
  );

  // Read Write Join
  axi_rw_join #(
    .axi_req_t        ( axi_xbar_req_t ),
    .axi_resp_t       ( axi_xbar_resp_t )
) i_axi_rw_join (
    .clk_i,
    .rst_ni,
    .slv_read_req_i   ( axi_dma_read_req  ),
    .slv_read_resp_o  ( axi_dma_read_rsp  ),
    .slv_write_req_i  ( axi_dma_write_req ),
    .slv_write_resp_o ( axi_dma_write_rsp ),
    .mst_req_o        ( axi_dma_req       ),
    .mst_resp_i       ( axi_dma_rsp       )
);

  axi_xbar #(
    .Cfg          ( XbarCfg             ),
    .Connectivity ( '1                  ),
    .ATOPs        ( 0                   ),
    .slv_aw_chan_t( axi_xbar_aw_chan_t  ),
    .mst_aw_chan_t( axi_xbar_aw_chan_t  ),
    .w_chan_t     ( axi_xbar_w_chan_t   ),
    .slv_b_chan_t ( axi_xbar_b_chan_t   ),
    .mst_b_chan_t ( axi_xbar_b_chan_t   ),
    .slv_ar_chan_t( axi_xbar_ar_chan_t  ),
    .mst_ar_chan_t( axi_xbar_ar_chan_t  ),
    .slv_r_chan_t ( axi_xbar_r_chan_t   ),
    .mst_r_chan_t ( axi_xbar_r_chan_t   ),
    .slv_req_t    ( axi_xbar_req_t      ),
    .slv_resp_t   ( axi_xbar_resp_t     ),
    .mst_req_t    ( axi_xbar_req_t      ),
    .mst_resp_t   ( axi_xbar_resp_t     ),
    .rule_t       ( xbar_rule_t         )
  ) i_xbar (
    .clk_i                  ( clk_i         ),
    .rst_ni                 ( rst_ni        ),
    .test_i                 ( 1'b0          ),
    .slv_ports_req_i        ( xbar_in_req   ),
    .slv_ports_resp_o       ( xbar_in_rsp   ),
    .mst_ports_req_o        ( xbar_out_req  ),
    .mst_ports_resp_i       ( xbar_out_rsp  ),
    .addr_map_i             ( XbarAddrMap   ),
    .en_default_mst_port_i  ( '1            ),
    .default_mst_port_i     ( 1'b1          )
  );

  floo_axi_rand_slave #(
    .AxiCfg       ( AxiCfg                    ),
    .ApplTime     ( TA                        ),
    .TestTime     ( TT                        ),
    .SlaveType    ( SlaveType                 ),
    .NumSlaves    ( 1                         ),
    .axi_req_t    ( axi_xbar_req_t            ),
    .axi_rsp_t    ( axi_xbar_resp_t           )
  ) i_dma_sim_mem (
    .clk_i              ( clk_i       ),
    .rst_ni             ( rst_ni      ),
    .slv_port_req_i     ( axi_mem_req ),
    .slv_port_rsp_o     ( axi_mem_rsp ),
    .mon_mst_port_req_o (             ),
    .mon_mst_port_rsp_o (             )
  );

  floo_axi_rand_slave #(
    .AxiCfg       ( floo_pkg::axi_cfg_swap_iw(AxiCfg) ),
    .ApplTime     ( TA                                ),
    .TestTime     ( TT                                ),
    .SlaveType    ( SlaveType                         ),
    .NumSlaves    ( 1                                 ),
    .axi_req_t    ( axi_in_req_t                      ),
    .axi_rsp_t    ( axi_in_rsp_t                      )
  ) i_sink_in_mem (
    .clk_i              ( clk_i         ),
    .rst_ni             ( rst_ni        ),
    .slv_port_req_i     ( axi_in_req_i  ),
    .slv_port_rsp_o     ( axi_in_rsp_o  ),
    .mon_mst_port_req_o (               ),
    .mon_mst_port_rsp_o (               )
  );

  assign xbar_in_req = axi_dma_req;
  assign axi_dma_rsp = xbar_in_rsp;
  assign axi_mem_req = xbar_out_req[0];
  assign xbar_out_rsp[0] = axi_mem_rsp;
  assign axi_out_req_o = xbar_out_req[1];
  assign xbar_out_rsp[1] = axi_out_rsp_i;

  //--------------------------------------
  // DMA Driver
  //--------------------------------------
  // virtual interface definition
  IDMA_DV #(
    .DataWidth  ( AxiCfg.DataWidth  ),
    .AddrWidth  ( AxiCfg.AddrWidth  ),
    .UserWidth  ( AxiCfg.UserWidth  ),
    .AxiIdWidth ( AxiCfg.OutIdWidth ),
    .TFLenWidth ( TFLenWidth        )
  ) idma_dv (clk_i);

  // DMA driver type
  typedef idma_test::idma_driver #(
      .DataWidth  ( AxiCfg.DataWidth  ),
      .AddrWidth  ( AxiCfg.AddrWidth  ),
      .UserWidth  ( AxiCfg.UserWidth  ),
      .AxiIdWidth ( AxiCfg.OutIdWidth ),
      .TFLenWidth ( TFLenWidth        ),
      .TA         ( TA                ),
      .TT         ( TT                )
  ) drv_t;

  // instantiation of the driver
  drv_t drv = new(idma_dv);

  // connect virtual driver interface to structs
  assign idma_req              = idma_dv.req;
  assign req_valid             = idma_dv.req_valid;
  assign rsp_ready             = idma_dv.rsp_ready;
  assign idma_eh_req           = idma_dv.eh_req;
  assign eh_req_valid          = idma_dv.eh_req_valid;
  // connect struct to virtual driver interface
  assign idma_dv.req_ready     = req_ready;
  assign idma_dv.rsp           = idma_rsp;
  assign idma_dv.rsp_valid     = rsp_valid;
  assign idma_dv.eh_req_ready  = eh_req_ready;

  //--------------------------------------
  // DMA Job Queue
  //--------------------------------------
  // job type definition
  typedef idma_test::idma_job #(
      .AddrWidth   ( AxiCfg.AddrWidth )
  ) tb_dma_job_t;

  // request and response queues
  tb_dma_job_t req_jobs [$];
  tb_dma_job_t rsp_jobs [$];

  `include "tb_tasks.svh"

  //--------------------------------------
  // Read Job queue from File
  //--------------------------------------
  initial begin
    string job_file, job_name, job_dir;
    if ($value$plusargs ("JOB_NAME=%s", job_name)) begin
      job_name = $sformatf("%s", job_name);
    end else begin
      job_name = "single_tf";
    end
    if ($value$plusargs("JOB_DIR=%s", job_dir)) begin
      job_dir = $sformatf("%s", job_dir);
    end else begin
      job_dir = "hw/test/jobs";
    end
    if (JobId != -1) begin
      job_file = $sformatf("%s/%s_%0d.txt", job_dir, job_name, JobId);
    end else begin
      job_file = $sformatf("%s/%s.txt", job_dir, job_name);
    end
    $display("[DMA%0d] Reading from %s", JobId + 1, job_file);
    read_jobs(job_file, req_jobs);
    read_jobs(job_file, rsp_jobs);
  end

  //--------------------------------------
  // Launch Transfers
  //--------------------------------------

  logic                error;
  logic                last;
  idma_pkg::err_type_t err_type;
  axi_pkg::resp_t      cause;
  addr_t               burst_addr;
  int                  err_idx [$];
  real                 injection_ratio;

  initial begin
    // reset driver
    drv.reset_driver();
    // wait until reset has completed
    wait (rst_ni);
    // print a job summary
    print_summary(req_jobs);
    // wait some additional time
    if (!$value$plusargs("TRAFFIC_INJ_RATIO=%f", injection_ratio)) begin
      injection_ratio = 1.0;
      $display("[DMA%0d] Using default injection ratio of 1.0", JobId + 1);
    end else begin
      $display("[DMA%0d] Using injection ratio of %f", JobId + 1, injection_ratio);
    end

    // wait some additional time
    #2ns;

    // run all requests in queue
    while (req_jobs.size() != 0) begin
      automatic tb_dma_job_t now;
      // Inject delay based on injection ratio
      // Compute whether to inject in the next cycle based on the injection ratio
      if (!($urandom_range(0, 100) < (injection_ratio * 100))) begin
        // Wait for the next cycle
        if (EnableDebug && (JobId == 100)) $display("[DMA%0d] Delay", JobId + 1);
        @(posedge clk_i);
        continue;
      end
      // pop front to get a job
      now = req_jobs.pop_front();
      // print job to terminal
      if (EnableDebug) $display("[DMA%0d]%s", JobId, now.pprint());
      // launch DUT
      drv.launch_tf(
                    now.length,
                    now.src_addr,
                    now.dst_addr,
                    now.src_protocol,
                    now.dst_protocol,
                    now.aw_decoupled,
                    now.rw_decoupled,
                    $clog2(now.max_src_len),
                    $clog2(now.max_dst_len),
                    now.max_src_len != 'd256,
                    now.max_dst_len != 'd256,
                    now.id
                  );
    end
    // once done: launched all transfers
    $display("[DMA%0d] Launched all Transfers.", JobId + 1);
  end

initial begin
  end_of_sim_o = 1'b0;
  // wait until reset has completed
  wait (rst_ni);
  // run all requests in queue
  while (rsp_jobs.size() != 0) begin
      // pop front to get a job
      automatic tb_dma_job_t now = rsp_jobs.pop_front();
      // launch DUT
      drv.wait_tf(cause, err_type, burst_addr, error, last);
      if (error) begin
        $display("[DMA%0d] Error: %s", JobId + 1, idma_pkg::err_type_t'(err_type));
        $display("[DMA%0d] Cause: %s", JobId + 1, axi_pkg::resp_t'(cause));
        $display("[DMA%0d] Burst Address: 0x%0h", JobId + 1, burst_addr);
      end
  end
  // stop simulation
  end_of_sim_o = 1'b1;
end

endmodule

`IDMA_TYPEDEF_FULL_REQ_T(idma_req_t, id_out_t, addr_t, tf_len_t)


/*
 *  Copyright 2023 CEA*
 *  *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA)
 *
 *  SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
 *
 *  Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
 *  may not use this file except in compliance with the License, or, at your
 *  option, the Apache License version 2.0. You may obtain a copy of the
 *  License at
 *
 *  https://solderpad.org/licenses/SHL-2.1/
 *
 *  Unless required by applicable law or agreed to in writing, any work
 *  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.
 */
/*
 *  Authors       : Cesar Fuguet
 *  Creation Date : April, 2021
 *  Description   : Dcache memory request to axi read channels
 *  History       :
 */
module hpdcache_mem_to_axi_read
import hpdcache_pkg::*;
#(
    parameter type hpdcache_mem_req_t    = logic,
    parameter type hpdcache_mem_resp_r_t = logic,
    parameter type ar_chan_t = logic,
    parameter type r_chan_t  = logic
)
(
    output logic                          req_ready_o,
    input  logic                          req_valid_i,
    input  hpdcache_mem_req_t             req_i,

    input  logic                          resp_ready_i,
    output logic                          resp_valid_o,
    output hpdcache_mem_resp_r_t          resp_o,

    output logic                          axi_ar_valid_o,
    output ar_chan_t                      axi_ar_o,
    input  logic                          axi_ar_ready_i,

    input  logic                          axi_r_valid_i,
    input  r_chan_t                       axi_r_i,
    output logic                          axi_r_ready_o
);

    logic                lock;
    axi_pkg::cache_t     cache;
    hpdcache_mem_error_e resp;

    assign  lock  = (req_i.mem_req_command == HPDCACHE_MEM_ATOMIC) &&
                    (req_i.mem_req_atomic  == HPDCACHE_MEM_ATOMIC_LDEX);

    assign  cache = req_i.mem_req_cacheable ?
                    axi_pkg::CACHE_BUFFERABLE |
                    axi_pkg::CACHE_MODIFIABLE |
                    axi_pkg::CACHE_RD_ALLOC   |
                    axi_pkg::CACHE_WR_ALLOC   : axi_pkg::CACHE_MODIFIABLE;

    always_comb
    begin : resp_decode_comb

            axi_ar_o.id       = req_i.mem_req_id,
            axi_ar_o.addr     = req_i.mem_req_addr,
            axi_ar_o.len      = req_i.mem_req_len,
            axi_ar_o.size     = req_i.mem_req_size,
            axi_ar_o.burst    = axi_pkg::BURST_INCR,
            axi_ar_o.lock     = lock,
            axi_ar_o.cache    = cache,
            axi_ar_o.prot     = '0,
            axi_ar_o.qos      = '0,
            axi_ar_o.region   = '0,
            axi_ar_o.user     = '0;

    assign  axi_r_ready_o           = resp_ready_i,
            resp_valid_o            = axi_r_valid_i,
            resp_o.mem_resp_r_error = resp,
            resp_o.mem_resp_r_id    = axi_r_i.id,
            resp_o.mem_resp_r_data  = axi_r_i.data,
            resp_o.mem_resp_r_last  = axi_r_i.last;

endmodule

        case (axi_r_i.resp)
            axi_pkg::RESP_SLVERR,
            axi_pkg::RESP_DECERR: resp = HPDCACHE_MEM_RESP_NOK;
            default:              resp = HPDCACHE_MEM_RESP_OK;
        endcase


`include "common_defs.svh"

module graphics_controller(
    Bus_if.slave  data_bus,
    VGA_if.master vga
);

    wire vga_clk, bus_clk, bus_clk_2x, ram_clk;
    wire rst;

    assign vga.clk = vga_clk;
    assign vga_clk = data_bus.clk._50M;
    assign bus_clk = data_bus.clk.base;
    assign bus_clk_2x = data_bus.clk.base_2x;
    assign rst = data_bus.clk.rst;

    Word_t mem_data;

    Word_t pixel_offset_reg_i[0:1];
    Word_t pixel_offset_reg_o[0:1];
    Word_t pixel_offset;
    Word_t pixel_count;

    GraphicsMemoryAddress_t mem_address_offset, mem_address_overflow, mem_address;
    assign mem_address_offset = GraphicsMemoryAddress_t'(pixel_offset >> 2);
    assign mem_address_overflow = GraphicsMemoryAddress_t'(pixel_count >> 2) + mem_address_offset + 1'b1;
    assign mem_address = (mem_address_overflow >= `GRAPHICS_CONFIG_ADDRESS * 2) ?
                         (mem_address_overflow - `GRAPHICS_CONFIG_ADDRESS * 2) : (
                             (mem_address_overflow >= `GRAPHICS_CONFIG_ADDRESS) ?
                             (mem_address_overflow - `GRAPHICS_CONFIG_ADDRESS) : mem_address_overflow);

    Word_t gmem_data_out_a, gmem_data_in_a;
    GraphicsMemoryAddress_t gmem_address_a;

    ByteMask_t gmem_write_mask_a;

    function Word_t bit_expand(input Nibble_t bits);
        // bits is MSB first
        bit_expand[0 +: 8] = {8{bits[3]}};
        bit_expand[8 +: 8] = {8{bits[2]}};
        bit_expand[16 +: 8] = {8{bits[1]}};
        bit_expand[24 +: 8] = {8{bits[0]}};
    endfunction

    genvar i;

    generate
        for (i = 0; i < 4; i++) begin
            // port a for read/write request from bus
            // port b for read request from vga
            blk_mem_graphics blk_mem_graphics_instance (
                .clka(bus_clk_2x),
                .wea(gmem_write_mask_a[i]),
                .addra(gmem_address_a),
                .dina(gmem_data_in_a[8 * i +: 8]),
                .douta(gmem_data_out_a[8 * i +: 8]),
                .clkb(vga_clk),
                .web(`ZERO_BIT),
                .addrb(mem_address),
                .dinb(`ZERO_BYTE),
                .doutb(mem_data[8 * i +: 8])
            );
        end
    endgenerate

    logic dbus_last_stall;

    always_ff @(posedge bus_clk_2x or posedge rst) begin
        if (rst) begin
            gmem_data_in_a <= `ZERO_WORD;
            gmem_write_mask_a <= `BYTE_MASK_NONE;
            gmem_address_a <= GraphicsMemoryAddress_t'(`ZERO_WORD);
            data_bus.data_rd <= `ZERO_WORD;
            dbus_last_stall <= 1'b0;
            data_bus.stall <= 1'b0;
        end else begin

            dbus_last_stall <= data_bus.stall;

            if (bus_clk == ~`BUS_CLK_POSEDGE) begin // falling edge of main clock
                data_bus.stall <= 1'b0;
                gmem_data_in_a <= `ZERO_WORD;
                gmem_write_mask_a <= `BYTE_MASK_NONE;
                data_bus.data_rd <= `ZERO_WORD;
                if (data_bus.write) begin
                    gmem_address_a <= GraphicsMemoryAddress_t'(data_bus.address);
                    if (data_bus.mask == `BYTE_MASK_HALF_LO || data_bus.mask == `BYTE_MASK_HALF_HI) begin
                        // hardware extension
                        gmem_write_mask_a <= `BYTE_MASK_FULL;
                        gmem_data_in_a <= bit_expand(Nibble_t'(data_bus.data_wr));
                    end else begin
                        // normal write
                        gmem_write_mask_a <= data_bus.mask;
                        gmem_data_in_a <= data_bus.data_wr;
                    end
                end else if (data_bus.read) begin

                    end
                end
            end
        end
    end

    always_ff @(posedge bus_clk or posedge rst) begin
        if (rst) begin
            pixel_offset_reg_i[0] <= `ZERO_WORD;
            pixel_offset_reg_o[0] <= `ZERO_WORD;
            pixel_offset_reg_o[1] <= `ZERO_WORD;
        end else begin
            pixel_offset_reg_o[0] <= pixel_offset;
            pixel_offset_reg_o[1] <= pixel_offset_reg_o[0];
            if (data_bus.write && data_bus.address == `GRAPHICS_CONFIG_ADDRESS) begin
                pixel_offset_reg_i[0] <= data_bus.data_wr;
            end
        end
    end

    // cross clock domain synchronization
    always_ff @(posedge vga_clk or posedge rst) begin
        if (rst) begin
            pixel_offset_reg_i[1] <= `ZERO_WORD;
            pixel_offset <= `ZERO_WORD;
        end else begin
            pixel_offset_reg_i[1] <= pixel_offset_reg_i[0];
            pixel_offset <= pixel_offset_reg_i[1];
        end
    end

    // generate VGA signal
    localparam HOR_PXL    = 800;
    localparam VER_PXL    = 600;
    localparam TOL_PXL    = (HOR_PXL * VER_PXL);
    localparam HSYNC_POL  = 1; //positive
    localparam VSYNC_POL  = 1; //positive
    localparam HBACK_POCH = 64;
    localparam HFRNT_POCH = 56;
    localparam HSYNC_TIME = 120;
    localparam VBACK_POCH = 23;
    localparam VFRNT_POCH = 37;
    localparam VSYNC_TIME = 6;
    localparam HTOL_TIME  = (HBACK_POCH + HFRNT_POCH + HSYNC_TIME + HOR_PXL);
    localparam VTOL_TIME  = (VBACK_POCH + VFRNT_POCH + VSYNC_TIME + VER_PXL);
    localparam HPXL_BEGIN = (HSYNC_TIME + HBACK_POCH);
    localparam VPXL_BEGIN = (VSYNC_TIME + VBACK_POCH);

    Word_t now_data;
    Word_t x, y;
    Word_t visible_x, visible_y;

    wire [1:0] pixel_count_mod_4 = pixel_count[1:0];

    // VgaColorNumber_t color_number;
    // color_mapper color_mapper_instance(
    //     .color_number,
    //     .color
    // );

    VgaColor_t color;
    // every pixel takes 8 bits, little endian
    assign color = now_data[pixel_count_mod_4 * $bits(VgaColor_t) +: $bits(VgaColor_t)];

    assign {vga.red, vga.green, vga.blue} = color;

    // latch data
    always_ff @(posedge vga_clk or posedge rst) begin
        if (rst) begin
            now_data <= `ZERO_WORD;
        end else begin
            if (pixel_count_mod_4 == 2'd3) begin
                now_data <= mem_data;
            end
        end
    end

    // pixel coordinate
    always_ff @(posedge vga_clk or posedge rst) begin
        if (rst) begin
            x <= `ZERO_WORD;
            y <= `ZERO_WORD;
        end else begin
            if (x == HTOL_TIME - 1) begin
                x <= 0;
                if (y == VTOL_TIME - 1) begin
                    y <= 0;
                end else begin
                    y <= y + 32'b1;
                end
            end else begin
                x <= x + 32'b1;
            end
        end
    end

    // horizontal and vertical synchronization signal
    always_ff @(posedge vga_clk or posedge rst) begin
        if (rst) begin
            vga.hsync <= HSYNC_POL;
            vga.vsync <= HSYNC_POL;
        end else begin
            if (x == HTOL_TIME - 1) begin
                vga.hsync <= HSYNC_POL;
            end else if (x == HSYNC_TIME - 1) begin
                vga.hsync <= ~HSYNC_POL;
            end
            if (y == VTOL_TIME - 1 && x == HTOL_TIME - 1) begin
                vga.vsync <= VSYNC_POL;
            end else if (y == VSYNC_TIME - 1 && x == HTOL_TIME - 1) begin
                vga.vsync <= ~VSYNC_POL;
            end
        end
    end

    assign visible_x = x >= HPXL_BEGIN ? (x - HPXL_BEGIN < HOR_PXL ? x - HPXL_BEGIN : 0) : 0;
    assign visible_y = y >= VPXL_BEGIN ? (y - VPXL_BEGIN < VER_PXL ? y - VPXL_BEGIN : 0) : 0;

    assign vga.de = (x >= HPXL_BEGIN) && (x - HPXL_BEGIN < HOR_PXL) && (y >= VPXL_BEGIN) && (y - VPXL_BEGIN < VER_PXL);

    always_ff @(posedge vga_clk or posedge rst) begin
        if (rst) begin
            pixel_count <= `ZERO_WORD;
        end else begin
            if (vga.de) begin
                if (pixel_count == TOL_PXL - 1) begin
                    pixel_count <= 0;
                end else begin
                    pixel_count <= pixel_count + 1;
                end
            end
        end
    end

endmodule

                    if (dbus_last_stall) begin
                        data_bus.data_rd <= gmem_data_out_a;
                    end else begin
                        if (data_bus.address == `GRAPHICS_CONFIG_ADDRESS) begin
                            data_bus.data_rd <= pixel_offset_reg_o[1];


//////////////////////////////////////////////////////////////////////
////                                                              ////
//// WISHBONE SD Card Controller IP Core                          ////
////                                                              ////
//// sd_data_serial_host.v                                        ////
////                                                              ////
//// This file is part of the WISHBONE SD Card                    ////
//// Controller IP Core project                                   ////
//// http://opencores.org/project,sd_card_controller              ////
////                                                              ////
//// Description                                                  ////
//// Module resposible for sending and receiving data through     ////
//// 4-bit sd card data interface                                 ////
////                                                              ////
//// Author(s):                                                   ////
////     - Marek Czerski, ma.czerski@gmail.com                    ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
////                                                              ////
//// Copyright (C) 2013 Authors                                   ////
////                                                              ////
//// Based on original work by                                    ////
////     Adam Edvardsson (adam.edvardsson@orsoc.se)               ////
////                                                              ////
////     Copyright (C) 2009 Authors                               ////
////                                                              ////
//// This source file may be used and distributed without         ////
//// restriction provided that this copyright statement is not    ////
//// removed from the file and that any derivative work contains  ////
//// the original copyright notice and the associated disclaimer. ////
////                                                              ////
//// This source file is free software; you can redistribute it   ////
//// and/or modify it under the terms of the GNU Lesser General   ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any   ////
//// later version.                                               ////
////                                                              ////
//// This source is distributed in the hope that it will be       ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied   ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      ////
//// PURPOSE. See the GNU Lesser General Public License for more  ////
//// details.                                                     ////
////                                                              ////
//// You should have received a copy of the GNU Lesser General    ////
//// Public License along with this source; if not, download it   ////
//// from http://www.opencores.org/lgpl.shtml                     ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
`include "sd_defines.h"

module sd_data_serial_host(
           input 			 sd_clk,
           input 			 rst,
           //Tx Fifo
           input [31:0] 		 data_in,
           output reg 			 rd,
           //Rx Fifo
           output reg [31:0] 		 data_out,
           output reg 			 we,
           //tristate data
           output reg 			 DAT_oe_o,
           output reg [3:0] 		 DAT_dat_o,
           input [3:0] 			 DAT_dat_i,
           //Control signals
           input [`BLKSIZE_W-1:0] 	 blksize,
           input 			 bus_4bit,
           input [`BLKCNT_W-1:0] 	 blkcnt,
           input [1:0] 			 start,
           input [1:0] 			 byte_alignment,
	   input [31:0] 		 timeout_i,
           output 			 sd_data_busy,
           output 			 busy,
           output			 crc_ok,
           output reg 			 finish_o,
	   output reg [31:0] 		 wait_reg_o,
	   output reg [`BLKSIZE_W-1+4:0] transf_cnt_o
       );

reg [4:0] crc_s;
reg [3:0] crc_lane_ok;
reg [15:0]         crc_din[3:0];
wire [15:0]        crc_calc[3:0];
reg [31:0]                                        data_out0;
reg                                               we0;
reg [3:0] DAT_dat_reg;
reg [`BLKSIZE_W-1+4:0] data_cycles;
reg bus_4bit_reg;
//CRC16
   reg [4:0]                                         crc_in;
reg crc_rst;
parameter SIZE = 7;
reg [SIZE-1:0] state;
reg [SIZE-1:0] next_state;
parameter IDLE       = 7'b0000001;
parameter WRITE_DAT  = 7'b0000010;
parameter WRITE_CRC  = 7'b0000100;
parameter WRITE_BUSY = 7'b0001000;
parameter READ_WAIT  = 7'b0010000;
parameter READ_DAT   = 7'b0100000;
parameter FINISH     = 7'b1000000;
reg [2:0] crc_status;
reg busy_int;
reg [`BLKSIZE_W-1:0] blksize_reg;
reg [4:0] crc_c;
   reg [3:0]                                         crnt_din;
reg [4:0] data_index;

   integer                                           k;
genvar i;
generate
    for(i=0; i<4; i=i+1) begin: CRC_16_gen
         sd_crc_16 CRC_16_i (crc_in[i], crc_in[4], ~sd_clk, crc_rst, crc_calc[i]);
    end
endgenerate

assign busy = (state != IDLE) && (state != FINISH);
assign sd_data_busy = !DAT_dat_reg[0];
assign crc_ok = &crc_lane_ok;

always @(posedge sd_clk or posedge rst)
begin: FSM_OUT
    if (rst) begin
        state <= IDLE;
        DAT_oe_o <= 0;
        crc_in <= 0;
        crc_rst <= 1;
        transf_cnt_o <= 0;
        crc_c <= 15;
        rd <= 0;
        crc_c <= 0;
        DAT_dat_o <= 0;
        crc_status <= 0;
        crc_lane_ok <= 0;
        crc_s <= 0;
        we0 <= 0;
        we <= 0;
        data_out0 <= 0;
        busy_int <= 0;
        data_index <= 0;
        data_cycles <= 0;
        bus_4bit_reg <= 0;
        wait_reg_o <= 0;
        finish_o <= 0;
           DAT_dat_reg <= 0;
           data_out <= 0;
           transf_cnt_o <= 0;
    end
    else begin
           // sd data input pad register
           DAT_dat_reg <= DAT_dat_i;
           crnt_din = 4'hf;
           if (we0) data_out <= data_out0;
           we <= we0;
        case(state)
            IDLE: begin
                for (k = 0; k < 4; k=k+1)
                  crc_din[k] <= 0;
                DAT_oe_o <= 0;
                DAT_dat_o <= 4'b1111;
                crc_in <= 0;
                crc_rst <= 1;
                transf_cnt_o <= 0;
                crc_c <= 16;
                crc_status <= 0;
                crc_lane_ok <= 0;
                crc_s <= 0;
                we0 <= 0;
                rd <= 0;
                data_index <= 0;
                blksize_reg <= blksize;
                data_cycles <= (bus_4bit ? {2'b0,blksize,1'b0} + 'd2 : {blksize,3'b0} + 'd8);
                bus_4bit_reg <= bus_4bit;
	        wait_reg_o <= 0;
	        finish_o <= 0;
                data_out <= 0;
                if (start == 2'b01)
                  state <= WRITE_DAT;
                else if (start == 2'b10)
                  state <= READ_WAIT;
            end
            WRITE_DAT: begin
                transf_cnt_o <= transf_cnt_o + 16'h1;
                rd <= 0;
                //special case
                if (transf_cnt_o == 0) begin
                    crc_rst <= 0;
                   data_index <= 0;
                   crnt_din = bus_4bit_reg ? 4'h0 : 4'he;
                   rd <= 1;
                    DAT_oe_o <= 1;
                   DAT_dat_o <= crnt_din;
                end
                else if (transf_cnt_o < data_cycles+16) begin /* send the write data */
                    if (bus_4bit_reg) begin
                      crnt_din = {
                            data_in[31-({data_index[2:0],2'b00})],
                            data_in[30-({data_index[2:0],2'b00})],
                            data_in[29-({data_index[2:0],2'b00})],
                            data_in[28-({data_index[2:0],2'b00})]
                            };
                      if (data_index[2:0] == 3'h7 && transf_cnt_o < data_cycles-2) begin
                         begin
                            rd <= 1;
                         end
                        end
                    end
                    else begin
                      crnt_din = {3'h7, data_in[31-data_index]};
                        if (data_index == 29/*not 31 - read delay !!!*/) begin
                         begin
                            rd <= 1;
                         end
                        end
                    end
                    data_index <= data_index + 5'h1;
                   if (transf_cnt_o < data_cycles-1)
                     begin
                        crc_in <= {1'b1,crnt_din};
                        DAT_dat_o <= crnt_din;
                end
                   else if (crc_c!=0) begin /* send the CRC */
                      crc_in <= 0;
                    crc_c <= crc_c - 5'h1;
                    DAT_oe_o <= 1;
                      DAT_dat_o[0] <= crc_calc[0][crc_c-1];
                    if (bus_4bit_reg)
                        DAT_dat_o[3:1] <= {crc_calc[3][crc_c-1], crc_calc[2][crc_c-1], crc_calc[1][crc_c-1]};
                    else
                        DAT_dat_o[3:1] <= {3'h7};
                end
                   else /* send the stop bit */
                     begin
                        crc_in <= 0;
                    DAT_oe_o <= 1;
                    DAT_dat_o <= 4'hf;
                end
                end
                else begin /* wait for write ack */
                    DAT_oe_o <= 0;
                    crc_s[4] <= DAT_dat_reg[0];
                    if (!DAT_dat_reg[0])
                        state <= WRITE_CRC;
                end
            end
             WRITE_CRC: begin /* get write ack */
                DAT_oe_o <= 0;
                crc_status <= crc_status + 3'h1;
                crc_s[3-crc_status[1:0]] <= DAT_dat_reg[0];
                busy_int <= 1;
                if (crc_status == 3)
                  state <= WRITE_BUSY;
            end
             WRITE_BUSY: begin /* wait for write completion */
                busy_int <= !DAT_dat_reg[0];
                if (!busy_int)
                  state <= FINISH;
                end
             READ_WAIT: begin /* wait for a start bit in read mode */
                DAT_oe_o <= 0;
                crc_rst <= 0;
                crc_in <= 0;
                crc_c <= 15;// end
                transf_cnt_o <= 0;
                data_index <= 0;
	        wait_reg_o <= wait_reg_o + 1;
                if ((wait_reg_o >= 3) && !DAT_dat_reg[0]) begin // allow time for bus to change direction
                   state <= READ_DAT;
            end
                else if (wait_reg_o >= timeout_i) begin // prevent hang if card did not respond
                   state <= FINISH;
                end
             end
             READ_DAT: begin /* read the data and calculate CRC */
                we0 <= 0;
                if (transf_cnt_o < data_cycles-2) begin
                    if (bus_4bit_reg) begin
                      if (&data_index[2:0])
                        begin
                           we0 <= 1;
                        end;
                        data_out0[31-({data_index[2:0],2'b00})] <= DAT_dat_reg[3];
                        data_out0[30-({data_index[2:0],2'b00})] <= DAT_dat_reg[2];
                        data_out0[29-({data_index[2:0],2'b00})] <= DAT_dat_reg[1];
                        data_out0[28-({data_index[2:0],2'b00})] <= DAT_dat_reg[0];
                    end
                    else begin
                      if (&data_index)
                        begin
                           we0 <= 1;
                        end;
                        data_out0[31-data_index] <= DAT_dat_reg[0];
                    end
                    data_index <= data_index + 5'h1;
                   crc_in <= {1'b1,DAT_dat_reg};

                end
                else if (crc_c != 5'h1f) begin
                   for (k = 0; k < 4; k=k+1)
                     begin
                        crc_din[k][crc_c[3:0]] <= DAT_dat_reg[k];
                     end
<TARGET>
                   crc_in <= 0;
                    we0 <=0;
                        crc_c <= crc_c - 5'h1;
                        end
                else
                  begin
                     for (k = 0; k < 4; k=k+1)
                       crc_lane_ok[k] <= crc_calc[k] == crc_din[k];
                     state <= FINISH;
                end
            end // case: READ_DAT
	  FINISH:
               begin
	    finish_o <= 1;
                  if (start == 2'b00)
                    state <= IDLE;
               end
             default:
               state <= IDLE;
           endcase; // case (state)
           //abort
           if (start == 2'b11)
             state <= IDLE;
    end
end

endmodule


`timescale 1ns / 1ps
`include "../defines/defines.svh"
module Predictor(
    input clk,
    input rst,
    input pause,
    input hold,
    // Recover must be delayed for 1 cycle in case of flushing the newly committed history
    input recover,
    input [31:0] br_PC,
    input IF3_isBR,
    input IF3_isJ,
    output reg pred_valid,
    output PredInfo pred_info,
    output reg pred_taken,
    output reg [31:0] pred_target,
    input commit_valid,
    input PredInfo committed_pred_info,
    input committed_branch_taken,
    input committed_mispred,
    input commit_update_target_en,
    input [31:0] committed_target
    // For RAS
    // input IF3_isLink,
    // input IF3_isReturn,
    // input [31:0] IF3_LinkPC,
    // output reg [31:0] IF3_ReturnPC,
    // input committed_isLink,
    // input committed_isReturn,
    // input commmit_valid,
    // input [31:0] commit_LinkPC
);
    reg         commit_valid_r;
    PredInfo    committed_pred_info_r;
    reg         committed_branch_taken_r;
    reg         committed_mispred_r;
    reg         commit_update_target_en_r;
    reg[31:0]   committed_target_r;

    always @(posedge clk)  begin
        if(rst) begin
            commit_valid_r                  <= 0;
            committed_pred_info_r           <= 0;
            committed_branch_taken_r        <= 0;
            committed_mispred_r             <= 0;
            commit_update_target_en_r       <= 0;
            committed_target_r              <= 0;
        end else begin
            commit_valid_r                  <= commit_valid           ;
            committed_pred_info_r           <= committed_pred_info    ;
            committed_branch_taken_r        <= committed_branch_taken ;
            committed_mispred_r             <= committed_mispred      ;
            commit_update_target_en_r       <= commit_update_target_en;
            committed_target_r              <= committed_target       ;
        end
    end

    GlobalHistPred pred_info_global_o;
    LocalHistPred pred_info_local_o;
    BHREntry bhr;
    GlobalHist ghist;
    wire pred_taken_global, pred_taken_local;
    CPHTIndex CPHT_index, CPHT_index_update;
    CPHT_Entry [1023:0] CPHT;
    wire [31:0] br_PC_dly;
    LocalHistPred committed_pred_info_local;
    assign committed_pred_info_local.bht_index = committed_pred_info_r.bht_index;
    assign committed_pred_info_local.pht_index = committed_pred_info_r.pht_index;
    wire pred_direction_valid, pred_address_valid;
    LocalHistPredictor localhist(
        .clk(clk),
        .rst(rst),
        .pause(pause),
        .br_PC(br_PC),
        .pred_info(pred_info_local_o),
        .bhr(bhr),
        .pred_taken(pred_taken_local),
        .commit_update(commit_valid_r),
        .committed_pred_info(committed_pred_info_local),
        .committed_taken(committed_branch_taken_r),
        .br_PC_out(br_PC_dly)
    );
    wire [31:0] pred_target_o;
    wire [9:0] btb_index_o;
    BTB btb(
        .clk(clk),
        .rst(rst),
        .br_PC(br_PC_dly),
        .pred_valid(pred_address_valid),
        .pred_target(pred_target_o),
        .pred_btb_index(btb_index_o),
        .BHR(bhr),
        .commit_update_en(commit_valid_r && commit_update_target_en_r), // Only update for inderect branches
        .commit_update_index(committed_pred_info_r.btb_index),
        .commit_update_target(committed_target_r)
    );
    // wire [31:0] IF3_ReturnPC_o;
    // RAS ras(
    //     .clk(clk),
    //     .rst(rst),
    //     .pause(pause),
    //     .recover(recover),
    //     .isLink(IF3_isLink),
    //     .isReturn(IF3_isReturn),
    //     .target(IF3_ReturnPC_o),
    //     .commit_valid_r(commit_valid_r),
    //     .committed_isLink(committed_isLink),
    //     .committed_isReturn(committed_isReturn),
    //     .committed_pc(commit_LinkPC)
    // );
    GlobalHistPred committed_pred_info_global;
    assign committed_pred_info_global.pht_index_g = committed_pred_info_r.pht_index_g;
    GlobalHistPredictor globalhist(
        .clk(clk),
        .rst(rst),
        .pause(pause),
        .pred_valid(pred_direction_valid),
        .br_PC(br_PC),
        .recover(recover),
        .pred_info(pred_info_global_o),
        .ghist(ghist),
        .pred_taken(pred_taken_global),
        // For updating global histroy
        .pred_valid_local( IF3_isBR || IF3_isJ ),
        .pred_taken_local( pred_taken || IF3_isJ ),
        .commit_update(commit_valid_r),
        .committed_pred_info(committed_pred_info_global),
        .committed_taken(committed_branch_taken_r)
    );

    // Stage 2: Take the output and select the working predictor
    // Remove the useless CPHT
    // assign CPHT_index_update = committed_pred_info.cpht_index ;
    // assign CPHT_index= ghist[19:10] ^ ghist[9:0] ^ br_PC_dly[12:3];

    // always @(posedge clk)   begin
    //     if(rst) begin
    //         for(integer i = 0; i < 1024; i++)   begin
    //             CPHT[i] <= 0;
    //         end
    //     end else if(commit_valid_r) begin
    //         // TODO
    //         if(committed_pred_info.use_global && committed_mispred)   begin
    //             CPHT[CPHT_index_update] <= CPHT[CPHT_index_update] == 2'b00 ? 2'b00 : CPHT[CPHT_index_update] - 2'b01;
    //         end else begin
    //             CPHT[CPHT_index_update] <= CPHT[CPHT_index_update] == 2'b11 ? 2'b11 : CPHT[CPHT_index_update] + 2'b01;
    //         end
    //     end
    // end

    // Output is registered
    wire pred_taken_o, use_global_o;
    // assign pred_taken_o = CPHT[CPHT_index] == 1 ? pred_taken_global : pred_taken_local;
    // assign use_global_o = CPHT[CPHT_index] == 1;
    assign pred_taken_o = pred_taken_global;

    always @(posedge clk)   begin
        if(rst) begin
            pred_info <= 0;
            pred_taken <= 0;
            pred_target <= 0;
            pred_valid <= 0;
            // IF3_ReturnPC <= 0;
        end else if(~hold) begin
            pred_info <= pred_info_o;
            pred_taken <= pred_taken_o;
            pred_target <= pred_target_o;
            pred_valid <= pred_address_valid | pred_direction_valid;
            // IF3_ReturnPC <= IF3_ReturnPC_o;
        end
    end

endmodule

    assign use_global_o = 1;
    // Output logic
    PredInfo pred_info_o;
    assign pred_info_o.bht_index = pred_info_local_o.bht_index;
    assign pred_info_o.pht_index = pred_info_local_o.pht_index;


/*
Copyright (c) 2011, Sachin Gandhi
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// ----------------------------------------------------------------------
//  Example test to enc/dec pkt
// ----------------------------------------------------------------------

program gcm_test (); // {

  // include file
  `include "gcm_dpi.sv"

  // Local Variables
  bit [7:0]   pkt  [];
  bit [7:0]   epkt [];
  bit [7:0]   dpkt [];
  bit [7:0]   npkt [];
  bit [31:0]  pn;
  bit [63:0]  sci;
  bit [127:0] key;
  bit         pkt_err;
  bit         auth_err;
  int         plen;
  int         out_plen;

  int         auth_st;
  int         auth_sz;
  int         enc;
  int         enc_sz;
  int         crc_sz;
  int         icv_sz;
  int         i;

  initial
  begin // {
    // setting values
    plen      = 64;                       // pkt_len
    pkt       = new[plen];                // pkt to encrypt
    epkt      = new[plen + 16];           // Output pkt after enc
                                          // (encrypted pkt + 16B auth tag)
    key       = 128'habcdabcdabcdfffffff; // key from table lookup
    sci       = 64'ha5a5a5a5a5a5a5a5;     // sci from table lookup
    pn        = 32'hdeadbeef;             // pn from table lookup
    auth_only = 0;                        // 0 => Encrypt and auth, 1 -> only auth no encryption
    auth_st   = 0;                        // Authentication start
    auth_sz   = 20;                       // Total bytes to auth_only
                                          // For eg. DA + SA + MAcsecHdr
                                          // This part of the pkt is never encrypted
    enc       = 1;                        // 1 -> Encrypt, 0 -> decrypt the pkt
    enc_sz    = plen - auth_sz - auth_st; // total bytes to Encrypt or decrypt.
                                          // Encryption starts after auth_sz
    for (i = 0; i < plen; i++)
        pkt[i] = i[7:0];

    // DPI call to encrypt the pkt
    // This call will output epkt which is encrypted data + 16B of auth_tag
    // Auth tag is always last 16B
    gcm_crypt (key,
               sci,
               pn,
               auth_only,
               auth_st,
               auth_sz,
               enc,
               enc_sz,
               pkt,
               epkt,
               out_plen);

    // display
    $write ("gcm_test:        ~~~~~~~~~~~~~ Pkt before Encryption ~~~~~~~~~~~~~~\n");
    dump(pkt);
    $write ("gcm_test:        ~~~~~~~~~~~~~ Pkt after Encryption ~~~~~~~~~~~~~~\n");
    dump(epkt);

    // remove auth tag from epkt
    npkt = new[plen];
    for (i = 0; i < plen; i++)
        npkt[i] = epkt[i];

    // DPI call to decrypt the pkt
    // This call will output dpkt which is decrypted data + 16B of auth_tag
    // Auth tag is always last 16B
    // decrypt data should match with original pkt
    // Last 16B which is auth tag should match with auth tag of encrypted pkt
    enc = 0;
    dpkt      = new[plen + 16];
    gcm_crypt (key,
               sci,
               pn,
               auth_only,
               auth_st,
               auth_sz,
               enc,
               enc_sz,
               npkt,
               dpkt,
               out_plen);
    $write ("gcm_test:        ~~~~~~~~~~~~~ Pkt after decryption ~~~~~~~~~~~~~~\n");
    dump(dpkt);

    // Pkt and auth tag Comparison
    pkt_err  = 1'b0;
    auth_err = 1'b0;
    for (i = 0; i < plen ; i++)
    begin // {
        if (pkt[i] !== dpkt[i])
            pkt_err = 1'b1;
    end // }
    if (pkt_err)
         $display("%0t : ERROR  : GCM_TEST  :  Pkt Mismatch", $time);

    for (i = plen; i < (plen + 16) ; i++)
    begin // {
        if (epkt[i] !== dpkt[i])
            auth_err = 1'b1;
    end // }
    if (auth_err)
        $display("%0t : ERROR  : GCM_TEST  :  Auth Fail", $time);

    if (pkt_err | auth_err)
        $display("%0t : ~~~~~~~  Test FAIL :(- ~~~~~~~",$time);
    else
        $display("%0t : ~~~~~~~  Test PASS :)- ~~~~~~~",$time);
  end // }

  // This task display entire pkt
  task dump (bit [7:0] data []); // {
    int pkt_len = 0;
    int i;
    pkt_len = data.size ();
    for (i = 0; i < 16 ; i++)
    begin // {
        if (i % 16 == 0)
            $write ("gcm_test:       %2d ", i);
        else if (i % 16 == 7)
            $write ("%3d |", i);
        else if (i % 16 == 15)
            $write ("%3d\n", i);
        else
            $write ("%3d", i);
    end // }
    $write ("gcm_test:        ~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~\n");
    for (i = 0; i < pkt_len; i++)
    begin
        if (i % 16 == 0)
            $write ("gcm_test: %4d : ", i);
        $write ("%x ", data[i]);
        if (i % 16 == 7)
            $write ("| ");
        if (i % 16 == 15)
            $write ("\n");
    end
    $write ("\n\n");
  endtask : dump // }

endprogram : gcm_test // }


// 24C01, 24C02 EEPROM support
// by GreyRogue for NES MiSTer

module EEPROM_24C0x
(
	// Replace type_24C01 with EEPROM size and command size (Test State y/n and address bytes)?
	input             type_24C01,          // 24C01 is 128 bytes/no Test State?, 24C02 is 256 bytes/Test State
	input      [ 3:0] page_mask,           // Typically 0x3, 0x7, or 0xF; semi-correlated with size
	input             no_test_state,       // No control word (ID check/upper address bits); Usually only for X24C01
	input             address_write_only,  // Some models only set address in write mode (24C0xA?)
	input             clk,
	input             ce,
	input             reset,
	input             SCL,                 // Serial Clock
	input             SDA_in,              // Serial Data (same pin as below, split for convenience)
	output reg        SDA_out,             // Serial Data (same pin as above, split for convenience)
	input      [ 2:0] E_id,                // Chip Enable ID
	input             WC_n,                // ~Write Control
	input      [ 7:0] data_from_ram,       // Data read from RAM
	output     [ 7:0] data_to_ram,         // Data written to RAM
	output     [ 7:0] ram_addr,            // RAM Address
	output reg        ram_read,            // RAM read
	output reg        ram_write,           // RAM write
	input             ram_done,            // RAM access done
	// savestates
	input      [63:0] SaveStateBus_Din,
	input      [ 9:0] SaveStateBus_Adr,
	input             SaveStateBus_wren,
	input             SaveStateBus_rst,
	input             SaveStateBus_load,
	output     [63:0] SaveStateBus_Dout
);

mystate state;

reg [9:0] command;
reg [7:0] address;
reg [8:0] data;  // 8 bits data, plus ack bit
reg last_SCL;
reg last_SDA;
reg read_next;
// Some X24C0x documents show it working with a test state, instead of combined address/read/write.
// wire no_test_state = type_24C01;

always @(posedge clk) if (reset) begin
	state <= STATE_STANDBY;
	command <= 0;
	last_SCL <= 0;
	last_SDA <= 0;
	SDA_out <= 1;  // NoAck
	ram_read <= 0;
	ram_write <= 0;
	read_next <= 0;
end else if (SaveStateBus_load) begin
	case (SS_MAP1[2:0])
		0: state <= STATE_STANDBY;
		1: state <= STATE_TEST;
		2: state <= STATE_ADDRESS;
		3: state <= STATE_WRITE;
		4: state <= STATE_READ;
	endcase
	command   <= SS_MAP1[12: 3];
	last_SCL  <= SS_MAP1[   13];
	last_SDA  <= SS_MAP1[   14];
	SDA_out   <= SS_MAP1[   15];
	ram_read  <= SS_MAP1[   16];
	ram_write <= SS_MAP1[   17];
	data      <= SS_MAP1[26:18];
	address   <= SS_MAP1[34:27];
	read_next <= SS_MAP1[   35];
end else if (ce) begin
	last_SCL <= SCL;
	last_SDA <= SDA_in;
	if (ram_write && ram_done) begin
		ram_write <= 0;
		// address[3:0] <= (address[3:0] & ~page_mask) | (page_mask & (address[3:0] + 4'h1)); //Increment wraps at 4/8/16 byte boundary
		// Alternate for line above:
		address[3:0] <= address[3:0] + 4'h1;  // Wrap at 4/8/16 byte boundary
		if (!page_mask[2]) begin
			address[2] <= address[2];  // Put back; Increment wraps at 4 byte boundary   : mask = 0x3
		end
		if (!page_mask[3]) begin
			address[3] <= address[3];  // Put back; Increment wraps at 4/8 byte boundary : mask = 0x3 or 0x7
		end
	end
	if (ram_read && ram_done) begin
		ram_read <= 0;
		data <= {data_from_ram, 1'b1};  // NoAck at end
	end
	if (SCL && last_SCL && !SDA_in && last_SDA) begin
		if (no_test_state) state <= STATE_ADDRESS;
		else state <= STATE_TEST;
		command <= 10'd2;
	end else if (SCL && last_SCL && SDA_in && !last_SDA) begin
		state   <= STATE_STANDBY;
		command <= 10'd0;
	end else if (state == STATE_STANDBY) begin
		// Do nothing
	end else if (SCL && !last_SCL) begin
		command <= {command[8:0], SDA_in};
	end else if (!SCL && last_SCL) begin
		SDA_out <= 1;  //NoAck
		if (state == STATE_READ) begin
			SDA_out <= data[8];
			if (!ram_read) begin
				data[8:1] <= data[7:0];
			end
		end
		if (command[9]) begin
			if (state == STATE_TEST) begin
				if (command[7:1] == {4'b1010, E_id}) begin
					if (command[0]) begin
						if (address_write_only) begin
							state <= STATE_READ;
							ram_read <= 1;
						end else begin
							state <= STATE_ADDRESS;
						end
						read_next <= 1;
					end else begin
						state <= STATE_ADDRESS;
						read_next <= 0;
					end
					SDA_out <= 0;  //Ack
				end
				command <= 10'd1;
			end else if (state == STATE_ADDRESS) begin
				if (type_24C01) begin
					address <= {1'b0, command[7:1]};
					if (command[0]) begin
						state <= STATE_READ;
						ram_read <= 1;
					end else begin
						state <= STATE_WRITE;
					end
				end else begin
					address <= command[7:0];
					if (read_next & !address_write_only) begin
						state <= STATE_READ;
						ram_read <= 1;
					end else begin
						state <= STATE_WRITE;
					end
				end
				SDA_out <= 0;  //Ack
				command <= 10'd1;
			end else if (state == STATE_WRITE) begin
				data <= {command[7:0], 1'b0};
				if (!WC_n) begin
					ram_write <= 1;
					SDA_out   <= 0;  //Ack
				end
				command <= 10'd1;
			end else if (state == STATE_READ) begin
				ram_read <= 1;
				SDA_out  <= 1;  //NoAck
				address  <= address + 8'b1;
				command  <= 10'd1;
			end
		end
	end
end

assign ram_addr = (type_24C01 == 1) ? {1'b0, address[6:0]} : address;
assign data_to_ram = data[8:1];

// savestate
assign SS_MAP1_BACK[ 2: 0] = (state == STATE_STANDBY)  ? 3'd0 :
							 (state == STATE_TEST)     ? 3'd1 :
							 (state == STATE_ADDRESS)  ? 3'd2 :
							 (state == STATE_WRITE)    ? 3'd3 :
														 3'd4;
assign SS_MAP1_BACK[12: 3] = command;
assign SS_MAP1_BACK[   13] = last_SCL;
assign SS_MAP1_BACK[   14] = last_SDA;
assign SS_MAP1_BACK[   15] = SDA_out;
assign SS_MAP1_BACK[   16] = ram_read;
assign SS_MAP1_BACK[   17] = ram_write;
assign SS_MAP1_BACK[26:18] = data;
assign SS_MAP1_BACK[34:27] = address;
assign SS_MAP1_BACK[   35] = read_next;
assign SS_MAP1_BACK[63:36] = 28'b0;  // free to be used

wire [63:0] SS_MAP1;
wire [63:0] SS_MAP1_BACK;
eReg_SavestateV #(SSREG_INDEX_L2MAP1, 64'h0000000000000000) iREG_SAVESTATE_MAP1 (
		clk,
		SaveStateBus_Din,
		SaveStateBus_Adr,
		SaveStateBus_wren,
		SaveStateBus_rst,
		SaveStateBus_Dout,
		SS_MAP1_BACK,
		SS_MAP1
);

endmodule

typedef enum bit [2:0] {
	STATE_STANDBY,
	STATE_TEST,
	STATE_ADDRESS,


// --------------------------------------------------------------------
// Copyright (c) 2005 by Terasic Technologies Inc.
// --------------------------------------------------------------------
//
// Permission:
//
//   Terasic grants permission to use and modify this code for use
//   in synthesis for all Terasic Development Boards and Altrea Development
//   Kits made by Terasic.  Other use of this code, including the selling
//   ,duplication, or modification of any portion is strictly prohibited.
//
// Disclaimer:
//
//   This VHDL or Verilog source code is intended as a design reference
//   which illustrates how these types of functions can be implemented.
//   It is the user's responsibility to verify their design for
//   consistency and functionality through the use of formal
//   verification methods.  Terasic provides no warranty regarding the use
//   or functionality of this code.
//
// --------------------------------------------------------------------
//
//                     Terasic Technologies Inc
//                     356 Fu-Shin E. Rd Sec. 1. JhuBei City,
//                     HsinChu County, Taiwan
//                     302
//
//                     web: http://www.terasic.com/
//                     email: support@terasic.com
//
// --------------------------------------------------------------------
//
// Major Functions:i2c controller
//
// --------------------------------------------------------------------
//
// Revision History :
// --------------------------------------------------------------------
//   Ver  :| Author            :| Mod. Date :| Changes Made:
//   V1.0 :| Joe Yang          :| 05/07/10  :|      Initial Revision
// --------------------------------------------------------------------
module I2C_Controller (
	CLOCK,
	I2C_SCLK,//I2C CLOCK
 	I2C_SDAT,//I2C DATA
	I2C_DATA,//DATA:[SLAVE_ADDR,SUB_ADDR,DATA]
	GO,      //GO transfor
	END,     //END transfor
	W_R,     //W_R
	ACK,      //ACK
	RESET,
	//TEST
	SD_COUNTER,
	SDO
);
	input  CLOCK;
	input  [23:0]I2C_DATA;
	input  GO;
	input  RESET;
	input  W_R;
 	inout  I2C_SDAT;
	output I2C_SCLK;
	output END;
	output ACK;

//TEST
	output [5:0] SD_COUNTER;
	output SDO;

reg SDO;
reg SCLK;
reg END;
reg [23:0]SD;
reg [5:0]SD_COUNTER;

wire I2C_SCLK=SCLK | ( ((SD_COUNTER >= 4) & (SD_COUNTER <=30))? ~CLOCK :1'b0 );
wire I2C_SDAT=SDO?1'bz:1'b0 ;

reg ACK1,ACK2,ACK3;
wire ACK=ACK1 | ACK2 |ACK3;

//--I2C COUNTER
always @(negedge RESET or posedge CLOCK ) begin
if (!RESET) SD_COUNTER=6'b111111;
else begin
if (GO==0)
	SD_COUNTER=0;
	else
	if (SD_COUNTER < 6'b111111) SD_COUNTER=SD_COUNTER+1'b1;
end
end
//----

always @(negedge RESET or  posedge CLOCK ) begin
if (!RESET) begin SCLK=1;SDO=1; ACK1=0;ACK2=0;ACK3=0; END=1; end
else
case (SD_COUNTER)
	6'd0  : begin ACK1=0 ;ACK2=0 ;ACK3=0 ; END=0; SDO=1; SCLK=1;end
	//start
	6'd1  : begin SD=I2C_DATA;SDO=0;end
	6'd2  : SCLK=0;
	//SLAVE ADDR
	6'd3  : SDO=SD[23];
	6'd4  : SDO=SD[22];
	6'd5  : SDO=SD[21];
	6'd6  : SDO=SD[20];
	6'd7  : SDO=SD[19];
	6'd8  : SDO=SD[18];
	6'd9  : SDO=SD[17];
	6'd10 : SDO=SD[16];
	6'd11 : SDO=1'b1;//ACK

	//SUB ADDR
	6'd12  : begin SDO=SD[15]; ACK1=I2C_SDAT; end
	6'd13  : SDO=SD[14];
	6'd14  : SDO=SD[13];
	6'd15  : SDO=SD[12];
	6'd16  : SDO=SD[11];

	//DATA
	6'd21  : begin SDO=SD[7]; ACK2=I2C_SDAT; end
	6'd22  : SDO=SD[6];
	6'd23  : SDO=SD[5];
	6'd24  : SDO=SD[4];
	6'd25  : SDO=SD[3];
	6'd26  : SDO=SD[2];
	6'd27  : SDO=SD[1];
	6'd28  : SDO=SD[0];
	6'd29  : SDO=1'b1;//ACK

	//stop
    6'd30 : begin SDO=1'b0;	SCLK=1'b0; ACK3=I2C_SDAT; end
    6'd31 : SCLK=1'b1;
    6'd32 : begin SDO=1'b1; END=1; end

endcase
end

endmodule


//testbench for positive edge
module pedge_tb();

logic clk;
logic i_sig;
wire o_pe;

//module instantiation
pedge dut (
	.clk(clk),
	.i_sig(i_sig),
	.o_pe(o_pe)
	);

// Dump waves
initial begin
    	$dumpfile("pedge.vcd");
    	$dumpvars(0, pedge_tb);
end

//clock generation and signal initialisation
initial begin
	i_sig = 1'b0;
	clk = 1'b0;
	forever #5 clk = ~clk;
end

//input signal
initial begin

	#20 i_sig = 1'b1;
<TARGET>
	#10 i_sig = 1'b1;
end

//end of simulation
initial begin
	#200 $finish();
end

endmodule


package accelerator_types;
`define BYTE 8

typedef enum logic {
	READ 				= 1'b0,
	WRITE 				= 1'b1
} rd_wr_e;

typedef enum logic [31:0] {
	I_IDLE				= 32'd0,
	I_R_MAT_A			= 32'd1,
	I_R_MAT_B			= 32'd2,
	I_R_MAT_C			= 32'd3,
	I_GEMM				= 32'd4,
	I_RESET				= 32'd5
} accel_instr_e;

typedef enum logic [31:0] {
	S_IDLE				= 32'd0,
	S_WAIT_ARW_READY	= 32'd1,
	S_WAIT_RW			= 32'd2,
	S_WAIT_COMPUTE		= 32'd3,
	S_SEND_MAT_C		= 32'd4,
	S_DONE				= 32'd5
} accel_state_e;

endpackage

interface accel_mig_itf #(
	parameter ADDR_WIDTH = 32,
	parameter DATA_WIDTH = 512
)();
	logic 	[ADDR_WIDTH-1:0] 		addr;
	logic							arvalid;	// address read valid
	logic 							awvalid;	// address write valid
	logic 	[7:0]					arwlen;		// burst length of read or write
	logic 							awready;	// mig ready to take address
	logic 							arready;	// mig ready to take address

	// w channel
	logic							wvalid;
	logic	[DATA_WIDTH-1:0]		wdata;
	logic	[DATA_WIDTH/`BYTE-1:0]	wstrb;
	logic							wlast;
	logic 							wready;

	// b channel
	logic 							bvalid;
	logic							bready;

	// mig outputs
	logic 	[DATA_WIDTH-1:0] 		data;
	logic 							data_valid;
	logic 							rw_last;
	logic 							arwready;	// mig ready to take address

	modport accel (
		input 	data,
		input 	data_valid,
		input 	rw_last,
		// input 	arwready,
		input	awready,
		input 	arready,

		output	addr,
		output	arvalid,

		output	arwlen,

		// w channel
		output	wvalid,
		output	wdata,
		output	wstrb,
		output	wlast,
		input	wready,

		// b channel
		input	bvalid,
		output 	bready
	);

	modport mig (
		input	addr,
		input	arvalid,
		input	awvalid,
		input	arwlen,

		// w channel
		input	wvalid,
		input	wdata,
		input	wstrb,
		input	wlast,
		output	wready,

		output 	data,
		output 	data_valid,
		output 	rw_last,
		// output 	arwready
		output	awready,
		output 	arready,

		// b channel
		output	bvalid,
		input 	bready
	);

endinterface //accel_mig_itf

interface bram_itf #(
	parameter ADDR_WIDTH = 9,
	parameter DIN_WIDTH	 = 64,
	parameter DOUT_WIDTH = 256
)();
	logic 	[ADDR_WIDTH-1:0]	addr;
	logic 	[DIN_WIDTH-1:0]		bram_din;
	logic 	[DOUT_WIDTH-1:0]	bram_dout;
	logic						we;

	modport ctlr (
		input 	bram_dout,
		output 	addr,
		output 	bram_din,
		output 	we
	);

	modport bram (
		input 	addr,
		input 	bram_din,
		input 	we,
		output 	bram_dout
	);

endinterface //bram_itf


// Copyright 2023 Barcelona Supercomputing Center (BSC)
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1

// Licensed under the Solderpad Hardware License v 2.1 (the “License”);
// you may not use this file except in compliance with the License, or,
// at your option, the Apache License version 2.0.
// You may obtain a copy of the License at

// https://solderpad.org/licenses/SHL-2.1/

// Unless required by applicable law or agreed to in writing, any work
// 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.

//
// Jordi Fornt <jfornt@bsc.es>

// ----------
// MACROS
// ----------

// --------------------
// MODULE DECLARATION
// --------------------

module bam_cell #(

)(
	// Data Inputs
    input   logic	i_a,
	input   logic 	i_b,
	input  	logic 	i_s,
	input  	logic 	i_cin,

	// Data Outputs
	output  logic 	o_s,
	output  logic 	o_cout
);

logic 			and_result;

assign and_result = i_a & i_b;

// Adder
assign sum_result = and_result + i_s + i_cin;

// Outputs
assign o_s = sum_result[0];
assign o_cout = sum_result[1];

endmodule