module FRAM(
  input i_clk, //Module (Module CLock = SPI Clock)
  input i_nreset,
  
  input logic [19:0] i_adr, //Memorycell adress in FRAM
  input logic [7:0] i_data, //data to write
  output logic [7:0] o_data, //data to read
  
  input logic i_rw, //Read = 1, Write = 0
  input logic i_status, //If 1 Read Staut register
  input logic i_hbn, //If 1 FRAM will enter Hibernation Mode
  input logic i_cready, //Starts transmission 
  output logic o_busy, //Indicates FRAM Busy
  
  // SPI Interface
  output o_SPI_Clk,
  input  i_SPI_MISO,
  output o_SPI_MOSI,
  output o_SPI_CS_n  

);
  
  
  //FRAM SPI OP Codes

  //Write Enable Control
  localparam WREN = 8'h06; //Set Write enable latch
  localparam WRDI = 8'h04; //Reset write enable latch
  //Register Access
  localparam RDSR = 8'h05; //Read Status Register
  localparam WRSR = 8'h01; //Write Status Register
  //Memory Write
  localparam WRITE = 8'h02; //Write Memory Data
  //Memory Read
  localparam READ = 8'h03; //Read Memory Data
  localparam FSTRT = 8'h0B; //Fast read memory Data
  //Special Sector Memory Access
  localparam SSWR = 8'h42; //Spcial Sector Write
  localparam SSRD = 8'h4B; //Special Sector Read
  //Identification and serial Number
  localparam RDID = 8'h9F; //Read Device ID
  localparam RUID = 8'h4C; //Read Unique ID
  localparam WRSN = 8'hC2; //Write Serial Number
  localparam RDSN = 8'hC3; //Read Serial Number
  //Low Power Modes
  localparam DPD = 8'hBA; // Enter Deep Power-Down
  localparam HBN = 8'hB9; // Enter Hibernate Mode
  //end FRAM SPI OP Codes
  
  //Controller Specific
  logic [3:0] state;
  
  
  // SPI Specific
  parameter SPI_MODE = 0;          // CPOL = 0, CPHA = 0
  parameter CLKS_PER_HALF_BIT = 2; // 25MHz
  parameter MAX_BYTES_PER_CS = 5;  // 5 bytes max per chip select cycle
  parameter CS_INACTIVE_CLKS = 1;  // Adds delay (1clk) between cycles
  
  
  logic [7:0] r_Master_TX_Byte = 0;
  logic r_Master_TX_DV = 1'b0;
  logic w_Master_TX_Ready;
  logic w_Master_RX_DV;
  logic [7:0] w_Master_RX_Byte;
  logic [$clog2(MAX_BYTES_PER_CS+1)-1:0] w_Master_RX_Count, r_Master_TX_Count = 3'h1; //Standard 1 Byte pro CS Cycle
 
  
      
  SPI_Master_With_Single_CS
  #(.SPI_MODE(SPI_MODE), //SPI Mode 0-3
    .CLKS_PER_HALF_BIT(CLKS_PER_HALF_BIT), //sets Frequency of SPI_CLK
    .MAX_BYTES_PER_CS(MAX_BYTES_PER_CS), //Maximum Bytes per CS Cycle
    .CS_INACTIVE_CLKS(CS_INACTIVE_CLKS) //Amount of Time holding CS Low befor next command
   ) SPI
  (
   // Control/Data Signals,
    .i_Rst_L(i_nreset),     // FPGA Reset
    .i_Clk(i_clk),         // FPGA Clock
   
   // TX (MOSI) Signals
   .i_TX_Count(r_Master_TX_Count),   // Number of bytes per CS
   .i_TX_Byte(r_Master_TX_Byte),     // Byte to transmit on MOSI
   .i_TX_DV(r_Master_TX_DV),         // Data Valid Pulse with i_TX_Byte
   .o_TX_Ready(w_Master_TX_Ready),   // Transmit Ready for Byte
   
   // RX (MISO) Signals
   .o_RX_Count(w_Master_RX_Count), // Index of RX'd byte
   .o_RX_DV(w_Master_RX_DV),       // Data Valid pulse (1 clock cycle)
   .o_RX_Byte(w_Master_RX_Byte),   // Byte received on MISO

   // SPI Interface
   .o_SPI_Clk(o_SPI_Clk),
   .i_SPI_MISO(i_SPI_MISO),
   .o_SPI_MOSI(o_SPI_MOSI),
   .o_SPI_CS_n(o_SPI_CS_n)
   );
  
  //end SPI Specific
  
  
  task SPI_SendByte(input [7:0] data);
    @(posedge i_clk);       
    r_Master_TX_Byte <= data;
    r_Master_TX_DV   <= 1'b1;
    @(posedge i_clk);
    r_Master_TX_DV <= 1'b0;
    @(posedge i_clk);
    @(posedge w_Master_TX_Ready);    
  endtask //end SPI_SendByte
  
  //FRAM Tasks
  task FRAM_Write(input [19:0] adr, input [7:0] data); //vgl. Fig.11
  	
    logic [7:0] value;
    value <= 8'h0;
    
    //Set Write Enable
    r_Master_TX_Count <= 3'b1; //1Byte Transaction
    SPI_SendByte(WREN);
    
    //Write to fram
    r_Master_TX_Count <= 3'h5; //5 Byte Transaction 
    SPI_SendByte(WRITE); //OPCode
    SPI_SendByte({4'hF,adr[19:16]}); //Adress [23-16]
    SPI_SendByte(adr[15:8]); //Adress [15-8]
    SPI_SendByte(adr[7:0]); //Adress [7-0]
    SPI_SendByte(data); //Data [7:0]
    
    //Reset Write Disable and Verify    
    do begin
      r_Master_TX_Count <= 3'b1; //1Byte Transaction 
      SPI_SendByte(WRDI); //Set Write Disable    
    
      FRAM_Read_Status(value); //Lese Status Register
    end while(((value & 8'h2) >> 1) != 0);
    
        
  endtask //end FRAM_Write
  
  task FRAM_Read(input [19:0] adr, output [7:0] data); //vgl. Fig12
    r_Master_TX_Count <= 3'h5; //5 Byte Transaction
    SPI_SendByte(READ); //Opcode
    SPI_SendByte({4'hF,adr[19:16]}); //Adress [23-16]
    SPI_SendByte(adr[15:8]); //Adress [15-8]
    SPI_SendByte(adr[7:0]); //Adress [7-0]
    
    SPI_SendByte(8'hAA); //Dummy Bits, read byte in w_Master_RX_Byte
    data = w_Master_RX_Byte;
    
  endtask //end FRAM_READ
  
  task FRAM_Read_Status(output [7:0] data); //vgl. Fig9
    r_Master_TX_Count <= 3'h2; //2 Byte Transaction
    SPI_SendByte(RDSR); //OpCode
    SPI_SendByte(8'hFD); //Dummy Bits, read byte in w_Master_RX_Byte
    data = w_Master_RX_Byte;
  endtask //FRAM_Read_Status
  
  task FRAM_Hibernation(); //vgl. Fig22
    r_Master_TX_Count <= 3'h1; //1 Byte Transaction
    SPI_SendByte(HBN);    
  endtask //FRAM_Hibernation
  
  
  //end FRAM Tasks
  
  
  always @(posedge i_clk or negedge i_nreset) begin
 
    state[0] = i_cready;
    state[1] = i_hbn;
    state[2] = i_status;
    state[3] = i_rw;    
        
    if(~i_nreset) begin //Modul Reset
      o_data <=  8'h00;
    end //end if
    
    if(w_Master_TX_Ready) begin
    	case(state) inside
          4'b??11: FRAM_Hibernation();
          4'b?101: FRAM_Read_Status(o_data);
          4'b1001: FRAM_Read(i_adr, o_data);
          4'b0001: FRAM_Write(i_adr, i_data);
      		
      	  default:; 
        endcase //endcase
    end //endif

  end //end always
  
    
  assign o_busy = w_Master_TX_Ready; 
  
  
endmodule
