DV update
diff --git a/verilog/dv/caravel/user_proj_example/23LC512.v b/verilog/dv/caravel/user_proj_example/23LC512.v
new file mode 100644
index 0000000..fcff5b5
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/23LC512.v
@@ -0,0 +1,627 @@
+// *******************************************************************************************************
+// **                                                                                                   **
+// **   23LC512.v - 23LC512 512 KBIT SPI SERIAL SRAM (VCC = +2.5V TO +5.5V)                             **
+// **                                                                                                   **
+// *******************************************************************************************************
+// **                                                                                                   **
+// **                   This information is distributed under license from Young Engineering.           **
+// **                              COPYRIGHT (c) 2014 YOUNG ENGINEERING                                 **
+// **                                      ALL RIGHTS RESERVED                                          **
+// **                                                                                                   **
+// **                                                                                                   **
+// **   Young Engineering provides design expertise for the digital world                               **
+// **   Started in 1990, Young Engineering offers products and services for your electronic design      **
+// **   project.  We have the expertise in PCB, FPGA, ASIC, firmware, and software design.              **
+// **   From concept to prototype to production, we can help you.                                       **
+// **                                                                                                   **
+// **   http://www.young-engineering.com/                                                               **
+// **                                                                                                   **
+// *******************************************************************************************************
+// **                                                                                                   **
+// **   This information is provided to you for your convenience and use with Microchip products only.  **
+// **   Microchip disclaims all liability arising from this information and its use.                    **
+// **                                                                                                   **
+// **   THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF        **
+// **   ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO        **
+// **   THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY,           **
+// **   PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE.                         **
+// **   MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL      **
+// **   DAMAGES, FOR ANY REASON WHATSOEVER.                                                             **
+// **                                                                                                   **
+// **   It is your responsibility to ensure that your application meets with your specifications.       **
+// **                                                                                                   **
+// *******************************************************************************************************
+// **                                                                                                   **
+// **   Revision       : 1.0                                                                            **
+// **   Modified Date  : 04/23/2014                                                                     **
+// **   Revision History:                                                                               **
+// **                                                                                                   **
+// **   04/23/2014:  Initial design                                                                     **
+// **   Modified Date  : 5/5/2014                                                                       **
+// **   Revision History:                                                                               **
+// **   Based on the 23LC1024.v model a 512k bit model for the 23LC512 is drafted below                 **
+// *******************************************************************************************************
+// **                                       TABLE OF CONTENTS                                           **
+// *******************************************************************************************************
+// **---------------------------------------------------------------------------------------------------**
+// **   DECLARATIONS                                                                                    **
+// **---------------------------------------------------------------------------------------------------**
+// **---------------------------------------------------------------------------------------------------**
+// **   INITIALIZATION                                                                                  **
+// **---------------------------------------------------------------------------------------------------**
+// **---------------------------------------------------------------------------------------------------**
+// **   CORE LOGIC                                                                                      **
+// **---------------------------------------------------------------------------------------------------**
+// **   1.01:  Internal Reset Logic                                                                     **
+// **   1.02:  Input Data Shifter                                                                       **
+// **   1.03:  Clock Cycle Counter                                                                      **
+// **   1.04:  Instruction Register                                                                     **
+// **   1.05:  Address Register                                                                         **
+// **   1.06:  Status Register Write                                                                    **
+// **   1.07:  I/O Mode Instructions                                                                    **
+// **   1.08:  Array Write                                                                              **
+// **   1.09:  Output Data Shifter                                                                      **
+// **   1.10:  Output Data Buffer                                                                       **
+// **                                                                                                   **
+// **---------------------------------------------------------------------------------------------------**
+// **   DEBUG LOGIC                                                                                     **
+// **---------------------------------------------------------------------------------------------------**
+// **   2.01:  Memory Data Bytes                                                                        **
+// **                                                                                                   **
+// **---------------------------------------------------------------------------------------------------**
+// **   TIMING CHECKS                                                                                   **
+// **---------------------------------------------------------------------------------------------------**
+// **                                                                                                   **
+// *******************************************************************************************************
+
+
+`timescale 1ns/10ps
+
+module M23LC512 (SI_SIO0, SO_SIO1, SCK, CS_N, SIO2, HOLD_N_SIO3, RESET);
+
+   inout                SI_SIO0;                        // serial data input/output
+   input                SCK;                            // serial data clock
+
+   input                CS_N;                           // chip select - active low
+
+   inout                SIO2;                           // serial data input/output
+
+   inout                HOLD_N_SIO3;                    // interface suspend - active low/
+                                                        //   serial data input/output
+
+   input                RESET;                          // model reset/power-on reset
+
+   inout                SO_SIO1;                        // serial data input/output
+
+
+// *******************************************************************************************************
+// **   DECLARATIONS                                                                                    **
+// *******************************************************************************************************
+
+   reg  [07:00]         DataShifterI;                   // serial input data shifter
+   reg  [07:00]         DataShifterO;                   // serial output data shifter
+   reg  [31:00]         ClockCounter;                   // serial input clock counter
+   reg  [07:00]         InstRegister;                   // instruction register
+   reg  [15:00]         AddrRegister;                   // address register modified for 16 bit addresses
+
+
+   wire                 InstructionREAD;                // decoded instruction byte
+   wire                 InstructionRDMR;                // decoded instruction byte
+   wire                 InstructionWRMR;                // decoded instruction byte
+   wire                 InstructionWRITE;               // decoded instruction byte
+   wire                 InstructionEDIO;                // decoded instruction byte
+   wire                 InstructionEQIO;                // decoded instruction byte
+   wire                 InstructionRSTIO;               // decoded instruction byte
+
+   reg  [01:00]         OpMode;                         // operation mode
+
+   reg  [01:00]         IOMode;                         // I/O mode
+
+   wire                 Hold;                           // hold function
+
+   reg  [07:00]         MemoryBlock [0:65535];          // SRAM data memory array (65536x8)
+
+   reg  [03:00]         SO_DO;                          // serial output data - data
+   wire                 SO_OE;                          // serial output data - output enable
+
+   reg                  SO_Enable;                      // serial data output enable
+
+   wire                 OutputEnable1;                  // timing accurate output enable
+   wire                 OutputEnable2;                  // timing accurate output enable
+   wire                 OutputEnable3;                  // timing accurate output enable
+
+   integer              tV;                             // timing parameter
+   integer              tHZ;                            // timing parameter
+   integer              tHV;                            // timing parameter
+   integer              tDIS;                           // timing parameter
+
+`define READ      8'b0000_0011                          // Read instruction
+`define WRMR      8'b0000_0001                          // Write Mode Register instruction
+`define WRITE     8'b0000_0010                          // Write instruction
+`define RDMR      8'b0000_0101                          // Read Mode Register instruction
+`define EDIO      8'b0011_1011                          // Enter Dual I/O instruction
+`define EQIO      8'b0011_1000                          // Enter Quad I/O instruction
+`define RSTIO     8'b1111_1111                          // Reset Dual and Quad I/O instruction
+
+`define BYTEMODE  2'b00                                 // Byte operation mode
+`define PAGEMODE  2'b10                                 // Page operation mode
+`define SEQMODE   2'b01                                 // Sequential operation mode
+
+`define SPIMODE   2'b00                                 // SPI I/O mode
+`define SDIMODE   2'b01                                 // SDI I/O mode
+`define SQIMODE   2'b10                                 // SQI I/O mode
+
+// *******************************************************************************************************
+// **   INITIALIZATION                                                                                  **
+// *******************************************************************************************************
+
+   initial begin
+      `ifdef TEMP_INDUSTRIAL
+         tV   = 25;                                     // output valid from SCK low
+         tHZ  = 10;                                     // HOLD_N low to output high-z
+         tHV  = 50;                                     // HOLD_N high to output valid
+         tDIS = 20;                                     // CS_N high to output disable
+      `else
+      `ifdef TEMP_EXTENDED
+         tV   = 32;                                     // output valid from SCK low
+         tHZ  = 10;                                     // HOLD_N low to output high-z
+         tHV  = 50;                                     // HOLD_N high to output valid
+         tDIS = 20;                                     // CS_N high to output disable
+      `else
+         tV   = 25;                                     // output valid from SCK low
+         tHZ  = 10;                                     // HOLD_N low to output high-z
+         tHV  = 50;                                     // HOLD_N high to output valid
+         tDIS = 20;                                     // CS_N high to output disable
+      `endif
+      `endif
+   end
+
+   initial begin
+      OpMode = `SEQMODE;
+
+      IOMode = `SPIMODE;
+   end
+
+   assign Hold = (HOLD_N_SIO3 == 0) & (IOMode == `SPIMODE);
+
+
+// *******************************************************************************************************
+// **   CORE LOGIC                                                                                      **
+// *******************************************************************************************************
+// -------------------------------------------------------------------------------------------------------
+//      1.01:  Internal Reset Logic
+// -------------------------------------------------------------------------------------------------------
+
+   always @(negedge CS_N) ClockCounter <= 0;
+   always @(negedge CS_N) SO_Enable    <= 0;
+
+// -------------------------------------------------------------------------------------------------------
+//      1.02:  Input Data Shifter
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0) begin
+         if (CS_N == 0) begin
+            case (IOMode)
+               `SPIMODE: DataShifterI <= {DataShifterI[06:00],SI_SIO0};
+               `SDIMODE: DataShifterI <= {DataShifterI[05:00],SO_SIO1,SI_SIO0};
+               `SQIMODE: DataShifterI <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0};
+               default: $error("IOMode set to invalid value.");
+            endcase
+         end
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.03:  Clock Cycle Counter
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0) begin
+         if (CS_N == 0)         ClockCounter <= ClockCounter + 1;
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.04:  Instruction Register
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0) begin
+         case (IOMode)
+            `SPIMODE: begin
+               if (ClockCounter == 7) InstRegister <= {DataShifterI[06:00],SI_SIO0};
+            end
+            `SDIMODE: begin
+               if (ClockCounter == 3) InstRegister <= {DataShifterI[05:00],SO_SIO1,SI_SIO0};
+            end
+            `SQIMODE: begin
+               if (ClockCounter == 1) InstRegister <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0};
+            end
+            default: $error("IOMode set to invalid value.");
+         endcase
+      end
+   end
+
+   assign InstructionREAD  = (InstRegister[7:0] == `READ);
+   assign InstructionRDMR  = (InstRegister[7:0] == `RDMR);
+   assign InstructionWRMR  = (InstRegister[7:0] == `WRMR);
+   assign InstructionWRITE = (InstRegister[7:0] == `WRITE);
+   assign InstructionEDIO  = (InstRegister[7:0] == `EDIO);
+   assign InstructionEQIO  = (InstRegister[7:0] == `EQIO);
+   assign InstructionRSTIO = (InstRegister[7:0] == `RSTIO);
+
+// -------------------------------------------------------------------------------------------------------
+//      1.05:  Address Register
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0 & (InstructionREAD | InstructionWRITE)) begin
+         case (IOMode)
+            `SPIMODE: begin
+
+               if (ClockCounter == 15) AddrRegister[15:08] <= {DataShifterI[06:00],SI_SIO0};
+               else if (ClockCounter == 23) AddrRegister[07:00] <= {DataShifterI[06:00],SI_SIO0};
+
+            end
+            `SDIMODE: begin
+
+               if (ClockCounter == 7) AddrRegister[15:08] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0};
+               else if (ClockCounter == 11) AddrRegister[07:00] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0};
+
+            end
+            `SQIMODE: begin
+
+               if (ClockCounter == 3) AddrRegister[15:08] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0};
+               else if (ClockCounter == 5) AddrRegister[07:00] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0};
+
+
+            end
+            default: $error("IOMode set to invalid value.");
+         endcase
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.06:  Status Register Write
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0 & InstructionWRMR) begin
+         case (IOMode)
+            `SPIMODE: begin
+               if (ClockCounter == 15) OpMode <= DataShifterI[06:05]; //datashifter is missing one bit
+            end
+            `SDIMODE: begin
+               if (ClockCounter == 7) OpMode <= DataShifterI[05:04]; //datashifter is missing two bits
+            end
+            `SQIMODE: begin
+               if (ClockCounter == 3) OpMode <= DataShifterI[03:02]; //...missing a nibble
+            end
+            default: $error("IOMode set to invalid value.");
+         endcase
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.07:  I/O Mode Instructions
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin   //changes io mode.
+      case (IOMode)
+         `SPIMODE: begin
+            if (ClockCounter == 7) begin
+               if ({DataShifterI[06:00],SI_SIO0} == `EDIO) IOMode <= `SDIMODE;
+               else if ({DataShifterI[06:00],SI_SIO0} == `EQIO) IOMode <= `SQIMODE;
+            end
+         end
+         `SDIMODE: begin
+            if (ClockCounter == 3) begin
+               if ({DataShifterI[05:00],SO_SIO1,SI_SIO0} == `EQIO) IOMode <= `SQIMODE;
+               else if ({DataShifterI[05:00],SO_SIO1,SI_SIO0} == `RSTIO) IOMode <= `SPIMODE;
+            end
+         end
+         `SQIMODE: begin
+            if (ClockCounter == 1) begin
+               if ({DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0} == `EDIO) IOMode <= `SDIMODE;
+               else if ({DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0} == `RSTIO) IOMode <= `SPIMODE;
+            end
+         end
+      endcase
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.08:  Array Write
+// -------------------------------------------------------------------------------------------------------
+
+   always @(posedge SCK) begin
+      if (Hold == 0 & InstructionWRITE) begin
+         case (IOMode)
+            `SPIMODE: begin
+
+                 if ((ClockCounter >= 31) & (ClockCounter[2:0] == 3'b111)) begin   //every odd clock, where odd means %8=7
+
+                  MemoryBlock[AddrRegister[15:00]] <= {DataShifterI[06:00],SI_SIO0};
+
+                  case (OpMode)
+                     `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                     `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+                  endcase
+               end
+            end
+            `SDIMODE: begin
+
+               if ((ClockCounter >= 15) & (ClockCounter[1:0] == 2'b11)) begin
+
+                  MemoryBlock[AddrRegister[15:00]] <= {DataShifterI[05:00],SO_SIO1,SI_SIO0};
+
+                  case (OpMode)
+                     `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                     `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+                  endcase
+               end
+            end
+            `SQIMODE: begin
+
+              if ((ClockCounter >= 7) & (ClockCounter[0] == 1'b1)) begin
+
+                  MemoryBlock[AddrRegister[15:00]] <= {DataShifterI[03:00],HOLD_N_SIO3,SIO2,SO_SIO1,SI_SIO0};
+
+                  case (OpMode)
+                     `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                     `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+
+                  endcase
+               end
+            end
+            default: $error("IOMode set to invalid value.");
+         endcase
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.09:  Output Data Shifter
+// -------------------------------------------------------------------------------------------------------
+
+   always @(negedge SCK) begin
+      if (Hold == 0) begin
+         if (InstructionREAD) begin
+            case (IOMode)
+               `SPIMODE: begin
+
+                   if ((ClockCounter >= 24) & (ClockCounter[2:0] == 3'b000)) begin
+                     DataShifterO <= MemoryBlock[AddrRegister[15:00]];
+                     SO_Enable    <= 1;
+
+                     case (OpMode)
+                       `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                       `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+                     endcase
+                  end
+                  else DataShifterO <= DataShifterO << 1;
+               end
+               `SDIMODE: begin
+                  if ((ClockCounter >= 16) & (ClockCounter[1:0] == 2'b00)) begin
+                     DataShifterO <= MemoryBlock[AddrRegister[15:00]];
+                     SO_Enable    <= 1;
+
+                     case (OpMode)
+                       `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                       `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+                     endcase
+                  end
+                  else DataShifterO <= DataShifterO << 2;
+               end
+               `SQIMODE: begin
+                  if ((ClockCounter >= 8) & (ClockCounter[0] == 1'b0)) begin
+                     DataShifterO <= MemoryBlock[AddrRegister[15:00]];
+                     SO_Enable    <= 1;
+
+                     case (OpMode)
+                       `PAGEMODE: AddrRegister[04:00] <= AddrRegister[04:00] + 1;
+                       `SEQMODE: AddrRegister[15:00] <= AddrRegister[15:00] + 1;
+                     endcase
+                  end
+                  else DataShifterO <= DataShifterO << 4;
+               end
+               default: $error("IOMode set to invalid value.");
+            endcase
+         end
+         else if (InstructionRDMR) begin
+            case (IOMode)
+               `SPIMODE: begin
+                  if ((ClockCounter > 7) & (ClockCounter[2:0] == 3'b000)) begin
+                     DataShifterO <= {OpMode,6'b000000};
+                     SO_Enable    <= 1;
+                  end
+                  else DataShifterO <= DataShifterO << 1;
+               end
+               `SDIMODE: begin
+                  if ((ClockCounter > 3) & (ClockCounter[1:0] == 2'b00)) begin
+                     DataShifterO <= {OpMode,6'b000000};
+                     SO_Enable    <= 1;
+                  end
+                  else DataShifterO <= DataShifterO << 2;
+               end
+               `SQIMODE: begin
+                  if ((ClockCounter > 1) & (ClockCounter[0] == 1'b0)) begin
+                     DataShifterO <= {OpMode,6'b000000};
+                     SO_Enable    <= 1;
+                  end
+                  else DataShifterO <= DataShifterO << 4;
+               end
+               default: $error("IOMode set to invalid value.");
+            endcase
+         end
+      end
+   end
+
+// -------------------------------------------------------------------------------------------------------
+//      1.10:  Output Data Buffer
+// -------------------------------------------------------------------------------------------------------
+
+   // Buffer for SPI mode
+   bufif1 (SO_SIO1, SO_DO[0], SO_OE & (IOMode == `SPIMODE));
+
+   // Buffers for SDI mode
+   bufif1 (SI_SIO0, SO_DO[0], SO_OE & (IOMode == `SDIMODE));
+   bufif1 (SO_SIO1, SO_DO[1], SO_OE & (IOMode == `SDIMODE));
+
+   // Buffers for SQI Mode
+   bufif1 (SI_SIO0, SO_DO[0], SO_OE & (IOMode == `SQIMODE));
+   bufif1 (SO_SIO1, SO_DO[1], SO_OE & (IOMode == `SQIMODE));
+   bufif1 (SIO2, SO_DO[2], SO_OE & (IOMode == `SQIMODE));
+   bufif1 (HOLD_N_SIO3, SO_DO[3], SO_OE & (IOMode == `SQIMODE));
+
+   always @(DataShifterO) begin
+      case (IOMode)
+        `SPIMODE: begin
+           SO_DO[0] <= #(tV) DataShifterO[07];
+        end
+        `SDIMODE: begin
+           SO_DO[1] <= #(tV) DataShifterO[07];
+           SO_DO[0] <= #(tV) DataShifterO[06];
+        end
+        `SQIMODE: begin
+           SO_DO[3] <= #(tV) DataShifterO[07];
+           SO_DO[2] <= #(tV) DataShifterO[06];
+           SO_DO[1] <= #(tV) DataShifterO[05];
+           SO_DO[0] <= #(tV) DataShifterO[04];
+        end
+      endcase
+   end
+
+   bufif1 #(tV,0)    (OutputEnable1, SO_Enable, 1);
+   notif1 #(tDIS)    (OutputEnable2, CS_N,   1);
+   bufif1 #(tHV,tHZ) (OutputEnable3, HOLD_N_SIO3 | !(IOMode == `SPIMODE), 1);
+
+   assign SO_OE = OutputEnable1 & OutputEnable2 & OutputEnable3;
+
+
+// *******************************************************************************************************
+// **   DEBUG LOGIC                                                                                     **
+// *******************************************************************************************************
+// -------------------------------------------------------------------------------------------------------
+//      2.01:  Memory Data Bytes
+// -------------------------------------------------------------------------------------------------------
+
+   wire [07:00] MemoryByte00000 = MemoryBlock[000000];
+   wire [07:00] MemoryByte00001 = MemoryBlock[000001];
+   wire [07:00] MemoryByte00002 = MemoryBlock[000002];
+   wire [07:00] MemoryByte00003 = MemoryBlock[000003];
+   wire [07:00] MemoryByte00004 = MemoryBlock[000004];
+   wire [07:00] MemoryByte00005 = MemoryBlock[000005];
+   wire [07:00] MemoryByte00006 = MemoryBlock[000006];
+   wire [07:00] MemoryByte00007 = MemoryBlock[000007];
+   wire [07:00] MemoryByte00008 = MemoryBlock[000008];
+   wire [07:00] MemoryByte00009 = MemoryBlock[000009];
+   wire [07:00] MemoryByte0000A = MemoryBlock[000010];
+   wire [07:00] MemoryByte0000B = MemoryBlock[000011];
+   wire [07:00] MemoryByte0000C = MemoryBlock[000012];
+   wire [07:00] MemoryByte0000D = MemoryBlock[000013];
+   wire [07:00] MemoryByte0000E = MemoryBlock[000014];
+   wire [07:00] MemoryByte0000F = MemoryBlock[000015];
+
+   wire [07:00] MemoryByte0FFF0 = MemoryBlock[65519];
+   wire [07:00] MemoryByte0FFF1 = MemoryBlock[65520];
+   wire [07:00] MemoryByte0FFF2 = MemoryBlock[65521];
+   wire [07:00] MemoryByte0FFF3 = MemoryBlock[65522];
+   wire [07:00] MemoryByte0FFF4 = MemoryBlock[65523];
+   wire [07:00] MemoryByte0FFF5 = MemoryBlock[65524];
+   wire [07:00] MemoryByte0FFF6 = MemoryBlock[65525];
+   wire [07:00] MemoryByte0FFF7 = MemoryBlock[65526];
+   wire [07:00] MemoryByte0FFF8 = MemoryBlock[65527];
+   wire [07:00] MemoryByte0FFF9 = MemoryBlock[65528];
+   wire [07:00] MemoryByte0FFFA = MemoryBlock[65529];
+   wire [07:00] MemoryByte0FFFB = MemoryBlock[65530];
+   wire [07:00] MemoryByte0FFFC = MemoryBlock[65531];
+   wire [07:00] MemoryByte0FFFD = MemoryBlock[65532];
+   wire [07:00] MemoryByte0FFFE = MemoryBlock[65534];
+   wire [07:00] MemoryByte0FFFF = MemoryBlock[65535];
+
+// *******************************************************************************************************
+// **   TIMING CHECKS                                                                                   **
+// *******************************************************************************************************
+
+   wire TimingCheckEnable = (RESET == 0) & (CS_N == 0);
+   wire SPITimingCheckEnable = TimingCheckEnable & (IOMode == `SPIMODE);
+   wire SDITimingCheckEnable = TimingCheckEnable & (IOMode == `SDIMODE) & (SO_Enable == 0);
+   wire SQITimingCheckEnable = TimingCheckEnable & (IOMode == `SQIMODE) & (SO_Enable == 0);
+
+   specify
+      `ifdef TEMP_INDUSTRIAL
+         specparam
+            tHI  = 25,                                  // Clock high time
+            tLO  = 25,                                  // Clock low time
+            tSU  = 10,                                  // Data setup time
+            tHD  = 10,                                  // Data hold time
+            tHS  = 10,                                  // HOLD_N setup time
+            tHH  = 10,                                  // HOLD_N hold time
+            tCSD = 25,                                  // CS_N disable time
+            tCSS = 25,                                  // CS_N setup time
+            tCSH = 50,                                  // CS_N hold time
+            tCLD = 25;                                  // Clock delay time
+      `else
+      `ifdef TEMP_EXTENDED
+         specparam
+            tHI  = 32,                                  // Clock high time
+            tLO  = 32,                                  // Clock low time
+            tSU  = 10,                                  // Data setup time
+            tHD  = 10,                                  // Data hold time
+            tHS  = 10,                                  // HOLD_N setup time
+            tHH  = 10,                                  // HOLD_N hold time
+            tCSD = 32,                                  // CS_N disable time
+            tCSS = 32,                                  // CS_N setup time
+            tCSH = 50,                                  // CS_N hold time
+            tCLD = 32;                                  // Clock delay time
+      `else
+         specparam
+            tHI  = 25,                                  // Clock high time
+            tLO  = 25,                                  // Clock low time
+            tSU  = 10,                                  // Data setup time
+            tHD  = 10,                                  // Data hold time
+            tHS  = 10,                                  // HOLD_N setup time
+            tHH  = 10,                                  // HOLD_N hold time
+            tCSD = 25,                                  // CS_N disable time
+            tCSS = 25,                                  // CS_N setup time
+            tCSH = 50,                                  // CS_N hold time
+            tCLD = 25;                                  // Clock delay time
+      `endif
+      `endif
+
+      $width (posedge SCK,  tHI);
+      $width (negedge SCK,  tLO);
+      $width (posedge CS_N, tCSD);
+
+      $setup (negedge CS_N, posedge SCK &&& TimingCheckEnable, tCSS);
+      $setup (posedge CS_N, posedge SCK &&& TimingCheckEnable, tCLD);
+
+      $hold  (posedge SCK &&& TimingCheckEnable, posedge CS_N, tCSH);
+
+      // SPI-specific timing checks
+      $setup (SI_SIO0, posedge SCK &&& SPITimingCheckEnable, tSU);
+      $setup (negedge SCK, negedge HOLD_N_SIO3 &&& SPITimingCheckEnable, tHS);
+
+      $hold  (posedge SCK &&& SPITimingCheckEnable, SI_SIO0,   tHD);
+      $hold  (posedge HOLD_N_SIO3 &&& SPITimingCheckEnable, posedge SCK,  tHH);
+
+      // SDI-specific timing checks
+      $setup (SI_SIO0, posedge SCK &&& SDITimingCheckEnable, tSU);
+      $setup (SO_SIO1, posedge SCK &&& SDITimingCheckEnable, tSU);
+
+      $hold  (posedge SCK &&& SDITimingCheckEnable, SI_SIO0,   tHD);
+      $hold  (posedge SCK &&& SDITimingCheckEnable, SO_SIO1,   tHD);
+
+      // SQI-specific timing checks
+      $setup (SI_SIO0, posedge SCK &&& SQITimingCheckEnable, tSU);
+      $setup (SO_SIO1, posedge SCK &&& SQITimingCheckEnable, tSU);
+      $setup (SIO2, posedge SCK &&& SQITimingCheckEnable, tSU);
+      $setup (HOLD_N_SIO3, posedge SCK &&& SQITimingCheckEnable, tSU);
+
+      $hold  (posedge SCK &&& SQITimingCheckEnable, SI_SIO0,   tHD);
+      $hold  (posedge SCK &&& SQITimingCheckEnable, SO_SIO1,   tHD);
+      $hold  (posedge SCK &&& SQITimingCheckEnable, SIO2,   tHD);
+      $hold  (posedge SCK &&& SQITimingCheckEnable, HOLD_N_SIO3,   tHD);
+  endspecify
+
+endmodule
\ No newline at end of file
diff --git a/verilog/dv/caravel/user_proj_example/24LC16B.v b/verilog/dv/caravel/user_proj_example/24LC16B.v
new file mode 100644
index 0000000..786243e
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/24LC16B.v
@@ -0,0 +1,633 @@
+// *******************************************************************************************************

+// **                                                                           			**

+// **   24LC16B.v - Microchip 24LC16B 16K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V)			**

+// **                                                                           			**

+// *******************************************************************************************************

+// **                                                                           			**

+// **			This information is distributed under license from Young Engineering.		**

+// **                              COPYRIGHT (c) 2003 YOUNG ENGINEERING              			**

+// **                                      ALL RIGHTS RESERVED                         			**

+// **                                                                           			**

+// **                                                                                                   **

+// **   Young Engineering provides design expertise for the digital world                               **

+// **   Started in 1990, Young Engineering offers products and services for your electronic design      **

+// **   project.  We have the expertise in PCB, FPGA, ASIC, firmware, and software design.              **

+// **   From concept to prototype to production, we can help you.                                       **

+// **													**

+// **	http://www.young-engineering.com/								**

+// **													**

+// *******************************************************************************************************

+// **	This information is provided to you for your convenience and use with Microchip products only.  **

+// **	Microchip disclaims all liability arising from this information and its use.  			**

+// **													**

+// **	THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF 	**

+// **	ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO 	**

+// **	THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, 		**

+// **	PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE.  			**

+// **	MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL 	**

+// **	DAMAGES, FOR ANY REASON WHATSOEVER.								**

+// **													**

+// **	It is your responsibility to ensure that your application meets with your specifications.	**

+// **													**

+// *******************************************************************************************************

+// **   Revision       : 1.0                                                    			**

+// **   Modified Date  : 12/04/2006	                                             			**

+// **   Revision History:                                                       			**

+// **                                                                           			**

+// **   12/04/2006:  Initial design                                             			**

+// **                                                                           			**

+// *******************************************************************************************************

+// **                                       TABLE OF CONTENTS                          			**

+// *******************************************************************************************************

+// **---------------------------------------------------------------------------------------------------**

+// **   DECLARATIONS                                                          				**

+// **---------------------------------------------------------------------------------------------------**

+// **---------------------------------------------------------------------------------------------------**

+// **   INITIALIZATION                                              					**

+// **---------------------------------------------------------------------------------------------------**

+// **---------------------------------------------------------------------------------------------------**

+// **   CORE LOGIC                                                  					**

+// **---------------------------------------------------------------------------------------------------**

+// **   1.01:  START Bit Detection									**

+// **   1.02:  STOP Bit Detection									**

+// **   1.03:  Input Shift Register									**

+// **   1.04:  Input Bit Counter									**

+// **   1.05:  Control Byte Register									**

+// **   1.06:  Byte Address Register									**

+// **   1.07:  Write Data Buffer									**

+// **   1.08:  Acknowledge Generator									**

+// **   1.09:  Acknowledge Detect									**

+// **   1.10:  Write Cycle Timer									**

+// **   1.11:  Write Cycle Processor									**

+// **   1.12:  Read Data Multiplexor									**

+// **   1.13:  Read Data Processor									**

+// **   1.14:  SDA Data I/O Buffer									**

+// **                                                                           			**

+// **---------------------------------------------------------------------------------------------------**

+// **   DEBUG LOGIC                                                  					**

+// **---------------------------------------------------------------------------------------------------**

+// **   2.01:  Memory Data Bytes									**

+// **   2.02:  Write Data Buffer									**

+// **                                                                           			**

+// **---------------------------------------------------------------------------------------------------**

+// **   TIMING CHECKS                                                     				**

+// **---------------------------------------------------------------------------------------------------**

+// **                                                                           			**

+// *******************************************************************************************************

+

+

+`timescale 1ns/10ps

+

+module M24LC16B (A0, A1, A2, WP, SDA, SCL, RESET);

+

+   input 		A0;				// unconnected pin

+   input 		A1;				// unconnected pin

+   input 		A2;				// unconnected pin

+

+   input		WP;				// write protect pin

+

+   inout		SDA;				// serial data I/O

+   input		SCL;				// serial data clock

+

+   input		RESET;				// system reset

+

+

+// *******************************************************************************************************

+// **   DECLARATIONS                                                            			**

+// *******************************************************************************************************

+

+   reg			SDA_DO;				// serial data - output

+   reg			SDA_OE;				// serial data - output enable

+

+   wire			SDA_DriveEnable;		// serial data output enable

+   reg			SDA_DriveEnableDlyd;		// serial data output enable - delayed

+

+   reg	[03:00]		BitCounter;			// serial bit counter

+

+   reg			START_Rcvd;			// START bit received flag

+   reg			STOP_Rcvd;			// STOP bit received flag

+   reg			CTRL_Rcvd;			// control byte received flag

+   reg			ADDR_Rcvd;			// byte address received flag

+   reg			MACK_Rcvd;			// master acknowledge received flag

+

+   reg			WrCycle;			// memory write cycle

+   reg			RdCycle;			// memory read cycle

+

+   reg	[07:00]		ShiftRegister;			// input data shift register

+

+   reg  [07:00]		ControlByte;			// control byte register

+   wire	[02:00]		BlockSelect;			// memory block select

+   wire			RdWrBit;			// read/write control bit

+

+   reg	[10:00]		StartAddress;			// memory access starting address

+   reg	[03:00]		PageAddress;			// memory page address

+

+   reg	[07:00]		WrDataByte [0:15];		// memory write data buffer

+   wire [07:00]		RdDataByte;			// memory read data

+

+   reg	[15:00]		WrCounter;			// write buffer counter

+

+   reg	[03:00]		WrPointer;			// write buffer pointer

+   reg	[10:00]		RdPointer;			// read address pointer

+

+   reg			WriteActive;			// memory write cycle active

+

+   reg	[07:00]		MemoryBlock0 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock1 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock2 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock3 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock4 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock5 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock6 [0:255];		// EEPROM data memory array

+   reg	[07:00]		MemoryBlock7 [0:255];		// EEPROM data memory array

+

+   integer		LoopIndex;			// iterative loop index

+

+   integer 		tAA;				// timing parameter

+   integer 		tWC;				// timing parameter

+

+

+// *******************************************************************************************************

+// **   INITIALIZATION                                                         				**

+// *******************************************************************************************************

+

+   initial tAA = 900;					// SCL to SDA output delay

+   initial tWC = 5_000;//5_000_000;				// memory write cycle time // updated for SoC simulation

+

+   initial begin

+      SDA_DO = 0;

+      SDA_OE = 0;

+   end

+

+   initial begin

+      START_Rcvd = 0;

+      STOP_Rcvd  = 0;

+      CTRL_Rcvd  = 0;

+      ADDR_Rcvd  = 0;

+      MACK_Rcvd  = 0;

+   end

+

+   initial begin

+      BitCounter  = 0;

+      ControlByte = 0;

+   end

+

+   initial begin

+      WrCycle = 0;

+      RdCycle = 0;

+

+      WriteActive = 0;

+   end

+

+

+// *******************************************************************************************************

+// **   CORE LOGIC                                                    					**

+// *******************************************************************************************************

+// -------------------------------------------------------------------------------------------------------

+//      1.01:  START Bit Detection

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SDA) begin

+      if (SCL == 1) begin

+         START_Rcvd <= 1;

+         STOP_Rcvd  <= 0;

+         CTRL_Rcvd  <= 0;

+         ADDR_Rcvd  <= 0;

+         MACK_Rcvd  <= 0;

+

+         WrCycle <= #1 0;

+         RdCycle <= #1 0;

+

+         BitCounter <= 0;

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.02:  STOP Bit Detection

+// -------------------------------------------------------------------------------------------------------

+

+   always @(posedge SDA) begin

+      if (SCL == 1) begin

+         START_Rcvd <= 0;

+         STOP_Rcvd  <= 1;

+         CTRL_Rcvd  <= 0;

+         ADDR_Rcvd  <= 0;

+         MACK_Rcvd  <= 0;

+

+         WrCycle <= #1 0;

+         RdCycle <= #1 0;

+

+         BitCounter <= 10;

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.03:  Input Shift Register

+// -------------------------------------------------------------------------------------------------------

+

+   always @(posedge SCL) begin

+      ShiftRegister[00] <= SDA;

+      ShiftRegister[01] <= ShiftRegister[00];

+      ShiftRegister[02] <= ShiftRegister[01];

+      ShiftRegister[03] <= ShiftRegister[02];

+      ShiftRegister[04] <= ShiftRegister[03];

+      ShiftRegister[05] <= ShiftRegister[04];

+      ShiftRegister[06] <= ShiftRegister[05];

+      ShiftRegister[07] <= ShiftRegister[06];

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.04:  Input Bit Counter

+// -------------------------------------------------------------------------------------------------------

+

+   always @(posedge SCL) begin

+      if (BitCounter < 10) BitCounter <= BitCounter + 1;

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.05:  Control Byte Register

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (START_Rcvd & (BitCounter == 8)) begin

+         if (!WriteActive & (ShiftRegister[07:04] == 4'b1010)) begin

+            if (ShiftRegister[00] == 0) WrCycle <= 1;

+            if (ShiftRegister[00] == 1) RdCycle <= 1;

+

+            ControlByte <= ShiftRegister[07:00];

+

+            CTRL_Rcvd <= 1;

+         end

+

+         START_Rcvd <= 0;

+      end

+   end

+

+   assign BlockSelect = ControlByte[03:01];

+   assign RdWrBit     = ControlByte[00];

+

+// -------------------------------------------------------------------------------------------------------

+//      1.06:  Byte Address Register

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (CTRL_Rcvd & (BitCounter == 8)) begin

+         if (RdWrBit == 0) begin

+            StartAddress <= {BlockSelect[02:00],ShiftRegister[07:00]};

+            RdPointer    <= {BlockSelect[02:00],ShiftRegister[07:00]};

+

+            ADDR_Rcvd <= 1;

+         end

+

+         WrCounter <= 0;

+         WrPointer <= 0;

+

+         CTRL_Rcvd <= 0;

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.07:  Write Data Buffer

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (ADDR_Rcvd & (BitCounter == 8)) begin

+         if ((WP == 0) & (RdWrBit == 0)) begin

+            WrDataByte[WrPointer] <= ShiftRegister[07:00];

+

+            WrCounter <= WrCounter + 1;

+            WrPointer <= WrPointer + 1;

+         end

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.08:  Acknowledge Generator

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (!WriteActive) begin

+         if (BitCounter == 8) begin

+            if (WrCycle | (START_Rcvd & (ShiftRegister[07:04] == 4'b1010))) begin

+               SDA_DO <= 0;

+               SDA_OE <= 1;

+            end 

+         end

+         if (BitCounter == 9) begin

+            BitCounter <= 0;

+

+            if (!RdCycle) begin

+               SDA_DO <= 0;

+               SDA_OE <= 0;

+            end

+         end

+      end

+   end 

+

+// -------------------------------------------------------------------------------------------------------

+//      1.09:  Acknowledge Detect

+// -------------------------------------------------------------------------------------------------------

+

+   always @(posedge SCL) begin

+      if (RdCycle & (BitCounter == 8)) begin

+         if ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;

+      end

+   end

+

+   always @(negedge SCL) MACK_Rcvd <= 0;

+

+// -------------------------------------------------------------------------------------------------------

+//      1.10:  Write Cycle Timer

+// -------------------------------------------------------------------------------------------------------

+

+   always @(posedge STOP_Rcvd) begin

+      if (WrCycle & (WP == 0) & (WrCounter > 0)) begin

+         WriteActive = 1;

+         #(tWC);

+         WriteActive = 0;

+      end

+   end

+

+   always @(posedge STOP_Rcvd) begin

+      #(1.0);

+      STOP_Rcvd = 0;

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.11:  Write Cycle Processor

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge WriteActive) begin

+      for (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) begin

+         PageAddress = StartAddress[03:00] + LoopIndex;

+

+         case (StartAddress[10:08])

+            3'b000 : MemoryBlock0[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b001 : MemoryBlock1[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b010 : MemoryBlock2[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b011 : MemoryBlock3[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b100 : MemoryBlock4[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b101 : MemoryBlock5[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b110 : MemoryBlock6[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+            3'b111 : MemoryBlock7[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];

+         endcase

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.12:  Read Data Multiplexor

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (BitCounter == 8) begin

+         if (WrCycle & ADDR_Rcvd) begin

+            RdPointer <= StartAddress + WrPointer + 1;

+         end

+         if (RdCycle) begin

+            RdPointer <= RdPointer + 1;

+         end

+      end

+   end

+

+   assign RdDataByte = {8{(RdPointer[10:08] == 0)}} & MemoryBlock0[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 1)}} & MemoryBlock1[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 2)}} & MemoryBlock2[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 3)}} & MemoryBlock3[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 4)}} & MemoryBlock4[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 5)}} & MemoryBlock5[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 6)}} & MemoryBlock6[RdPointer[07:00]]

+                     | {8{(RdPointer[10:08] == 7)}} & MemoryBlock7[RdPointer[07:00]];

+

+// -------------------------------------------------------------------------------------------------------

+//      1.13:  Read Data Processor

+// -------------------------------------------------------------------------------------------------------

+

+   always @(negedge SCL) begin

+      if (RdCycle) begin

+         if (BitCounter == 8) begin

+            SDA_DO <= 0;

+            SDA_OE <= 0;

+         end

+         else if (BitCounter == 9) begin

+            SDA_DO <= RdDataByte[07];

+

+            if (MACK_Rcvd) SDA_OE <= 1;

+         end

+         else begin

+            SDA_DO <= RdDataByte[7-BitCounter];

+         end

+      end

+   end

+

+// -------------------------------------------------------------------------------------------------------

+//      1.14:  SDA Data I/O Buffer

+// -------------------------------------------------------------------------------------------------------

+

+   bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);

+

+   assign SDA_DriveEnable = !SDA_DO & SDA_OE;

+   always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;

+

+

+// *******************************************************************************************************

+// **   DEBUG LOGIC                                                           				**

+// *******************************************************************************************************

+// -------------------------------------------------------------------------------------------------------

+//      2.01:  Memory Data Bytes

+// -------------------------------------------------------------------------------------------------------

+

+   wire [07:00]	MemoryByte0_00 = MemoryBlock0[00];

+   wire [07:00]	MemoryByte0_01 = MemoryBlock0[01];

+   wire [07:00]	MemoryByte0_02 = MemoryBlock0[02];

+   wire [07:00]	MemoryByte0_03 = MemoryBlock0[03];

+   wire [07:00]	MemoryByte0_04 = MemoryBlock0[04];

+   wire [07:00]	MemoryByte0_05 = MemoryBlock0[05];

+   wire [07:00]	MemoryByte0_06 = MemoryBlock0[06];

+   wire [07:00]	MemoryByte0_07 = MemoryBlock0[07];

+

+   wire [07:00]	MemoryByte0_08 = MemoryBlock0[08];

+   wire [07:00]	MemoryByte0_09 = MemoryBlock0[09];

+   wire [07:00]	MemoryByte0_0A = MemoryBlock0[10];

+   wire [07:00]	MemoryByte0_0B = MemoryBlock0[11];

+   wire [07:00]	MemoryByte0_0C = MemoryBlock0[12];

+   wire [07:00]	MemoryByte0_0D = MemoryBlock0[13];

+   wire [07:00]	MemoryByte0_0E = MemoryBlock0[14];

+   wire [07:00]	MemoryByte0_0F = MemoryBlock0[15];

+

+   wire [07:00]	MemoryByte1_00 = MemoryBlock1[00];

+   wire [07:00]	MemoryByte1_01 = MemoryBlock1[01];

+   wire [07:00]	MemoryByte1_02 = MemoryBlock1[02];

+   wire [07:00]	MemoryByte1_03 = MemoryBlock1[03];

+   wire [07:00]	MemoryByte1_04 = MemoryBlock1[04];

+   wire [07:00]	MemoryByte1_05 = MemoryBlock1[05];

+   wire [07:00]	MemoryByte1_06 = MemoryBlock1[06];

+   wire [07:00]	MemoryByte1_07 = MemoryBlock1[07];

+

+   wire [07:00]	MemoryByte1_08 = MemoryBlock1[08];

+   wire [07:00]	MemoryByte1_09 = MemoryBlock1[09];

+   wire [07:00]	MemoryByte1_0A = MemoryBlock1[10];

+   wire [07:00]	MemoryByte1_0B = MemoryBlock1[11];

+   wire [07:00]	MemoryByte1_0C = MemoryBlock1[12];

+   wire [07:00]	MemoryByte1_0D = MemoryBlock1[13];

+   wire [07:00]	MemoryByte1_0E = MemoryBlock1[14];

+   wire [07:00]	MemoryByte1_0F = MemoryBlock1[15];

+

+   wire [07:00]	MemoryByte2_00 = MemoryBlock2[00];

+   wire [07:00]	MemoryByte2_01 = MemoryBlock2[01];

+   wire [07:00]	MemoryByte2_02 = MemoryBlock2[02];

+   wire [07:00]	MemoryByte2_03 = MemoryBlock2[03];

+   wire [07:00]	MemoryByte2_04 = MemoryBlock2[04];

+   wire [07:00]	MemoryByte2_05 = MemoryBlock2[05];

+   wire [07:00]	MemoryByte2_06 = MemoryBlock2[06];

+   wire [07:00]	MemoryByte2_07 = MemoryBlock2[07];

+

+   wire [07:00]	MemoryByte2_08 = MemoryBlock2[08];

+   wire [07:00]	MemoryByte2_09 = MemoryBlock2[09];

+   wire [07:00]	MemoryByte2_0A = MemoryBlock2[10];

+   wire [07:00]	MemoryByte2_0B = MemoryBlock2[11];

+   wire [07:00]	MemoryByte2_0C = MemoryBlock2[12];

+   wire [07:00]	MemoryByte2_0D = MemoryBlock2[13];

+   wire [07:00]	MemoryByte2_0E = MemoryBlock2[14];

+   wire [07:00]	MemoryByte2_0F = MemoryBlock2[15];

+

+   wire [07:00]	MemoryByte3_00 = MemoryBlock3[00];

+   wire [07:00]	MemoryByte3_01 = MemoryBlock3[01];

+   wire [07:00]	MemoryByte3_02 = MemoryBlock3[02];

+   wire [07:00]	MemoryByte3_03 = MemoryBlock3[03];

+   wire [07:00]	MemoryByte3_04 = MemoryBlock3[04];

+   wire [07:00]	MemoryByte3_05 = MemoryBlock3[05];

+   wire [07:00]	MemoryByte3_06 = MemoryBlock3[06];

+   wire [07:00]	MemoryByte3_07 = MemoryBlock3[07];

+

+   wire [07:00]	MemoryByte3_08 = MemoryBlock3[08];

+   wire [07:00]	MemoryByte3_09 = MemoryBlock3[09];

+   wire [07:00]	MemoryByte3_0A = MemoryBlock3[10];

+   wire [07:00]	MemoryByte3_0B = MemoryBlock3[11];

+   wire [07:00]	MemoryByte3_0C = MemoryBlock3[12];

+   wire [07:00]	MemoryByte3_0D = MemoryBlock3[13];

+   wire [07:00]	MemoryByte3_0E = MemoryBlock3[14];

+   wire [07:00]	MemoryByte3_0F = MemoryBlock3[15];

+

+   wire [07:00]	MemoryByte4_00 = MemoryBlock4[00];

+   wire [07:00]	MemoryByte4_01 = MemoryBlock4[01];

+   wire [07:00]	MemoryByte4_02 = MemoryBlock4[02];

+   wire [07:00]	MemoryByte4_03 = MemoryBlock4[03];

+   wire [07:00]	MemoryByte4_04 = MemoryBlock4[04];

+   wire [07:00]	MemoryByte4_05 = MemoryBlock4[05];

+   wire [07:00]	MemoryByte4_06 = MemoryBlock4[06];

+   wire [07:00]	MemoryByte4_07 = MemoryBlock4[07];

+

+   wire [07:00]	MemoryByte4_08 = MemoryBlock4[08];

+   wire [07:00]	MemoryByte4_09 = MemoryBlock4[09];

+   wire [07:00]	MemoryByte4_0A = MemoryBlock4[10];

+   wire [07:00]	MemoryByte4_0B = MemoryBlock4[11];

+   wire [07:00]	MemoryByte4_0C = MemoryBlock4[12];

+   wire [07:00]	MemoryByte4_0D = MemoryBlock4[13];

+   wire [07:00]	MemoryByte4_0E = MemoryBlock4[14];

+   wire [07:00]	MemoryByte4_0F = MemoryBlock4[15];

+

+   wire [07:00]	MemoryByte5_00 = MemoryBlock5[00];

+   wire [07:00]	MemoryByte5_01 = MemoryBlock5[01];

+   wire [07:00]	MemoryByte5_02 = MemoryBlock5[02];

+   wire [07:00]	MemoryByte5_03 = MemoryBlock5[03];

+   wire [07:00]	MemoryByte5_04 = MemoryBlock5[04];

+   wire [07:00]	MemoryByte5_05 = MemoryBlock5[05];

+   wire [07:00]	MemoryByte5_06 = MemoryBlock5[06];

+   wire [07:00]	MemoryByte5_07 = MemoryBlock5[07];

+

+   wire [07:00]	MemoryByte5_08 = MemoryBlock5[08];

+   wire [07:00]	MemoryByte5_09 = MemoryBlock5[09];

+   wire [07:00]	MemoryByte5_0A = MemoryBlock5[10];

+   wire [07:00]	MemoryByte5_0B = MemoryBlock5[11];

+   wire [07:00]	MemoryByte5_0C = MemoryBlock5[12];

+   wire [07:00]	MemoryByte5_0D = MemoryBlock5[13];

+   wire [07:00]	MemoryByte5_0E = MemoryBlock5[14];

+   wire [07:00]	MemoryByte5_0F = MemoryBlock5[15];

+

+   wire [07:00]	MemoryByte6_00 = MemoryBlock6[00];

+   wire [07:00]	MemoryByte6_01 = MemoryBlock6[01];

+   wire [07:00]	MemoryByte6_02 = MemoryBlock6[02];

+   wire [07:00]	MemoryByte6_03 = MemoryBlock6[03];

+   wire [07:00]	MemoryByte6_04 = MemoryBlock6[04];

+   wire [07:00]	MemoryByte6_05 = MemoryBlock6[05];

+   wire [07:00]	MemoryByte6_06 = MemoryBlock6[06];

+   wire [07:00]	MemoryByte6_07 = MemoryBlock6[07];

+

+   wire [07:00]	MemoryByte6_08 = MemoryBlock6[08];

+   wire [07:00]	MemoryByte6_09 = MemoryBlock6[09];

+   wire [07:00]	MemoryByte6_0A = MemoryBlock6[10];

+   wire [07:00]	MemoryByte6_0B = MemoryBlock6[11];

+   wire [07:00]	MemoryByte6_0C = MemoryBlock6[12];

+   wire [07:00]	MemoryByte6_0D = MemoryBlock6[13];

+   wire [07:00]	MemoryByte6_0E = MemoryBlock6[14];

+   wire [07:00]	MemoryByte6_0F = MemoryBlock6[15];

+

+   wire [07:00]	MemoryByte7_00 = MemoryBlock7[00];

+   wire [07:00]	MemoryByte7_01 = MemoryBlock7[01];

+   wire [07:00]	MemoryByte7_02 = MemoryBlock7[02];

+   wire [07:00]	MemoryByte7_03 = MemoryBlock7[03];

+   wire [07:00]	MemoryByte7_04 = MemoryBlock7[04];

+   wire [07:00]	MemoryByte7_05 = MemoryBlock7[05];

+   wire [07:00]	MemoryByte7_06 = MemoryBlock7[06];

+   wire [07:00]	MemoryByte7_07 = MemoryBlock7[07];

+

+   wire [07:00]	MemoryByte7_08 = MemoryBlock7[08];

+   wire [07:00]	MemoryByte7_09 = MemoryBlock7[09];

+   wire [07:00]	MemoryByte7_0A = MemoryBlock7[10];

+   wire [07:00]	MemoryByte7_0B = MemoryBlock7[11];

+   wire [07:00]	MemoryByte7_0C = MemoryBlock7[12];

+   wire [07:00]	MemoryByte7_0D = MemoryBlock7[13];

+   wire [07:00]	MemoryByte7_0E = MemoryBlock7[14];

+   wire [07:00]	MemoryByte7_0F = MemoryBlock7[15];

+

+// -------------------------------------------------------------------------------------------------------

+//      2.02:  Write Data Buffer

+// -------------------------------------------------------------------------------------------------------

+

+   wire [07:00]	WriteData_0 = WrDataByte[00];

+   wire [07:00]	WriteData_1 = WrDataByte[01];

+   wire [07:00]	WriteData_2 = WrDataByte[02];

+   wire [07:00]	WriteData_3 = WrDataByte[03];

+   wire [07:00]	WriteData_4 = WrDataByte[04];

+   wire [07:00]	WriteData_5 = WrDataByte[05];

+   wire [07:00]	WriteData_6 = WrDataByte[06];

+   wire [07:00]	WriteData_7 = WrDataByte[07];

+   wire [07:00]	WriteData_8 = WrDataByte[08];

+   wire [07:00]	WriteData_9 = WrDataByte[09];

+   wire [07:00]	WriteData_A = WrDataByte[10];

+   wire [07:00]	WriteData_B = WrDataByte[11];

+   wire [07:00]	WriteData_C = WrDataByte[12];

+   wire [07:00]	WriteData_D = WrDataByte[13];

+   wire [07:00]	WriteData_E = WrDataByte[14];

+   wire [07:00]	WriteData_F = WrDataByte[15];

+

+

+// *******************************************************************************************************

+// **   TIMING CHECKS                                                           			**

+// *******************************************************************************************************

+

+   wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);

+

+   specify

+      specparam

+         tHI = 600,                                     // SCL pulse width - high

+         tLO = 1300,                                    // SCL pulse width - low

+         tSU_STA = 600,                                 // SCL to SDA setup time

+         tHD_STA = 600,                                 // SCL to SDA hold time

+         tSU_DAT = 100,                                 // SDA to SCL setup time

+         tSU_STO = 600,                                 // SCL to SDA setup time

+         tBUF = 1300;                                   // Bus free time

+

+      $width (posedge SCL, tHI);

+      $width (negedge SCL, tLO);

+

+      $width (posedge SDA &&& SCL, tBUF);

+

+      $setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);

+      $setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);

+      $setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);

+

+      $hold  (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);

+   endspecify

+

+endmodule

diff --git a/verilog/dv/caravel/user_proj_example/DFFRAM_beh.v b/verilog/dv/caravel/user_proj_example/DFFRAM_beh.v
new file mode 100644
index 0000000..3ef1c4e
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/DFFRAM_beh.v
@@ -0,0 +1,41 @@
+module DFFRAM_beh #( parameter COLS=1)
+(
+`ifdef USE_POWER_PINS
+    VPWR,
+    VGND,
+ `endif
+    CLK,
+    WE,
+    EN,
+    Di,
+    Do,
+    A
+);
+    localparam A_WIDTH = 8+$clog2(COLS);
+
+`ifdef USE_POWER_PINS
+    input VPWR;
+    input VGND;
+ `endif
+ 
+    input   wire            CLK;
+    input   wire    [3:0]   WE;
+    input   wire            EN;
+    input   wire    [31:0]  Di;
+    output  reg     [31:0]  Do;
+    input   wire    [(A_WIDTH - 1): 0]   A;
+
+    reg [31:0] RAM[(256*COLS)-1 : 0];
+    
+    always @(posedge CLK) 
+        if(EN) begin
+            Do <= RAM[A];
+            if(WE[0]) RAM[A][ 7: 0] <= Di[7:0];
+            if(WE[1]) RAM[A][15:8] <= Di[15:8];
+            if(WE[2]) RAM[A][23:16] <= Di[23:16];
+            if(WE[3]) RAM[A][31:24] <= Di[31:24];
+        end 
+        else
+            Do <= 32'b0;
+            
+endmodule
diff --git a/verilog/dv/caravel/user_proj_example/io_ports/Makefile b/verilog/dv/caravel/user_proj_example/io_ports/Makefile
index d6c2bf6..9b5a8d4 100644
--- a/verilog/dv/caravel/user_proj_example/io_ports/Makefile
+++ b/verilog/dv/caravel/user_proj_example/io_ports/Makefile
@@ -34,14 +34,14 @@
 
 hex:  ${PATTERN:=.hex}
 
-%.vvp: %_tb.v %.hex
+%.vvp: %_tb.v %.hex test.hex
 ifeq ($(SIM),RTL)
-	iverilog -DFUNCTIONAL -DSIM -I $(BEHAVIOURAL_MODELS) \
-	-I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+	iverilog -DFUNCTIONAL -DSIM -DFAST -DUSE_POWER_PINS -I $(BEHAVIOURAL_MODELS) \
+	-I $(PDK_PATH) -I .. -I $(RTL_PATH) \
 	$< -o $@
 else
-	iverilog -DFUNCTIONAL -DSIM -DGL -I $(BEHAVIOURAL_MODELS) \
-	-I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+	iverilog -DFUNCTIONAL -DSIM -DUSE_POWER_PINS  -DGL_UA -I $(BEHAVIOURAL_MODELS) \
+	-I $(PDK_PATH) -I .. -I $(VERILOG_PATH) -I $(RTL_PATH) \
 	$< -o $@
 endif
 
@@ -56,6 +56,9 @@
 	# to fix flash base address
 	sed -i 's/@10000000/@00000000/g' $@
 
+test.hex: ../sw/test.c ../sw/crt0.S ../sw/link.ld 
+	$(MAKE) -C ../sw/ all
+
 %.bin: %.elf
 	${GCC_PATH}/${GCC_PREFIX}-objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
 
@@ -63,5 +66,5 @@
 
 clean:
 	rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
-
+	rm -f ../sw/*.bin ../sw/*.hex ../sw/*.elf ../sw/*.lst
 .PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/io_ports/io_ports.c b/verilog/dv/caravel/user_proj_example/io_ports/io_ports.c
index a159f0a..52c5980 100644
--- a/verilog/dv/caravel/user_proj_example/io_ports/io_ports.c
+++ b/verilog/dv/caravel/user_proj_example/io_ports/io_ports.c
@@ -41,20 +41,78 @@
 
 	*/
 
-	// Configure lower 8-IOs as user output
-	// Observe counter value in the testbench
-	reg_mprj_io_0 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_1 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_2 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_3 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_4 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_5 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_6 =  GPIO_MODE_USER_STD_OUTPUT;
-	reg_mprj_io_7 =  GPIO_MODE_USER_STD_OUTPUT;
+	// GPIOs
+	reg_mprj_io_0 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_1 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_2 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_3 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_4 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_5 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_6 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_7 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
 
-        /* Apply configuration */
-        reg_mprj_xfer = 1;
-        while (reg_mprj_xfer == 1);
+	reg_mprj_io_8  =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_9  =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_10 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_11 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_12 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_13 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+
+	// Flash
+	reg_mprj_io_14 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_15 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_16 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_17 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+
+	// Flash CLK and Enable
+	reg_mprj_io_18 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_19 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	// UART 0
+	reg_mprj_io_20 =  GPIO_MODE_USER_STD_INPUT_NOPULL;
+	reg_mprj_io_21 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	// UART 1
+	reg_mprj_io_22 =  GPIO_MODE_USER_STD_INPUT_NOPULL;
+	reg_mprj_io_23 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	// SPI0
+	reg_mprj_io_24 =  GPIO_MODE_USER_STD_INPUT_NOPULL;
+	reg_mprj_io_25 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_26 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_27 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	// SPI1
+	reg_mprj_io_28 =  GPIO_MODE_USER_STD_INPUT_NOPULL;
+	reg_mprj_io_29 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_30 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_31 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	// I2C0
+	reg_mprj_io_32 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_33 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	
+	// I2C1
+	reg_mprj_io_34 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+	reg_mprj_io_35 =  GPIO_MODE_USER_STD_BIDIRECTIONAL;
+
+	// PWM
+	reg_mprj_io_36 =  GPIO_MODE_USER_STD_OUTPUT;
+	reg_mprj_io_37 =  GPIO_MODE_USER_STD_OUTPUT;
+
+	/* Apply configuration */
+	reg_mprj_xfer = 1;
+	while (reg_mprj_xfer == 1);
+
+	// Logic probes: configure LA[9:0] as outputs from the mgmt area
+	/*
+		LA      | EL2
+
+		la[1]   | NMI     | 1'b0
+		la[0]   | HResetn | 1'b1
+	*/
+	reg_la0_ena = 0x00000000;  
+	reg_la0_data = 0x01;
 
 }
 
diff --git a/verilog/dv/caravel/user_proj_example/io_ports/io_ports_tb.v b/verilog/dv/caravel/user_proj_example/io_ports/io_ports_tb.v
index c680a0b..0ce28f0 100644
--- a/verilog/dv/caravel/user_proj_example/io_ports/io_ports_tb.v
+++ b/verilog/dv/caravel/user_proj_example/io_ports/io_ports_tb.v
@@ -17,17 +17,85 @@
 
 `timescale 1 ns / 1 ps
 
-`include "caravel.v"
+`define   TEST_FILE   "../sw/test.hex" 
+`define   SIM_TIME    1500_000
+`define   SIM_LEVEL   0
+
+`define SOC_SETUP_TIME 800*2001
+
+`define NO_HC_CACHE
+
 `include "spiflash.v"
 
+`include "sst26wf080b.v"
+`include "23LC512.v"
+`include "caravel.v"
+
+`ifdef SIM
+
+`ifdef FAST
+    `define USE_DFFRAM_BEH
+    `define NO_HC_CACHE
+    `include "DFFRAM_beh.v"
+`else
+	`ifndef GL_UA
+		`include "user_project/IPs/DFFRAM_4K.v"
+		`include "user_project/IPs/DFFRAMBB.v"
+		`include "user_project/IPs/DMC_32x16.v"
+	`endif
+`endif
+
+`ifdef GL_UA
+	`include "gl/user_project/gl/apb_sys_0.v"
+	`include "gl/user_project/gl/DFFRAM_4K.v"
+	`include "gl/user_project/gl/DMC_32x16.v"
+	`include "gl/user_project/gl/el2_swerv_wrapper.v"
+	`include "gl/user_project/gl/user_project_wrapper.v"
+`else
+
+`include "user_project/AHB_sys_0/AHBlite_sys_0.v"
+`include "user_project/AHB_sys_0/AHBlite_bus0.v"
+`include "user_project/AHB_sys_0/AHBlite_GPIO.v"
+`include "user_project/AHB_sys_0/AHBlite_db_reg.v"
+
+`include "user_project/AHB_sys_0/APB_sys_0/APB_WDT32.v"
+`include "user_project/AHB_sys_0/APB_sys_0/APB_TIMER32.v"
+`include "user_project/AHB_sys_0/APB_sys_0/APB_PWM32.v"
+`include "user_project/AHB_sys_0/APB_sys_0/AHB_2_APB.v"
+`include "user_project/AHB_sys_0/APB_sys_0/APB_bus0.v"
+`include "user_project/AHB_sys_0/APB_sys_0/APB_sys_0.v"
+
+`include "user_project/IPs/TIMER32.v"
+`include "user_project/IPs/PWM32.v"
+`include "user_project/IPs/WDT32.v"
+`include "user_project/IPs/spi_master.v"
+`include "user_project/IPs/i2c_master.v"
+`include "user_project/IPs/GPIO.v"
+`include "user_project/IPs/APB_UART.v"
+`include "user_project/IPs/APB_SPI.v"
+`include "user_project/IPs/APB_I2C.v"
+`include "user_project/IPs/AHBSRAM.v"
+`include "user_project/acc/AHB_SPM.v"
+
+`include "user_project/IPs/QSPI_XIP_CTRL.v"
+`include "user_project/IPs/DMC_32x16.v"
+`include "user_project/IPs/RAM_1024x32.v"
+
+`include "user_project/el2_n5_soc_wrapper.v"
+`include "user_project/el2.v"
+`include "user_project/soc_core.v"
+
+`endif
+`endif
+
 module io_ports_tb;
 	reg clock;
-    	reg RSTB;
+    reg RSTB;
 	reg power1, power2;
 	reg power3, power4;
 
-    	wire gpio;
-    	wire [37:0] mprj_io;
+	wire gpio;
+	wire [37:0] mprj_io;
 	wire [7:0] mprj_io_0;
 
 	assign mprj_io_0 = mprj_io[7:0];
@@ -42,44 +110,70 @@
 		clock = 0;
 	end
 
+	// GPIO Loopback!
+    // wire [13:0] GPIO_PINS;
+    // generate
+    //     genvar i;
+    //     for(i=0; i<14; i=i+1)
+    //         assign GPIO_PINS[i] = GPIOOEN_Sys0_S2[i] ? GPIOOUT_Sys0_S2[i] : 1'bz;
+    // endgenerate
+
+    // assign GPIO_PINS[15:8] = GPIO_PINS[7:0];
+    // assign GPIOIN_Sys0_S2 = GPIO_PINS;
+
+	// Serial Terminal connected to UART0 TX*/
+    terminal term(.rx(mprj_io[21]));  // RsTx_Sys0_SS0_S0
+
+    // SPI SRAM connected to SPI0
+    wire SPI_HOLD = 1'b1;
+    M23LC512 SPI_SRAM(
+        .RESET(~RSTB),
+        .SO_SIO1(mprj_io[24]),  // MSI_Sys0_SS0_S2
+        .SI_SIO0(mprj_io[25]),  // MSO_Sys0_SS0_S2
+        .CS_N(mprj_io[26]),     // SSn_Sys0_SS0_S2
+        .SCK(mprj_io[27]),      // SCLK_Sys0_SS0_S2
+        .HOLD_N_SIO3(SPI_HOLD)
+	);
+
+	// I2C E2PROM connected to I2C0
+    wire    scl, sda;
+    delay   m0_scl (scl_oen_o_Sys0_SS0_S4 ? 1'bz : scl_o_Sys0_SS0_S4, scl),
+            m0_sda (sda_oen_o_Sys0_SS0_S4 ? 1'bz : sda_o_Sys0_SS0_S4, sda);
+
+    assign  scl_i_Sys0_SS0_S4 = scl;
+    assign  sda_i_Sys0_SS0_S4 = sda;
+    
+	pullup p1(scl); // pullup scl line
+	pullup p2(sda); // pullup sda line
+
+    M24LC16B I2C_E2PROM(
+        .A0(1'b0), 
+        .A1(1'b0), 
+        .A2(1'b0), 
+        .WP(1'b0), 
+        .SDA(sda), 
+        .SCL(scl), 
+        .RESET(~HRESETn)
+    );
+
+	initial begin
+		// Load the application into the N5 flash memory
+		#1  $readmemh(`TEST_FILE, flash.I0.memory);
+		$display("---------N5 Flash -----------");
+		$display("Memory[0]: %0d, Memory[1]: %0d, Memory[2]: %0d, Memory[3]: %0d", 
+            flash.I0.memory[0], flash.I0.memory[1], flash.I0.memory[2], flash.I0.memory[3]);
+	end
+
 	initial begin
 		$dumpfile("io_ports.vcd");
 		$dumpvars(0, io_ports_tb);
 
-		// Repeat cycles of 1000 clock edges as needed to complete testbench
-		repeat (25) begin
-			repeat (1000) @(posedge clock);
-			// $display("+1000 cycles");
-		end
-		$display("%c[1;31m",27);
-		$display ("Monitor: Timeout, Test Mega-Project IO Ports (RTL) Failed");
-		$display("%c[0m",27);
-		$finish;
-	end
-
-	initial begin
-	    // Observe Output pins [7:0]
-	    wait(mprj_io_0 == 8'h01);
-	    wait(mprj_io_0 == 8'h02);
-	    wait(mprj_io_0 == 8'h03);
-    	    wait(mprj_io_0 == 8'h04);
-	    wait(mprj_io_0 == 8'h05);
-            wait(mprj_io_0 == 8'h06);
-	    wait(mprj_io_0 == 8'h07);
-            wait(mprj_io_0 == 8'h08);
-	    wait(mprj_io_0 == 8'h09);
-            wait(mprj_io_0 == 8'h0A);   
-	    wait(mprj_io_0 == 8'hFF);
-	    wait(mprj_io_0 == 8'h00);
-
-	    $display("Monitor: Test 1 Mega-Project IO (RTL) Passed");
-	    $finish;
-	end
-
-	initial begin
 		RSTB <= 1'b0;
 		#2000;
 		RSTB <= 1'b1;	    // Release reset
+		#(`SOC_SETUP_TIME);
+		#(`SIM_TIME);
+	    $finish;
 	end
 
 	initial begin		// Power-up sequence
@@ -97,10 +191,6 @@
 		power4 <= 1'b1;
 	end
 
-	always @(mprj_io) begin
-		#1 $display("MPRJ-IO state = %b ", mprj_io[7:0]);
-	end
-
 	wire flash_csb;
 	wire flash_clk;
 	wire flash_io0;
@@ -129,7 +219,7 @@
 		.vssd2	  (VSS),
 		.clock	  (clock),
 		.gpio     (gpio),
-        	.mprj_io  (mprj_io),
+        .mprj_io  (mprj_io),
 		.flash_csb(flash_csb),
 		.flash_clk(flash_clk),
 		.flash_io0(flash_io0),
@@ -148,5 +238,33 @@
 		.io3()			// not used
 	);
 
+	/* N5 Flash */
+    sst26wf080b flash(
+        .SCK(mprj_io[18]),     // fsclk
+        .SIO(mprj_io[17:14]),  // fdo
+        .CEb(mprj_io[19])      // fcen
+    );
+
+endmodule
+
+module terminal #(parameter bit_time = 400) (input rx);
+
+    integer i;
+    reg [7:0] char;
+    initial begin
+        forever begin
+            @(negedge rx);
+            i = 0;
+            char = 0;
+            #(3*bit_time/2);
+            for(i=0; i<8; i=i+1) begin
+                char[i] = rx;
+                #bit_time;
+            end
+            $write("%c", char);
+        end
+    end
+
+
 endmodule
 `default_nettype wire
diff --git a/verilog/dv/caravel/user_proj_example/sst26wf080b.v b/verilog/dv/caravel/user_proj_example/sst26wf080b.v
new file mode 100755
index 0000000..895906a
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sst26wf080b.v
@@ -0,0 +1,2485 @@
+/*
+---------------------------------------------------------------------------
+   Proprietary & Confidental, All right reserved, 2013
+   Silicon Storage Technology, Inc.	
+---------------------------------------------------------------------------
+Description     : SQI Serial Flash Memory, SST26WF080B 1MB Memory
+Revision Status : 1.0
+---------------------------------------------------------------------------
+
+Important Notes Please Read
+---------------------------
+ (1) This model has several non-volatile memorys. 
+     These bits are initalized at time 0 to the default values. To 
+     load these memorys with customer data load them after time 0;
+     The below example loads the main flash memory from a file.
+     Example: initial #1 $readmemh(<path>.I0.memory,<file name>);       // 080=1M, 016=2M, 032=4M, 064=8M bytes memory
+
+ (2) This model has several non-volatial bits in the status registers. If you want them
+     set to a different initial condition then edit this model defparam commands to
+     change the values.
+
+ (3) For simulation purposes the erase times can be shortened using the 'defparam' command
+      do not shorten to less than 1uS
+      Example: defparam <path>.I0.Tbe = 25_000;   // reduce block erase time from 25mS to 25uS
+      Example: defparam <path>.I0.Tse = 25_000;   // reduce sector erase time from 25mS to 25uS
+      Example: defparam <path>.I0.Tsce = 25_000;  // reduce chip erase time from 50mS to 25uS
+      Example: defparam <path>.I0.Tpp = 25_000;   // reduce page program time from 1.5mS to 25uS
+      Example: defparam <path>.I0.Tws = 1000;     // reduce suspend to ready time from 10uS to 1uS
+
+ (4) On timing errors all of flash memory will be set to 'xx', timming errors are a fatal problem.
+
+ (5) Parameter MASK_ERRORS is used to inhibit corruption of memory on timing errors at the start of simulation.
+     This allows the unknows to be cleared before corrupting memory on timing checks. The default value is 500nS.
+     To change this value use defparam to increase it to the value you need.
+     Example: defparam <path>.MASK_ERRORS = 1000;	// increase time to 1uS
+
+ (6) This model uses a common module for all the SST26VF***B family of products. This
+     module is appended to the top level module code. The module name is "sst26wfxxxb".
+     If more than 1 instance of the SST26VF*B family of products is placed in a design
+     there will be a syntact error that module sst26wfxxxb is defined more than once.
+     if this happens delete the sst26wfxx module definitions from all but one of the Verilog models.
+
+ (7) The below code is the definition of the non-volatile memory used in the chip.
+
+//---------------------------------------------------------------------------
+// Define non-volatile Memory
+//---------------------------------------------------------------------------
+reg [7:0] memory[Memsize-1:0];          // define Flash memory array, non-volitable
+reg [7:0] security_id[(Kilo*2)-1:0];	// Define secutity ID memory addr 0->7 are SST space the rest is user space, non-volitable
+reg [7:0] SFDP[(Kilo*2)-1:0];		// serial flash discoverable parameters
+reg [PROTECT_REG_MSB:0] wlldr_mem;	// Define write-lock lock down reg, non-volitable
+reg WPEN;				// bit 7 of config register, non-volitable
+reg SEC;				// status[5] 1-bit Lockout Security ID, non-volitable
+
+
+
+------------------------------------------------------------------------------------
+ Pin Descriptions
+ Symbol    Pin Name   Type    Function
+-------    ------------ ------ -------------------------------------------
+ SCK       Serial Clock input  Provides clock to device
+ SIO[3:0]  Serial Data  I/O    provide data input and output
+ CEb       Chipenable   input  Active low chip enable
+------------------------------------------------------------------------------------
+*/
+
+`timescale 1ns / 10 ps
+module sst26wf080b(SCK,SIO,CEb);
+input SCK;              // device clock
+input CEb;              // chip enable active low
+inout [3:0] SIO;        // serial 4-bit bus I/O
+reg [31:0] error_cnt;	// count timing errors
+	parameter MASK_ERRORS=500;			// mask any timing errors before this time
+
+	defparam I0.Ksize = 0;  //Size of memory in Kilo bytes
+	defparam I0.Msize = 1;				// Size of memory in Mega bites. I.E. 8-bit field size, use S080=1, S016=2, S032=4
+	defparam I0.ADDR_MSB=19;			// most significant address bit, 32Mb=21,16Mb=20, 8Mb=19, 4Mb=18;
+	defparam I0.Memory_Capacity = 8'h58;		// ID read memory size 32M=52,16M=51,8M=58, JEDEC read value
+	// change below values if you need non-default values on POR
+	defparam I0.WLLD_value = 32'h0000_0000;	// init WLLD protection register non-volitale, default to all 0's
+	defparam I0.INIT_WPEN = 1'b0;			// value of WPEN, configuration register bit 7 default on POR
+	defparam I0.SECURITY_LOCKOUT_VALUE=1'b0;	// Value of SEC, status register bit 5 on POR
+
+	//--------------------------------------------------
+	// Place common sst26wfxxxB model
+	//--------------------------------------------------
+	sst26wfxxxb I0(SCK,SIO,CEb);
+
+	//--------------------------------------------------
+	// Timing checks
+	//--------------------------------------------------
+	wire HOLDb;
+	wire read_slow_flag;
+	wire read_dual_io_flag;
+	wire [3:0] Tds_inhibit;
+	reg notifer_Tsckh,notifer_Tsckl,notifier_Tces,notifier_Tceh,notifier_Tchs,notifier_Tchh;
+	reg notifier_Tcph,notifier_Tds;
+	reg notifier_Fclk;
+	reg notifier_Thls,notifier_Thhs,notifier_Thhh;
+	assign HOLDb = I0.HOLDb;			// bring up lower level holdb net
+	assign read_slow_flag = I0.read_slow_flag;	// slow timing on SPI read flag I.E. Read cmd '03h'
+	assign read_dual_io_flag = I0.SPI_SDIOR_active;	// slow timing on SPI dual I/O read, i.e. cmd 'BBh'
+	assign Tds_inhibit[3] = I0.SIO_IO[3] | CEb;	// i/o = input and CEb active
+	assign Tds_inhibit[2] = I0.SIO_IO[2] | CEb;	// i/o = input and CEb active
+	assign Tds_inhibit[1] = I0.SIO_IO[1] | CEb;	// i/o = input and CEb active
+	assign Tds_inhibit[0] = I0.SIO_IO[0] | CEb;	// i/o = input and CEb active
+	specify
+		specparam CellType = "SST26WFxxx";
+
+		specparam Fclk_slow = 24.99;		// Min serial clock period during '03h' Read command
+		specparam Tsckh_slow = 11.0;		// Min serial clock high time 'SCK' during '03h' Read command
+		specparam Tsckl_slow = 11.0;		// Min serial clock low time 'SCK' during '03h' Read command
+
+		specparam Fclk_dual_io = 12.49;		// Min serial clock period during 'BBh' Read command
+		specparam Tsckh_dual_io = 5.5;		// Min serial clock high time 'SCK' during 'BBh' Read command
+		specparam Tsckl_dual_io = 5.5;		// Min serial clock low time 'SCK' during 'BBh' Read command
+
+		specparam Fclk = 9.59;			// Min serial clock period
+		specparam Tsckh = 4.5;			// Min serial clock high time 'SCK'
+		specparam Tsckl = 4.5;			// Min serial clock low time 'SCK'
+		specparam Tces = 5;			// CEb falling to SCK rising setup time
+		specparam Tceh = 5;			// SCK rising to CEb rising hold time
+		specparam Tchs = 5;			// CEb not active setup time
+		specparam Tchh = 5;			// CEb not active hold time
+		specparam Tcph = 12.0;			// Min CEb high time
+		specparam Tds = 3;			// Data in setup time to SCK rising
+		specparam Tdh = 4;			// Data in hold time to SCK rising
+		specparam Thls = 5.0;			// HOLDb falling to SCK rising setup
+		specparam Thhs = 5.0;			// HOLDb rising to SCK risinf setup
+		specparam Thhh = 5.0;			// SCK to HOLDb hold time
+
+		// HOLDb timing tests
+		$setup(posedge SCK ,negedge HOLDb,Thls, notifier_Thls);
+		$setup(posedge SCK ,posedge HOLDb,Thhs, notifier_Thhs);
+		$hold (posedge SCK ,negedge HOLDb,Thhh, notifier_Thhh);
+
+
+		// 40Mhz, slow speed read timing checks opcode Read '03h' in SPI mode
+		$period(posedge SCK &&& read_slow_flag==1'b1 ,Fclk_slow,notifier_Fclk);
+		$period(negedge SCK &&& read_slow_flag==1'b1 ,Fclk_slow,notifier_Fclk);
+		$width(negedge SCK &&& read_slow_flag==1'b1,Tsckh_slow,0,notifer_Tsckh);
+		$width(posedge SCK &&& read_slow_flag==1'b1,Tsckl_slow,0,notifer_Tsckl);
+
+		// 80Mhz, read timing checks opcode Read 'BBh' in SPI mode
+		$period(posedge SCK &&& read_dual_io_flag==1'b1 ,Fclk_dual_io,notifier_Fclk);
+		$period(negedge SCK &&& read_dual_io_flag==1'b1 ,Fclk_dual_io,notifier_Fclk);
+		$width(negedge SCK &&& read_dual_io_flag==1'b1,Tsckh_dual_io,0,notifer_Tsckh);
+		$width(posedge SCK &&& read_dual_io_flag==1'b1,Tsckl_dual_io,0,notifer_Tsckl);
+
+		// 104 Mhz timing
+		$period(posedge SCK &&& CEb==1'b0 ,Fclk,notifier_Fclk);
+		$period(negedge SCK &&& CEb==1'b0 ,Fclk,notifier_Fclk);
+		$width(negedge SCK,Tsckh,0,notifer_Tsckh);
+		$width(posedge SCK,Tsckl,0,notifer_Tsckl);
+		$setup(negedge CEb,posedge SCK,Tces, notifier_Tces);
+		$setup(negedge CEb,posedge SCK,Tchh, notifier_Tchh);
+		$hold (posedge SCK,posedge CEb,Tceh, notifier_Tceh);
+		$setup(posedge SCK,posedge CEb,Tchs, notifier_Tchs);
+		$width(posedge CEb,Tcph,0,notifier_Tcph);
+		$setuphold(posedge SCK &&& Tds_inhibit[3]==1'b0, SIO[3],Tds,Tdh,notifier_Tds);
+		$setuphold(posedge SCK &&& Tds_inhibit[2]==1'b0, SIO[2],Tds,Tdh,notifier_Tds);
+		$setuphold(posedge SCK &&& Tds_inhibit[1]==1'b0, SIO[1],Tds,Tdh,notifier_Tds);
+		$setuphold(posedge SCK &&& Tds_inhibit[0]==1'b0, SIO[0],Tds,Tdh,notifier_Tds);
+	endspecify
+
+always @(notifier_Thls or notifier_Thhs or notifier_Thhh) begin
+        if($realtime > MASK_ERRORS ) begin
+                $display("\t%m Fatal Timing Error for SIO[3] HOLDb timing to SCK rising time=%0.2f",$realtime);
+                corrupt_all;            // corrupt memory
+        end
+end
+
+always @(notifier_Fclk) begin
+        if($realtime > MASK_ERRORS ) begin
+                $display("\t%m Fatal Timing Error Fclk time=%0.2f",$realtime);
+                corrupt_all;            // corrupt memory
+        end
+end
+
+
+always @(notifier_Tds) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tds/Tdh time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+always @(notifier_Tcph) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tcph time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+always @(notifier_Tchh) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tchh time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+always @(notifier_Tchs) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tchs time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+always @(notifier_Tceh) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tceh time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+always @(notifier_Tces) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tces time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+
+always @( notifer_Tsckh or notifer_Tsckl) begin
+	if($realtime > MASK_ERRORS ) begin
+		$display("\t%m Fatal Timing Error Tsckh or Tsckl CEb width error time=%0.2f",$realtime);
+		corrupt_all;		// corrupt memory
+	end
+end
+
+//---------------------------------------------------------------------------
+// corrupt all of memory on timing error
+//---------------------------------------------------------------------------
+task corrupt_all;
+reg [31:0] nn;
+begin
+	error_cnt = error_cnt + 1;	// count the number of timing errors
+	$display("\t%m Fatal Error all of memory being set to 'xx' error_cnt=%0d",error_cnt);
+        for(nn=0;nn<I0.Memsize;nn=nn+16) begin
+                I0.memory[nn+0] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+1] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+2] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+3] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+4] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+5] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+6] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+7] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+8] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+9] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+10] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+11] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+12] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+13] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+14] = 8'hxx; // clear main memory to 'xx' on error
+                I0.memory[nn+15] = 8'hxx; // clear main memory to 'xx' on error
+        end
+end
+endtask
+
+endmodule
+
+/* -------------------------------------------------------------------------------
+            Proprietary & Confidental, All right reserved, 2010
+            Silicon Storage Technology, Inc. a subsidiary of Microchip
+----------------------------------------------------------------------------------
+This is the common model for the SST26WFxxxB family of chips
+This model is configured using defparam commands from the chip level module
+This is a serial FLASH memory that can be configured for SPI or SQI bus formats.
+----------------------------------------------------------------------------------
+Block memory definisions
+----------------------------------------------------------------------------------
+top    4 * 8K parameter block
+---    1 * 32K block
+---    64k blocks
+---    ...
+---    ...
+---    64k blocks
+---    1  * 32K block
+bottom 4 * 8K parameter block
+---------------------------------------------------------------------------
+
+ Pin Descriptions
+ Symbol    Pin Name         Type       Function
+-------    ------------     ------    -------------------------------------------
+ SCK       Serial Clock     input     Provides clock to device
+ SIO[3:0]  Serial Data      I/O       Provide data input and output, QUAD/SPI mode
+            SIO[0] = SPI    I/O       SIO[0]/SI
+            SIO[1] = SPI    I/O       SIO[1]/SO
+            SIO[2] Quad     I/O       SIO[2]/WPb
+            SIO[3] Quad     I/O       SIO[3]/HOLDb
+ CEb       Chip Enable      input     Active low chip enable
+------------------------------------------------------------------------------- */
+
+`timescale 1ns / 10 ps
+module sst26wfxxxb(SCK,SIO,CEb);
+input SCK;              // device clock
+input CEb;              // chip enable active low
+inout [3:0] SIO;        // serial 4-bit bus I/O
+
+parameter True = 1'b1;
+parameter False = 1'b0;
+parameter Kilo = 1024;                  // size of kilo 2^10
+parameter Mega = (Kilo * Kilo);         // Size of mega 2^20
+parameter S080B=1;                      // size of SST26WF080B 1M bytes
+parameter S016B=2;                      // size of SST26WF016B 2M bytes
+parameter S032B=4;                      // size of SST26WF032B 4M bytes
+parameter S040B=0;	//For memory size less than 1M
+
+//--------------------------------------------------------------------------------
+// change below parameters to change Memory size or init non-volitale registers
+//--------------------------------------------------------------------------------
+parameter Ksize = 0;
+parameter Msize = S032B;                			// Size of Memory in Mega bites. use S080B, S016B, S032B
+parameter Memsize = (Msize * Mega ) + (Ksize * Kilo);    			// Size of Memory in bytes
+parameter ADDR_MSB=21;                  			// most significant address bit, 32Mb=21,16Mb=20, 8Mbit=19
+parameter INIT_WPEN = 1'b0;					// For Configuration Register bit[7]/WPEN default value on por
+parameter PROTECT_REG_MSB = ((Memsize/(Kilo*64))+(16-1));	// MSB of protection register, 1M=31, 2M=47, 4M=79
+parameter WLLD_value = 80'h0000_0000_0000_0000_0000;		// WLLD non-Volatile memory initial contents
+parameter SECURITY_LOCKOUT_VALUE=1'b0;				// value of security lockout bit on default startup, SEC status[5]
+parameter Memory_Capacity=8'h52;				// JEDEC device ID 32B=52,16B=51,080B=58
+
+`protect
+//---------------------------------------------------------------------------
+parameter MANUFACTURE=8'hBF;            // JEDEC manufacture ID for SST
+parameter Memory_Type = 8'h26;          // JEDEC define Memory type
+parameter Sector_MSB=11;                // 4k sector size MSB sector address = [11:0]
+parameter Block08k_MSB=12;		// MSB of 8K blocks
+parameter Block32k_MSB=14;		// MSB of 32K blocks
+parameter Block64k_MSB=15;		// MSB of 64K blocks
+parameter SST_SEC_ID_LSB='h18;          // bottom address of SST security id space that  cannot be written
+parameter ID_MSB=4;                     // MSB bit of security ID address max address 'h1F
+parameter Burst8_MSB=2;			// Burst MSB bit
+parameter Burst16_MSB=3;		// Burst MSB bit
+parameter Burst32_MSB=4;		// Burst MSB bit
+parameter Burst64_MSB=5;		// Burst MSB bit
+parameter Burst8=8'h00;			// burst values
+parameter Burst16=8'h01;		// burst values
+parameter Burst32=8'h02;		// burst values
+parameter Burst64=8'h03;		// burst values
+
+parameter AF_MSB = 23;			// MSB of serial address field [23:0] i.e. 3 bytes of address data
+parameter Sector_Size = (4 * Kilo);     // sector size;
+parameter Block_64k = (64*Kilo);        // normal block size
+parameter Block_32k=(32*Kilo);          // 32K->64K Memory space/top-32K-->top-64K Memory space
+parameter Block_08k=(8*Kilo);           // top/bottom 32k blocks of Memory are in 8K blocks
+parameter Program_Page_Size=(Kilo/4);   // program page size 256 bytes
+
+
+//---------------------------------------------------------------------------
+// SPI opcodes 
+//---------------------------------------------------------------------------
+parameter SPI_NOP		= 8'h00;	// NOP command
+parameter SPI_RSTEN		= 8'h66;	// Reset Enable
+parameter SPI_RST		= 8'h99;	// Reset Chip
+parameter SPI_EQIO		= 8'h38;	// Enable QUAD I/O
+parameter SPI_RSTQIO		= 8'hFF;	// Restet QUAD I/O
+parameter SPI_RDSR		= 8'h05;	// Read Status Register
+parameter SPI_WRSR		= 8'h01;	// Write Status Register
+parameter SPI_RDCR		= 8'h35;	// Read Configuration Register
+parameter SPI_READ		= 8'h03;	// 50Mhz Read of Memory
+parameter SPI_HS_READ		= 8'h0B;	// High Speed Read of Memory 80Mhz
+parameter SPI_QUAD_READ		= 8'h6B;	// SPI QUAD Output Read
+parameter SPI_QUAD_IO_READ	= 8'hEB;	// SPI QUAD I/O Read
+parameter SPI_SDOR		= 8'h3B;	// SPI DUAL Output Read
+parameter SPI_SDIOR		= 8'hBB;	// SPI DUAL I/O Read
+parameter SPI_SB		= 8'hC0;	// Set Burst Length
+parameter SPI_RBSPI		= 8'hEC;	// SPI nB Burst with Wrap
+parameter SPI_JEDEC_ID		= 8'h9F;	// Jedec ID Read
+parameter SPI_SFDP		= 8'h5A;	// Serial Flash Discoverable Parameters
+parameter SPI_WREN		= 8'h06;	// Write Enable
+parameter SPI_WRDI		= 8'h04;	// Write Disable
+parameter SPI_SE		= 8'h20;	// Sector Erase 4K bytes size
+parameter SPI_BE		= 8'hD8;	// Block Erase 64K,32K,8K erase
+parameter SPI_CE		= 8'hC7;	// Chip Erase
+parameter SPI_PP		= 8'h02;	// Page Program SIO[1] bit used as data in, SIO[0] used as data out
+parameter SPI_QUAD_PP		= 8'h32;	// SPI QUAD Page Program
+parameter SPI_WRSU		= 8'hB0;	// Suspend Program/Erase
+parameter SPI_WRRE		= 8'h30;	// Resume Program/Erase
+parameter SPI_RBPR		= 8'h72;	// Read Block Protection Register
+parameter SPI_WBPR		= 8'h42;	// Write Block Protection Register
+parameter SPI_LBPR		= 8'h8D;	// Lock Down Block Protection Register
+parameter SPI_nVWLDR		= 8'hE8;	// Non-Volatile Write Lock down Register
+parameter SPI_ULBPR		= 8'h98;	// Global Block Protection Unlock
+parameter SPI_RSID		= 8'h88;	// Read Security ID
+parameter SPI_PSID		= 8'hA5;	// Program User Security ID Area
+parameter SPI_LSID		= 8'h85;	// Lock Out security ID Programing
+parameter SPI_DPD		= 8'hB9;	// Enable Deep Power Down mode
+parameter SPI_DPD_RST		= 8'hAB;	// Disable Deep Power Down mode; output device ID
+
+//---------------------------------------------------------------------------
+// SQI opcodes 
+//---------------------------------------------------------------------------
+parameter SQI_NOP 		= SPI_NOP;	// NOP command
+parameter SQI_RSTEN		= SPI_RSTEN;	// Reset Enable
+parameter SQI_RST		= SPI_RST;	// Reset Chip
+parameter SQI_RSTQIO		= SPI_RSTQIO;	// Restet QUAD I/O
+parameter SQI_RDSR		= SPI_RDSR;	// Read Status Register
+parameter SQI_WRSR		= SPI_WRSR;	// Write Status Register
+parameter SQI_RDCR		= SPI_RDCR;	// Read Configuration Register
+parameter SQI_HS_READ		= SPI_HS_READ;	// High Speed Read of Memory 80Mhz
+parameter SQI_SB		= SPI_SB;	// Set Burst Length
+parameter SQI_RBSQI		= 8'h0C;	// SQI nB Burst with Wrap
+parameter SQI_J_ID		= 8'hAF;	// Quad I/O J-ID Read
+parameter SQI_WREN		= SPI_WREN;	// Write Enable
+parameter SQI_WRDI		= SPI_WRDI;	// Write Disable
+parameter SQI_SE		= SPI_SE;	// Sector Erase 4K bytes size
+parameter SQI_BE		= SPI_BE;	// Block Erase 64K,32K,8K erase
+parameter SQI_CE		= SPI_CE;	// Chip Erase
+parameter SQI_PP		= SPI_PP;	// Page Program SIO[1] bit used as data in, SIO[0] used as data out
+parameter SQI_WRSU		= SPI_WRSU;	// Suspend Program/Erase
+parameter SQI_WRRE		= SPI_WRRE;	// Resume Program/Erase
+parameter SQI_RBPR		= SPI_RBPR;	// Read Block Protection Register
+parameter SQI_WBPR		= SPI_WBPR;	// Write Block Protection Register
+parameter SQI_LBPR		= SPI_LBPR;	// Lock Down Block Protection Register
+parameter SQI_nVWLDR		= SPI_nVWLDR;	// Non-Volatile Write Lock down Register
+parameter SQI_ULBPR		= SPI_ULBPR;	// Global Block Protection Unlock
+parameter SQI_RSID		= SPI_RSID;	// Read Security ID
+parameter SQI_PSID		= SPI_PSID;	// Program User Security ID Area
+parameter SQI_LSID		= SPI_LSID;	// Lock Out security ID Programing
+parameter SQI_DPD		= SPI_DPD;	// Enable Deep Power Down mode
+parameter SQI_DPD_RST		= SPI_DPD_RST;	// Disable Deep Power Down mode; output device ID
+
+`endprotect
+//---------------------------------------------------------------------------
+// Define Timings
+// You can use defparam to change the erase/program times to a shorter value.
+// This may make your simulation run faster Tws, Tse, Tbe, Tpp, minimun value 1000,
+// Tsce min value 2000
+//---------------------------------------------------------------------------
+parameter Tv = 5;               // Output valid from SCK falling
+parameter Tclz = 0;             // SCK low to low-z output
+parameter Tchz = 12.5;          // Chip enable inactive to SIO z-stated
+parameter Tse = 25_000_000;     // Sector erase time 25mS
+parameter Tbe = 25_000_000;     // Block erase 25mS
+parameter Tsce = 50_000_000;    // chip erase time 50mS
+parameter Tpp = 1_500_000;      // page program time 1.5mS
+parameter Tws = 10_000;         // suspend to ready time 10uS
+parameter Tpsid = 200_000;      // Program Security ID time
+parameter Tre = 1_000_000;	// reset recovery when reset during program/erase
+parameter Trp = 100_000;	// reset recovery when reset during program/erase
+parameter Thz = 7.0;		// HOLD falling to SO z-state
+parameter Tlz = 7.0;		// HOLD rising to SO not z-state
+parameter Tsbr = 10_000;	// recovery from Deep Power Down mode
+
+//---------------------------------------------------------------------------
+// Define non-volatile Memory
+//---------------------------------------------------------------------------
+reg [7:0] memory[Memsize-1:0];          // define Flash Memory array, non-volitable, 1, 2 or 4 Megabytes
+reg [7:0] security_id[(Kilo*2)-1:0];	// Define secutity ID Memory addr 0->7 are SST space the rest is user space, non-volitable
+reg [7:0] SFDP[(Kilo*2)-1:0];		// serial flash discoverable parameters
+reg [PROTECT_REG_MSB:0] wlldr_mem;	// Define write-lock lock down reg, non-volitable, [31:0], [47:0], [79:0]
+reg WPEN;				// (bit 7 WPEN) of configuration register, non-volitable
+reg SEC;				// (status[5] SEC), 1-bit Lockout Security ID, non-volitable
+
+`protect
+//---------------------------------------------------------------------------
+// reg/wire definishions
+//---------------------------------------------------------------------------
+reg [PROTECT_REG_MSB:0] t_wlldr_mem;		// Define temp storage, write-lock lock down reg, non-volitable
+reg [PROTECT_REG_MSB:0] protect;		// protection register definishion max size for 32M-bit
+wire [PROTECT_REG_MSB:0] protect_or;		// combine protection bits protect | wlldr_mem
+wire [7:0] status,config_reg;			// status, configuration  register
+wire BUSY;					// status reg bit 0,7 active high busy signal, program or erase in progress
+reg RES;					// reserved status bit
+reg WPLD;					// Write protection lock down, status bit 4
+reg WSP;                                	// Program Suspend status, Status Register
+reg WEL;					// status bit 1
+reg WSE;					// status bit 2, write suspend erase status, 1= suspend in progress
+reg IOC;					// config[1]
+reg DPD;
+wire BPNV;					// config[3], Block Protection Volatility State
+reg PE;						// config[5]
+reg EE;						// config[6]
+reg RSTEN;					// enable reset command
+reg [7:0] pmem[Program_Page_Size-1:0];		// storage for program Memory 256 bytes
+reg [7:0] x_pmem[Program_Page_Size-1:0];	// tmp storage for program Memory 256 bytes
+reg [7:0] s_pmem[Program_Page_Size-1:0];	// save suspended data here
+reg read_slow_flag;				// timing check flag for 40Mhz read
+reg [3:0] SIO_IO;                       	// True if outputing data on SIO[3:0]
+reg [3:0] SIO_OUT;                      	// output data for SIO
+reg [31:0] cnt;					// generic counter for loops
+reg clock;                              	// internal SCK
+reg [7:0] spi_count,sqi_count;                 	// SPI clock counter
+reg [7:0] sqi_cmd,spi_cmd,l_spi_cmd;		// command input storage
+reg [7:0] RSTQIO_cmd;				// storage for the SPI RSTQIO command in SQI mode
+reg [7:0] l_sqi_cmd;				// latched sqi command
+wire suspend_act;                        	// True if in suspend mode
+reg SPI_SDIOR_active;				// loop active
+reg SPI_SDOR_active;				// loop active
+reg SQI_HS_READ_active;				// SQI high speed read loop active
+reg SPI_WRSU_active;				// suspend active flag
+reg SPI_nVWLDR_active;				// loop active
+reg SPI_WRSR_PGM_active;			// used to set busy on config reg program
+reg SPI_WRSR_active;				// loop active
+reg SPI_LSID_active;				// loop active
+reg SPI_PSID_active;				// spi PSID loop active
+reg SPI_PSID_ip;				// security programing in progress
+reg SPI_RSID_active;				// spi read security id active
+reg SPI_DPD_RST_RDID_active;			// loop active
+reg erase_active;				// SE,BE,CE erase actice
+reg erase_ip;					// SE,BE,CE erase in progress
+reg SPI_QUAD_PP_active;				// loop active
+reg SPI_RDCR_active;				// loop active
+reg SQI_PP_active;				// loop active
+reg SPI_PP_active;				// loop active
+reg SPI_RBPR_active;				// loop active
+reg SPI_WBPR_active;				// loop active flag
+reg SPI_nVWLDR_cmd_active;			// loop active
+reg SPI_RDSR_active;				// loop active
+reg SPI_SFDP_active;				// loop active
+reg SPI_JEDEC_ID_active;			// loop active
+reg SPI_RBSPI_active;				// loop active flag
+reg SPI_SB_active;				// loop active flag
+reg SPI_READ_active,SPI_READ_QUAD_active;	// read loop is active ir True
+reg SPI_QUAD_IO_READ_active;			// loop active
+reg SQI_SPI_mode; 	                     	// True = QUAD mode False=SPI mode
+reg [7:0] Mode_Configuration;           	// configuration data for continious read with no opcode
+reg [7:0] burst_length;                 	// burst length  00=8bytes, 01=16bytes, 02=32bytes, 03=64bytes
+wire WBPR_protection_lck;			// table 2 for WBPR cammond
+wire WPb;					// write protect bar signal
+reg valid_addr;					// program address valid, address was read in 
+reg valid_data;					// program data valid, i.e. at least 1 clock of data
+reg [AF_MSB:0] pgm_addr;			// program address 
+reg [15:0] pgm_id_addr;				// program security id address 
+reg [AF_MSB:0] erase_addr;			// erase address 
+reg [31:0] erase_size,resume_size;		// erase size 
+reg [AF_MSB:0] suspend_addr,resume_addr;	// save program address on suspend
+real start_erase;				// start time of erase or program
+real s_time_left;				// save time left for nested suspends
+real save_erase_time;				// on suspend save realtime in here
+real erase_time;				// time needed to erase sector/block/chip
+real time_left;					// time left for program/erase to finish
+reg page_program_active;			// page program is active
+reg [7:0] wsr_sreg,wsr_creg;			// tmp data storage for write status/configuration registers
+wire CONFIG_protection_lck;			// configuration write protect 
+wire write_protect;
+reg CE_flag,BE_flag,SE_flag;			// erase type flags 
+reg s_BE_flag,s_SE_flag;			// flags saved for suspend 
+wire HOLDb, HOLDb_IO;				// composit hold control siginals
+reg clockx;					// internal clock
+reg pgm_sus_reset;
+
+event reset;					// Trigger reset block
+event SPI_READ_trg;				// Start spi read operation 50Mhz
+event SPI_QUAD_READ_trg;			// Start SPI QUAD read
+event SPI_QUAD_IO_READ_trg;			// Start SPI QUAD IO READ	
+event SPI_SB_trg;				// Start SPI Set Burst Count
+event SPI_RBSPI_trg;				// Start SPI Burst Read
+event SPI_JEDEC_ID_trg;				// Start JEDEC_ ID read
+event SPI_SFDP_trg;				// start SFDP , Serial Flash Discoverable Parameters
+event SPI_RDSR_trg;				// Read Status register
+event SPI_WBPR_trg;				// Trigger write block protection register
+event SPI_RBPR_trg;				// Read block protection register
+event SPI_PP_trg;				// Page program trigger
+event SPI_RDCR_trg;				// Start read of Configuration Register
+event SPI_QUAD_PP_trg;				// Start SPI QUAD page write
+event SPI_SE_trg;				// Start sector erase
+event SPI_BE_trg;				// Start block erase
+event SPI_CE_trg;				// Start Chip erase
+event SPI_RSID_trg;				// Spi read security id
+event SPI_PSID_trg;				// Spi program user security id space
+event SPI_LSID_trg;				// Security ID lockout
+event SPI_DPD_RST_trg;				// Deep Power Down Reset
+event SPI_WRSR_trg;				// Write status register
+event SPI_nVWLDR_trg;				// Write non-volatile block protection register
+event SPI_ULBPR_trg;				// Trigger Global Block Protection Unlock
+event SPI_WRSU_trg;				// Enter suspend mode
+event SPI_WRRE_trg;				// Exit suspend mode , resume
+event SQI_HS_READ_trg;				// SQI high speed read trigger
+event SPI_SDOR_trg;				// Spi dual output read
+event SPI_SDIOR_trg;				// Dual I/O read
+event SPI_LBPR_trg;				// Lock down block protection reg
+event SPI_EQIO_trg;				// Enable Quad I/O
+
+//---------------------------------------------------------------------------
+// Status/Configuration register definisions, Protection logic
+//---------------------------------------------------------------------------
+assign status = {BUSY,RES,SEC,WPLD,WSP,WSE,WEL,BUSY};		// create status register
+assign config_reg = {WPEN,EE,PE,1'b0,BPNV,1'b0,IOC,1'b0};	// create configuration register
+assign BUSY = page_program_active | erase_ip | SPI_LSID_active | SPI_WRSR_PGM_active | SPI_nVWLDR_active
+		| pgm_sus_reset;	
+assign BPNV = ~(|wlldr_mem);					// block protection volatility state, configuration reg[3]
+assign WPb = SIO[2];						// rename SIO[2] to WPb for code clairity
+assign protect_or = protect | wlldr_mem;			// combine non-volital protect with volital protect
+assign suspend_act = (WSE | WSP);				// suspend active net
+
+//---------------------------------------------------------------------------
+// write protection SPI mode configuration reg write only, protection = True
+// Check Table 2 lines 2,4,5,6
+//---------------------------------------------------------------------------
+assign CONFIG_protection_lck = (SQI_SPI_mode===True) ? False : (~WPb & ~IOC & WPEN);
+
+assign write_protect = ~SQI_SPI_mode & ~IOC & WPEN & ~WPb;	
+assign WBPR_protection_lck = (SQI_SPI_mode===True) ? WPLD : WPLD | write_protect;
+
+//---------------------------------------------------------------------------
+// I/O assignments
+//---------------------------------------------------------------------------
+assign SIO[0] = (SIO_IO[0]===True && HOLDb_IO===1'b1) ? SIO_OUT[0] : 1'bz;         // output data on SIO ? SI
+assign SIO[1] = (SIO_IO[1]===True && HOLDb_IO===1'b1) ? SIO_OUT[1] : 1'bz;         // output data on SIO ? SO
+assign SIO[2] = (SIO_IO[2]===True)                    ? SIO_OUT[2] : 1'bz;         // output data on SIO ?
+assign SIO[3] = (SIO_IO[3]===True)                    ? SIO_OUT[3] : 1'bz;         // output data on SIO ?
+
+//---------------------------------------------------------------------------
+// HOLD# setup
+//---------------------------------------------------------------------------
+assign HOLDb = CEb | IOC | SIO[3] | SQI_SPI_mode ;	// no timing HOLDb
+assign #(Tlz,Thz) HOLDb_IO = HOLDb;			// I/O control timing HOLDb signal
+
+//-------------------------------------------------------
+// Generate internal clock
+//-------------------------------------------------------
+always @(SCK) if(CEb === 1'b0) clockx = SCK;
+              else if (CEb === 1'b1 ) clockx = 1'b0;
+              else clockx = 1'bx;
+
+always @(posedge clockx) if(HOLDb===1'b1) clock = clockx; else  clock = 1'b0;
+always @(negedge clockx) clock = clockx;
+
+//-------------------------------------------------------
+// Define begining of command operation
+//-------------------------------------------------------
+always @( negedge CEb ) begin
+	spi_count = 0;			// clear spi clock counter
+	sqi_count = 0;			// clear sqi clock counter
+	spi_cmd = SPI_NOP;		// clear SPI command register
+	sqi_cmd = SQI_NOP;		// clear SQI command register
+	RSTQIO_cmd = SPI_NOP;		// clear command
+end
+
+//-------------------------------------------------------
+// Terminate still runnung named blocks on CEb inactive
+//-------------------------------------------------------
+always @( posedge CEb ) begin
+      	SIO_IO <= #Tchz  {False,False,False,False};    	// Turn off IO control SIO[3:0]
+	#0 if(SPI_READ_active==True) begin
+		disable SPI_READ_label;
+		SPI_READ_active = False;		// read loop is inactive
+		read_slow_flag = False;			// set timing checks back to normal
+	end
+	if(SPI_READ_QUAD_active==True) begin
+		disable SPI_QUAD_READ_label;
+		SPI_READ_QUAD_active = False;
+	end
+	if(SPI_QUAD_IO_READ_active===True) begin
+		disable SPI_QUAD_IO_READ_label;
+		SPI_QUAD_IO_READ_active = False;
+	end
+	if(SPI_SB_active===True) begin
+		disable SPI_SB_label;
+		SPI_SB_active = False;
+	end
+	if(SPI_RBSPI_active===True) begin
+		disable SPI_RBSPI_label;
+		SPI_RBSPI_active = False;
+	end
+	if(SPI_JEDEC_ID_active===True) begin
+		disable SPI_JEDEC_ID_label;
+		SPI_JEDEC_ID_active = False;
+	end
+	if(SPI_SFDP_active===True) begin
+		disable SPI_SFDP_label;
+		SPI_SFDP_active = False;
+	end
+	if(SPI_RDSR_active===True) begin
+		disable SPI_RDSR_label;
+		SPI_RDSR_active = False;
+	end
+	if(SPI_RDCR_active===True) begin
+		disable SPI_RDCR_label;
+		SPI_RDCR_active = False;
+	end
+	if(SPI_RBPR_active===True) begin
+		disable SPI_RBPR_label;
+		SPI_RBPR_active = False;
+	end
+	if(SPI_RSID_active ===True) begin
+		disable SPI_RSID_label;
+		SPI_RSID_active = False;
+	end
+	if(SPI_WBPR_active ===True) begin
+		disable SPI_WBPR_label;
+		SPI_WBPR_active = False;
+	end
+	if(SQI_HS_READ_active===True) begin
+		disable SQI_HS_READ_label;
+		SQI_HS_READ_active = False;
+	end
+	if(SPI_SDOR_active===True) begin
+		disable SPI_SDOR_label;
+		SPI_SDOR_active = False;
+	end
+	if(SPI_SDIOR_active ===True) begin
+		disable SPI_SDIOR_label;
+		SPI_SDIOR_active = False;
+	end
+	if(SPI_DPD_RST_RDID_active ===True) begin
+		disable SPI_DPD_RST_RDID_label;
+		SPI_DPD_RST_RDID_active = False;
+	end
+end
+
+//-----------------------------------------------------------
+// Read in hex command stream,  SQI mode commands
+//-----------------------------------------------------------
+always @( posedge clock && SQI_SPI_mode === True) begin
+	if(BUSY===False && Mode_Configuration[7:4]===4'hA && sqi_count===8'h00) begin			// continue sqi_hs_read command ?
+		sqi_count = 8;										// abort this command loop
+		-> SQI_HS_READ_trg;									// continue from previous read
+		#1 Mode_Configuration = 8'hFF;								// clear mode config
+	end
+	if(sqi_count < 2 ) begin									// 1st 2 clocks are command
+		sqi_cmd = sqi_cmd <<4;									// shift command data
+		sqi_cmd[3:0] = SIO[3:0];								// load in cmd data
+	end
+	if(sqi_count < 8 ) begin									// look for SPI RSTIO cmd
+		RSTQIO_cmd = RSTQIO_cmd <<1;								// shift command data
+		RSTQIO_cmd[0] = SIO[0];									// load in cmd data
+		if(BUSY===False && sqi_count===8'h07 && RSTQIO_cmd===SPI_RSTQIO)
+			@(posedge CEb) SQI_SPI_mode=False;						// exit SQI mode while in SQI mode using SPI format
+	end
+	if(sqi_count === 8'h01) begin									// start of SQI command interperter
+	   l_sqi_cmd = sqi_cmd;										// latch SQI command
+	   if(DPD===False && RSTEN===True && l_sqi_cmd !== SQI_RST) RSTEN = False;					// clear reset enable on incorrect sequence
+	   if(DPD===False && l_sqi_cmd === SQI_RSTEN)  RSTEN = True;		    					// enable reset command
+	   else if(DPD===False && l_sqi_cmd===SQI_RST && RSTEN===True)	@(posedge CEb)	-> reset;		// reset chip
+	   else if(DPD===False && l_sqi_cmd===SQI_NOP )					RSTEN=False;		// NOP command
+	   else if(DPD===False && l_sqi_cmd===SQI_RDSR)					-> SPI_RDSR_trg;	// SQI read status register
+	   else if(DPD===False && l_sqi_cmd===SQI_RDCR)					-> SPI_RDCR_trg;	// SQI read configuration register
+	   else if(DPD===False && BUSY===True  && l_sqi_cmd===SQI_WRSU)			-> SPI_WRSU_trg;	// Enter suspend mode
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RSTQIO) @(posedge CEb) SQI_SPI_mode=False;	// Reset to SPI mode
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WREN)	@(posedge CEb)	WEL = True;		// Write Enable flag set
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRDI)	@(posedge CEb)	WEL = False;		// Write Enable flag cleared
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRRE )		-> SPI_WRRE_trg;	// exit suspend mode resume normal mode
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RBPR)			-> SPI_RBPR_trg;	// Read Block Protection Register
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_HS_READ)		-> SQI_HS_READ_trg;   	// normal read, 80Mhz
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_SB)			-> SPI_SB_trg;		// Set Burst Count
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RBSQI)		-> SPI_RBSPI_trg;	// sqi nB Burst with Wrap
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_J_ID)			-> SPI_JEDEC_ID_trg; 	// Read JEDEC ID
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RSID)			-> SPI_RSID_trg;	// Read security ID
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_CE     && WEL===True)	-> SPI_CE_trg;		// Chip erase
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_SE     && WEL===True)	-> SPI_SE_trg;		// Sector erase
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_BE     && WEL===True)	-> SPI_BE_trg;		// Block erase
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_LBPR   && WEL===True)	-> SPI_LBPR_trg;	// Lock Down Block Protection Reg.
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_ULBPR  && WEL===True)	-> SPI_ULBPR_trg;	// Global Block Protection Unlock
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_LSID   && WEL===True)	-> SPI_LSID_trg;	// lockout security ID programing
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRSR   && WEL===True)	-> SPI_WRSR_trg;	// Write status register
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_PP     && WEL===True)	-> SPI_QUAD_PP_trg;	// SQI page program
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WBPR   && WEL===True)	-> SPI_WBPR_trg;	// Write Block Protection Register
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_nVWLDR && WEL===True)	-> SPI_nVWLDR_trg;	// write non-volatile block protection register
+	   else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_PSID   && WEL===True)	-> SPI_PSID_trg;	// program Security ID space 2K
+	   else if(BUSY===False && l_sqi_cmd===SQI_DPD)	  		 @(posedge CEb)	DPD = True;		// deep power down mode
+	   else if(BUSY===False && l_sqi_cmd===SQI_DPD_RST)				-> SPI_DPD_RST_trg;	// exit deep power down mode
+	   else begin
+		   if({l_sqi_cmd[7],l_sqi_cmd[3]}!== 2'b11)					// check for start of SPI RSTQIO cmd
+		      $display("\t%m Warning Illegal SQI Instruction='%h' aborted, time=%0.2f",l_sqi_cmd,$realtime);
+		      if(BUSY===True) $display("\t%m Check BUSY most commands don't run during busy");
+	   end
+	end
+	if( sqi_count < 9 ) sqi_count = sqi_count + 1;				              		// incremint bit counter for command sample
+end
+
+//-----------------------------------------------------------
+// Read in serial command stream command,  SPI mode commands
+//-----------------------------------------------------------
+always @( posedge clock && SQI_SPI_mode === False ) begin
+	if(BUSY===False && Mode_Configuration[7:4]===4'hA && spi_count===8'h00) begin			// continue previous command ?
+		spi_count = 8;										// abort command loop
+		if(l_spi_cmd === SPI_QUAD_IO_READ) -> SPI_QUAD_IO_READ_trg;				// continue from previous read, quad read
+		else if(l_spi_cmd === SPI_SDIOR)   -> SPI_SDIOR_trg;					// continue from previous read, dual read
+		Mode_Configuration <= #1 8'hFF;								// clear mode config
+	end
+	if(spi_count < 8 ) begin									// 1st 8 clocks are command
+		spi_cmd = spi_cmd <<1;									// shift command data
+		spi_cmd[0] = SIO[0];									// load in cmd data
+	end
+	if( spi_count === 8'h07) begin									// start of SPI command interperter
+		l_spi_cmd = spi_cmd;                                                    		// latch command
+		if(DPD===False && RSTEN===True && l_spi_cmd !== SPI_RST) RSTEN = False;				// clear reset enable on incorrect sequence
+
+		if(DPD===False && l_spi_cmd === SPI_RSTEN)  RSTEN = True;		    				// enable reset command
+		else if(DPD===False && l_spi_cmd===SPI_NOP ) RSTEN=False;						// NOP command
+		else if(DPD===False && l_spi_cmd===SPI_RST && RSTEN===True)	@(posedge CEb)	-> reset;		// reset command
+		else if(DPD===False && l_spi_cmd===SPI_RDSR)					-> SPI_RDSR_trg;		// SPI read status register
+		else if(DPD===False && l_spi_cmd===SPI_RDCR)					-> SPI_RDCR_trg;		// SPI read configuration register
+		else if(DPD===False && BUSY===True  && l_spi_cmd===SPI_WRSU)			-> SPI_WRSU_trg;		// Enter suspend mode
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_READ)			-> SPI_READ_trg;        	// normal read, 50Mhz
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_HS_READ)			-> SPI_READ_trg;		// normal read, 80Mhz
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RSTQIO)	@(posedge CEb)	SQI_SPI_mode=False;	// This cmd does nothing as already in SPI mode
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WREN)	@(posedge CEb)	WEL = True;		// Write Enable flag set
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRDI)	@(posedge CEb)	WEL = False;		// Write Enable flag cleared
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SDOR)			-> SPI_SDOR_trg;		// dual output read
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SDIOR)			-> SPI_SDIOR_trg;	// dual I/O output read
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_READ)		-> SPI_QUAD_READ_trg;	// SPI QUAD read
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_IO_READ)		-> SPI_QUAD_IO_READ_trg;	// SPI QUAD IO READ
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RBSPI)			-> SPI_RBSPI_trg;	// SPI Burst read
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_EQIO )			-> SPI_EQIO_trg;		// enter SQI mode
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_JEDEC_ID)		-> SPI_JEDEC_ID_trg;	// Read JEDEC ID
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RBPR)			-> SPI_RBPR_trg;		// Read Block Protection Register
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SFDP)			-> SPI_SFDP_trg;		// Read Serial Flash Discoverable Parameters
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RSID)			-> SPI_RSID_trg;		// SPI Read security ID
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SB)			-> SPI_SB_trg;		// SPI Set Burst Count
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRRE)			-> SPI_WRRE_trg;		// exit suspend mode resume normal mode
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_PP && WEL===True)	-> SPI_QUAD_PP_trg;	// SPI QUAD page program
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_ULBPR   && WEL===True)	-> SPI_ULBPR_trg;	// Global Block Protection Unlock
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_nVWLDR  && WEL===True)	-> SPI_nVWLDR_trg;	// write non-volatile block protection register
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRSR    && WEL===True)	-> SPI_WRSR_trg;		// Write status register
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_LSID    && WEL===True)	-> SPI_LSID_trg;		// lockout security ID programing
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_PP      && WEL===True)	-> SPI_PP_trg;		// SPI page program
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WBPR    && WEL===True)	-> SPI_WBPR_trg;		// Write Block Protection Register
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SE      && WEL===True)	-> SPI_SE_trg;		// Sector erase
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_BE      && WEL===True)	-> SPI_BE_trg;		// Block erase
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_CE      && WEL===True)	-> SPI_CE_trg;		// Chip erase
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_PSID    && WEL===True)	-> SPI_PSID_trg;		// program Security ID space 2K
+		else if(DPD===False && BUSY===False && l_spi_cmd===SPI_LBPR    && WEL===True)	-> SPI_LBPR_trg;		// Lock Down Block Protection Reg.
+	  	else if(BUSY===False && l_spi_cmd===SPI_DPD)			@(posedge CEb)	DPD = True;		// deep power down mode
+		else if(BUSY===False && l_spi_cmd===SPI_DPD_RST)				-> SPI_DPD_RST_trg;	// exit deep power down mode
+	        else begin
+			$display("\t%m Warning Illegal SPI Instruction='%h' aborted, time=%0.2f",l_spi_cmd,$realtime);
+			if(BUSY===True) $display("\t%m Check BUSY most commands not allowed during busy");
+		end
+	end
+	if( spi_count < 9 ) spi_count = spi_count + 1;				              		// incremint bit counter for command sample
+end
+
+//---------------------------------------------------------------------------
+// Enter SQI mode
+//---------------------------------------------------------------------------
+always @(SPI_EQIO_trg) begin
+	@(posedge CEb)
+	if (~write_protect) SQI_SPI_mode = True;
+end
+
+//---------------------------------------------------------------------------
+// Lock Down Block Protection Reg.
+//---------------------------------------------------------------------------
+always @(SPI_LBPR_trg) begin
+	@(posedge CEb) begin
+		WPLD = True;	// set WPLD (write protection lock down status[4])
+		WEL = False;	// clear status[2] WEL
+	end
+end
+
+//---------------------------------------------------------------------------
+// Resume, exit suspend mode
+//---------------------------------------------------------------------------
+always @(SPI_WRRE_trg) begin :SPI_WRRE_label
+reg [8:0] pcount;
+	if(suspend_act===False) $display("\t%m Warning WRRE(h30) cmd ignnored not in suspend mode, time=%0.2f",$realtime);
+	else begin							// suspend_act===True
+		if(WSP === True) begin					// resume from Page program ?
+			WSP = False;					// clear program suspend flag
+			page_program_active = True;			// flags needed to continue PP program 
+			valid_data = True; valid_addr = True;		// flags needed to continue PP program
+			SPI_PP_active = True;				// flags needed to continue PP program
+			time_left = s_time_left;			// program time left
+			pgm_addr = resume_addr;				// restort program address
+			for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = s_pmem[pcount];	// restore suspended program data
+		end
+		else if(WSE===True) begin				// Erase Suspended ?
+			erase_active = True;				// restart erase on CEb inactive
+			WSE = False;					// clear erase suspend flag
+			valid_addr = True;				// set resume address as valid
+			valid_data = True;				// set resume data as valid
+			erase_addr = resume_addr;			// restore address
+			erase_size = resume_size;			// restore size of erase area
+			time_left = s_time_left;			// erase time left
+			BE_flag = s_BE_flag;				// restore type of erase
+			SE_flag = s_SE_flag;				// restore type of erase
+		end
+		@(posedge CEb) ;					// wait for CEb to go inactive, starts erase/program loops
+	end
+end
+
+//---------------------------------------------------------------------------
+// enter suspend mode, WEL=1 already to get here
+//---------------------------------------------------------------------------
+always @(SPI_WRSU_trg) begin :SPI_WRSU_label
+reg [8:0] pcount;
+	@(posedge CEb) ;					// wait for CEb to go inactive
+	#0 if((page_program_active === False && erase_ip === False) || SPI_PSID_ip===True ) begin
+		$display("\t%m Warning Write Suspend(hB0), only allowed during PP(h32),PP(h02),BE(hD8),SE(h20) cmds, cmd aborted time=%0.2f",$realtime);
+	end
+	else if(CE_flag===True)  begin				// no suspend during chip erase
+		$display("\t%m Warning Write Suspend(hB0), not allowed during CE(hC7) cmd, cmd aborted time=%0.2f",$realtime);
+	end
+	else if(suspend_act===True) begin
+		$display("\t%m Warning Write Suspend(hB0), nested suspends not allowed, WRSU(hB0) cmd aborted time=%0.2f",$realtime);
+	end
+	else begin						// begin Suspend mode
+		SPI_WRSU_active = True;				// this loop is active
+		if(page_program_active === True) begin
+			disable page_program_label;		// abort programing on suspend
+			s_time_left = (time_left - ($realtime - start_erase)) + Tws;
+			resume_addr = suspend_addr;		// save suspended address
+			for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) s_pmem[pcount] = pmem[pcount];	// save suspended data program data
+			WSP = True;				// set WSE in status register
+			#Tws ;					// wait for suspend program complete
+			page_program_active = False;		// clear busy
+			WEL = False;				// clear WEL write enable
+		end
+		else if( erase_ip === True) begin		// Sector/Block erase in progress ?
+			disable erase_label;			// abort erase on suspend
+			s_time_left = (time_left - ($realtime - start_erase)) + Tws;
+			resume_addr = suspend_addr;		// save suspended address
+			resume_size = erase_size;		// save block size to erase 
+			WSE = True;				// set WSE in status register
+			#Tws ;					// wait for suspend of erase complete
+			erase_ip = False;			// clear erase loop flag;
+			WEL = False;
+			s_BE_flag = BE_flag;			// save type of erase
+			s_SE_flag = SE_flag;			// save type of erase
+			BE_flag = False;
+			SE_flag = False;
+		end
+		SPI_WRSU_active = False;			// this loop is inactive
+	end
+end
+
+//---------------------------------------------------------------------------
+// Global Block Protection Unlock
+//---------------------------------------------------------------------------
+always @(SPI_ULBPR_trg) begin :SPI_ULBPR_label
+reg [7:0] count;				// counter
+	@(posedge CEb) begin
+		if(WBPR_protection_lck === False) begin		// check block protect lock down register
+
+			// clear the block write protection
+			for(count=0;count<(PROTECT_REG_MSB-15);count=count+1) protect[count]=1'b0;
+	
+			// clear the 8 8K block write protection bits
+			count = PROTECT_REG_MSB-15;
+			repeat(8) begin protect[count] = 1'b0; count = count + 2; end
+			WEL = False;				// clear write enable in status reg
+		end
+		else begin
+			$display("\t%m Warning ULBPR(h98) cmd aborted time=%0.2f",$realtime);
+		end
+	end
+end
+
+//---------------------------------------------------------------------------
+// program status/configuration register
+//---------------------------------------------------------------------------
+always @(posedge CEb) begin : SPI_WRSR_PGM
+	if(SPI_WRSR_active===True && suspend_act===False) begin
+		disable SPI_WRSR_label;
+		SPI_WRSR_active = False;
+		if(valid_data===True) begin		// 16 clocks of data ?
+			IOC = wsr_creg[1];		// set IOC right away, don't wait for program to finish
+			if(wsr_creg[7]!==WPEN) begin	// WPEN <- 0
+				SPI_WRSR_PGM_active = True;	// set busy
+				if(wsr_creg[7]===False) begin	// WPEN <- 0
+					#Tse WPEN = wsr_creg[7];// program WPEN
+				end
+				else begin			// WPEN <- 1
+					#Tpp WPEN = wsr_creg[7];// Erase WPEN
+				end
+			end
+			WEL = False;			// clear WEL
+			valid_data = False;		// clear valid data flag
+			SPI_WRSR_PGM_active = False;	// clear busy
+		end
+		else $display("\t%m Warning WRSR(h01) has invalid data, cmd aborted, time=%0.2f",$realtime);
+	end
+end
+
+//---------------------------------------------------------------------------
+// write status register
+//---------------------------------------------------------------------------
+always @(SPI_WRSR_trg) begin :SPI_WRSR_label
+	if(suspend_act===False && CONFIG_protection_lck===False) begin
+		SPI_WRSR_active = True;
+		valid_data = False;					// default valid data
+		if(SQI_SPI_mode === True) begin				// SQI bus
+			$display("\t%m Warning do not run cmd WRSR(h01) in SQI mode time=%0.2f",$realtime);
+			@(posedge clock) wsr_sreg[7:4] = SIO[3:0];	// read in status register
+			@(posedge clock) wsr_sreg[3:0] = SIO[3:0];
+			@(posedge clock) wsr_creg[7:4] = SIO[3:0];	// read in configuration register
+			@(posedge clock) wsr_creg[3:0] = SIO[3:0];
+		end
+		else begin						// SPI bus
+			repeat(8) @(posedge clock ) begin		// read in status register
+				wsr_sreg = wsr_sreg <<1;
+				wsr_sreg[0] = SIO[0];
+			end
+			repeat(8) @(posedge clock ) begin		// read in configuration register
+				wsr_creg = wsr_creg <<1;
+				wsr_creg[0] = SIO[0];
+			end
+		end
+		valid_data = True;					// set valid data flag
+		forever @(posedge clock ) ;				// wait here for CEb rising
+	end
+	else begin
+		if(CONFIG_protection_lck===True) begin
+		  $display("\t%m Warning command WRSR('h01) aborted, configuration reg write protected time=%0.2f",$realtime);
+		end
+		if(suspend_act===True ) begin
+		  $display("\t%m Warning command WRSR('h01) aborted, not a valid cmd is suspend mode, time=%0.2f",$realtime);
+		end
+		valid_data = False;			// default valid data
+		SPI_WRSR_active = False;
+	end
+end
+
+//---------------------------------------------------------------------------
+// Security ID program, lockout
+//---------------------------------------------------------------------------
+always @(SPI_LSID_trg) begin :SPI_LSID_label
+	@(posedge CEb) begin
+		if(suspend_act===False) begin
+			SPI_LSID_active = True;			// set busy
+			SEC = True;			// program SEC bit of status register
+			#Tpsid
+			WEL = False;				// clear write enable in status reg
+			SPI_LSID_active = False;		// clear busy
+		end
+		else begin
+			$display("\t%m Warning command LSID(h85) not allowed in suspend mode, aborted time=%0.2f",$realtime);
+		end
+	end
+end
+
+//---------------------------------------------------------------------------
+// Security ID program, Program Security ID Memory when CEb inactive ?
+//---------------------------------------------------------------------------
+always @(posedge CEb) begin :Sec_ID_pgm_label
+reg [AF_MSB:0] nn;
+	if(SPI_PSID_active === True && suspend_act===False ) begin		// Page_Program of Security ID is active
+		disable SPI_PSID_label;						// disable Security ID page program loop
+		SPI_PSID_active = False;					// clear program loop
+		if(valid_data===True && valid_addr===True && suspend_act===False) begin
+			page_program_active = True;				// set busy
+			valid_addr = False;					// default valid address
+			valid_data = False;					// default valid data
+			for(nn=0;nn<Program_Page_Size;nn=nn+1) begin		// save current data in Memory
+			   x_pmem[nn]=security_id[{pgm_id_addr[10:8],nn[7:0]}];	// save security_id data that will be written over
+			   security_id[{pgm_id_addr[10:8],nn[7:0]}] = 8'hxx;	// make data 'xx'
+			end
+			SPI_PSID_ip = True;					// security programing in progress
+			#Tpp for(nn=0;nn<Program_Page_Size;nn=nn+1) begin	// Wait Tpp time for program to finish, then update memory
+				security_id[{pgm_id_addr[10:8],nn[7:0]}] = x_pmem[nn] & pmem[nn[7:0]];
+			//$display("\tprogram security_id add=%h, data=%h time=%0.2f",{pgm_id_addr[ADDR_MSB:8],nn[7:0]},(x_pmem[nn] & pmem[nn[7:0]]),
+				//$realtime);
+			end
+			SPI_PSID_ip = False;					// security programing complete
+			page_program_active = False;				// clear busy
+			WEL = False;
+		end
+		else begin
+			$display("\t%m Warning PSID(hA5) Page Program error, PSID(hA5) cmd aborted time=%0.2f",$realtime);
+		end
+	end
+end
+
+//---------------------------------------------------------------------------
+// Program Security ID , get address and data
+//---------------------------------------------------------------------------
+always @(SPI_PSID_trg) begin : SPI_PSID_label
+reg [8:0] pcount;
+reg [7:0] sdata;
+
+	valid_addr = False;						// default valid address
+	valid_data = False;						// default valid data
+	if(suspend_act === False) begin					// check WREN flag, no program on suspend active
+		SPI_PSID_active = True;					// program loop is active
+		for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF;  // clear program data to all 1's
+
+		if(SQI_SPI_mode === True) begin
+			repeat(4) begin						// read in address, set valid address flag when complete
+				pgm_id_addr = pgm_id_addr <<4;
+				@(posedge clock) pgm_id_addr[3:0]=SIO[3:0];	// read in 1-nibble of address
+			end
+		end
+		else begin
+			repeat(16) begin					// read in address, set valid address flag when complete
+				pgm_id_addr = pgm_id_addr <<1;
+				@(posedge clock) pgm_id_addr[0] = SIO[0];	// read in 1-bit of address
+			end
+		end
+		pgm_id_addr = pgm_id_addr & ((Kilo*2)-1);			// clear unused upper address bits above 2K memory boundry
+		valid_addr = True;						// address read complete set valid address flag
+		if(SEC===False) begin						// check for proteced Memory
+			valid_data = False;					// no data valid
+			forever begin						// Read program data, loop through all data abort on CEb rising
+				if(SQI_SPI_mode === True) begin			// SQI mode
+					@(posedge clock) sdata[7:4]=SIO[3:0];	// read high nibble
+					valid_data = True;			// at least 1 valid data clock
+					@(posedge clock) sdata[3:0]=SIO[3:0];	// read low nibble
+				end
+				else begin					// SPI mode
+					repeat(8) @(posedge clock) begin	// read in byte of data
+						valid_data = True;		// at least 1 valid data clock
+						sdata = sdata <<1;
+						sdata[0] = SIO[0];
+					end
+				end
+				if(pgm_id_addr >= 'h0008) begin			// don't program SST Memory section
+					pmem[pgm_id_addr[7:0]] = sdata;		// save byte of data page Memory
+				end
+				pgm_id_addr[7:0]=pgm_id_addr[7:0] + 1;		// increment to next addr of page Memory, wrap on 256 byte bountry
+			end
+		end
+		else begin							// protected Memory abort program
+			valid_addr = False;					// default valid address
+			valid_data = False;					// default valid data
+			SPI_PSID_active = False;				// abort program on protected Memory
+			$display("\t%m Warning PSID(hA5) command aborted SEC=1 Locked Memory address=%h, time %0.2f",
+			pgm_id_addr,$realtime);
+		end
+		SPI_PSID_active = False;
+	end
+	else begin
+			$display("\t%m Warning PSID(hA5) command aborted, PSID does not work in Suspend Mode, time=%0.2f",$realtime);
+	end
+end
+
+
+//----------------------------------------------------------------------------
+// SPI Mode Read Security ID space
+//----------------------------------------------------------------------------
+always @(SPI_RSID_trg ) begin :SPI_RSID_label
+reg [7:0] data;
+reg [15:0] addr;		// max value 2K-1
+	SPI_RSID_active = True;
+	if(SQI_SPI_mode === True) begin					// SQI mode
+		// read in address[15:0]
+       		@(posedge clock) addr[15:12] = SIO[3:0];
+       		@(posedge clock) addr[11:8]  = SIO[3:0];
+       		@(posedge clock) addr[7:4]   = SIO[3:0];
+       		@(posedge clock) addr[3:0]   = SIO[3:0];
+		repeat(6) @(posedge clock) ;				// 3 dummy cycles
+		forever begin						// output SQI nibble data
+			data = security_id[addr[10:0]];			// read from RSID Memory, limit to 2K address range
+			addr[10:0] = addr[10:0] + 1;			// increment address, wrap at 2k boundry
+			@(negedge clock) begin
+				SIO_IO <= #Tclz  {True,True,True,True};	// Set I/O controls
+				#Tv SIO_OUT[3:0] = data[7:4];		// send high nibble
+			end
+			@(negedge clock)
+				#Tv SIO_OUT[3:0] = data[3:0];		// send low nibble
+		end
+	end
+	else begin							// SPI mode
+		repeat(16) begin
+        		@(posedge clock)  begin                         // wait for clk rising
+                        	addr = addr <<1;                        // shift left address
+                        	addr[0] = SIO[0];                       // read in address bit
+                	end
+        	end
+		repeat(8) @(posedge clock) ;				// dummy cycle
+		forever begin						// output SPI serial data
+			data = security_id[addr[10:0]];			// read from RSID Memory, limit to 2K address range
+			addr[10:0] = addr[10:0] + 1;			// increment address, wrap at 2k boundry
+			repeat(8) begin
+				@(negedge clock) ;			// wait here for clock falling
+ 				SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
+				#Tv SIO_OUT[1] = data[7];		// output 1 bit data
+				data = data <<1;			// shift data left
+			end
+		end
+	end
+	SPI_RSID_active = False;
+end
+
+
+
+//---------------------------------------------------------------------------
+// erase Memory when CEb inactive ?
+//---------------------------------------------------------------------------
+always @(posedge CEb) begin :erase_label
+reg [31:0] nn;
+	if(erase_active === True) begin
+		erase_ip = True;						// set erase in progress flag
+		erase_active = False;
+		disable erase_setup_label;
+		if(valid_addr === True ) begin					// check valid address and WSE
+			suspend_addr = erase_addr;				// save erase address for possible suspend
+			start_erase = $realtime;				// save time of program/erase start
+			for(nn=erase_addr;nn<(erase_addr+erase_size);nn=nn+8) begin	// make unknown
+				memory[nn[ADDR_MSB:0]+0] = 8'hxx; memory[nn[ADDR_MSB:0]+1] = 8'hxx;
+				memory[nn[ADDR_MSB:0]+2] = 8'hxx; memory[nn[ADDR_MSB:0]+3] = 8'hxx;
+				memory[nn[ADDR_MSB:0]+4] = 8'hxx; memory[nn[ADDR_MSB:0]+5] = 8'hxx;
+				memory[nn[ADDR_MSB:0]+6] = 8'hxx; memory[nn[ADDR_MSB:0]+7] = 8'hxx;
+			end
+			#time_left for(nn=erase_addr;nn<(erase_addr+erase_size);nn=nn+8) begin	// make known at completion of erase
+				memory[nn[ADDR_MSB:0]+0]=8'hFF; memory[nn[ADDR_MSB:0]+1]=8'hFF;
+				memory[nn[ADDR_MSB:0]+2]=8'hFF; memory[nn[ADDR_MSB:0]+3]=8'hFF;
+				memory[nn[ADDR_MSB:0]+4]=8'hFF; memory[nn[ADDR_MSB:0]+5]=8'hFF;
+				memory[nn[ADDR_MSB:0]+6]=8'hFF; memory[nn[ADDR_MSB:0]+7]=8'hFF;
+				WEL = False;
+			end
+		end
+		else if(valid_addr === False) begin
+			$display("\t%m Warning erase address error, erase cmd aborted time=%0.2f",$realtime);
+		end
+		CE_flag = False; BE_flag = False; SE_flag = False;
+		erase_ip = False;
+	end
+end
+//---------------------------------------------------------------------------
+// Erase SE,BE,CE Memory
+//---------------------------------------------------------------------------
+always @(SPI_SE_trg or SPI_BE_trg or SPI_CE_trg) begin :erase_setup_label
+	if(WEL === True && WSE === False) begin 					// check no suspend of sector/block
+		erase_active = True;							// erase loop is active
+		valid_addr = False;							// default valid address as bad
+		if(l_spi_cmd===SPI_CE || l_sqi_cmd===SQI_CE) begin			// chip erase
+			CE_flag = True; BE_flag=False; SE_flag=False;			// set erase type
+			time_left = Tsce;						// erase time
+			erase_addr = 0;							// chip erase address starts at 0
+			erase_time = Tsce;
+			erase_size = Memsize;
+			if(Chip_proT(erase_addr)===False && suspend_act===False) begin	// check protected areas
+				valid_addr = True;					// set address as valid
+			end
+			else begin
+				$display("\t%m Warning chip erase error, trying to erase protected Memory cmd aborted time=%0.2f",$realtime);
+				valid_addr = False;
+			end
+		end
+		else begin								// read in 24 bit address
+			if(SQI_SPI_mode === False) begin				// SPI
+				repeat(24) begin					// read in address, set valid address flag when complete
+					erase_addr = erase_addr <<1;
+					@(posedge clock) erase_addr[0] = SIO[0];
+
+				end
+			end
+			else begin							// SQI
+				repeat(6) begin						// read in address, set valid address flag when complete
+					erase_addr = erase_addr <<4;
+					@(posedge clock) erase_addr[3:0] = SIO[3:0];
+				end
+			end
+			if(Write_proT(erase_addr)===False && PGM_ERASE(erase_addr,resume_addr)===False) valid_addr = True;
+			else begin
+				$display("\t%m Warning erase error, trying to erase protected Memory cmd aborted time=%0.2f",
+				$realtime);
+				valid_addr = False;
+			end
+		end
+		erase_addr = erase_addr & (Memsize-1);			// clear unused upper address bits if address is greater tham memory size
+
+		if(l_spi_cmd===SPI_SE || l_sqi_cmd===SQI_SE) begin	// Sector Erase ?
+			time_left = Tse;				// time left to program
+			SE_flag=True; BE_flag=False; CE_flag = False;	// set erase flag for SE
+			erase_size = Sector_Size;			// set erase size
+			erase_addr[Sector_MSB:0] = 0;			// clear unused lower address bits to 0
+		end
+		else if(l_spi_cmd===SPI_BE || l_sqi_cmd===SQI_BE)  begin// Block erase ?
+			BE_flag=True; SE_flag=False; CE_flag = False;	// set erase flag for BE
+			time_left = Tbe;				// time left to program
+									// set block size, clear unused lower address bits to 0
+			if(erase_addr < (Kilo * 32))                 begin erase_size=Block_08k; erase_addr[Block08k_MSB:0]=0; end
+			else if(erase_addr < (Kilo * 64))            begin erase_size=Block_32k; erase_addr[Block32k_MSB:0]=0; end
+			else if(erase_addr >= (Memsize-(Kilo * 32))) begin erase_size=Block_08k; erase_addr[Block08k_MSB:0]=0; end
+			else if(erase_addr >= Memsize-(Kilo * 64))   begin erase_size=Block_32k; erase_addr[Block32k_MSB:0]=0; end
+			else 					     begin erase_size=Block_64k; erase_addr[Block64k_MSB:0]=0; end
+		end
+		forever @(posedge clock) ;						// wait here for CEb to become inactice
+	
+		erase_active = False;							// erase loop is active
+	end
+	else begin
+		$display("\t%m Warning erase error,nested erase not allowed in suspend mode, cmd aborted time=%0.2f",$realtime);
+	end
+end
+
+//---------------------------------------------------------------------------
+// page program Memory when CEb inactive ?
+//---------------------------------------------------------------------------
+always @(posedge CEb) begin :page_program_label
+reg [AF_MSB:0] nn;
+	if(SPI_PP_active === True || SPI_QUAD_PP_active === True) begin		// Page_Program is active
+		if(SPI_PP_active === True) begin				// Page_Program_label is active
+			disable SPI_PP_label;					// disable page program loop
+			SPI_PP_active = False;					// clear program loop
+		end
+		else if(SPI_QUAD_PP_active === True) begin			// Page_Program_label is active
+			disable SPI_QUAD_PP_label;				// disable page program loop
+			SPI_QUAD_PP_active = False;				// clear program loop
+		end
+		if(valid_data===True && valid_addr===True ) begin
+			page_program_active = True;				// set busy
+			valid_addr = False;					// default valid address
+			valid_data = False;					// default valid data
+			suspend_addr = {pgm_addr[ADDR_MSB:8],8'h00};		// save program address for possible suspend
+			start_erase = $realtime;				// save time of program/erase start
+			if (time_left == Tpp) begin
+				for(nn=0;nn<Program_Page_Size;nn=nn+1) begin		// save current data in Memory
+				   x_pmem[nn] = memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}];	// save Memory data that will be written over
+				   memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}] = 8'hxx;	// make data 'xx'
+				end
+			end
+			#time_left for(nn=0;nn<Program_Page_Size;nn=nn+1) begin
+				memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}] = x_pmem[nn] & pmem[nn[7:0]];
+			//$display("\tprogram Memory add=%h, data=%h time=%0.2f",{pgm_addr[ADDR_MSB:8],nn[7:0]},(x_pmem[nn] & pmem[nn[7:0]]),$realtime);
+			//$display("\tnn=%h, nn[7:0]=%h, x_pmem[nn]=%h, pmem[nn[7:0]]=%h time=%0.2f",nn,nn[7:0],x_pmem[nn], pmem[nn[7:0]],$realtime);
+			end
+			page_program_active = False;				// clear busy
+			WEL = False;
+		end
+		else begin
+			$display("\t%m Warning Page Program error, PP(h02)/PP(h32) cmd aborted time=%0.2f",$realtime);
+		end
+	end
+end
+
+//---------------------------------------------------------------------------
+// QUAD Page program read in address, data, place program data into pgm_addr array
+// When CEb goes high program loop is called using pgm_addr and pmem
+//---------------------------------------------------------------------------
+always @(SPI_QUAD_PP_trg) begin : SPI_QUAD_PP_label
+reg [8:0] pcount;
+reg [7:0] sdata;
+	if((IOC === False) && (SQI_SPI_mode === False)) $display("\t%m Warning SPI QUAD PAGE READ(h32) command aborted when IOC=0 time=%0.2f",$realtime);
+	else begin
+		valid_addr = False;						// default valid address
+		valid_data = False;						// default valid data
+		if(WEL === True && WSP === False) begin				// check WSP flag, no program on program suspend active
+			SPI_QUAD_PP_active = True;				// program loop is active
+			time_left = Tpp;					// time left to program
+			for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF;  // clear program data to all 1's
+	
+			repeat(6) begin						// read in address, set valid address flag when complete
+				pgm_addr = pgm_addr <<4;
+				@(posedge clock) pgm_addr[3:0] = SIO[3:0];
+			end
+			pgm_addr = pgm_addr & (Memsize-1);			// clear upper unused address bits
+			valid_addr = True;					// address read complete set valid address flag
+			if(Write_proT(pgm_addr)===False && ERASE_PGM(resume_addr,pgm_addr)===False) begin	// check for proteced Memory
+				valid_data = False;				// no data valid
+				forever begin					// Read program data, loop through all data abort on CEb rising
+					repeat(2) @(posedge clock ) begin	// read in byte of data
+							valid_data = True;	// at least 1 data clock
+							sdata = sdata <<4;
+							sdata[3:0] = SIO[3:0];	// read data as nibbles
+					end
+					pmem[pgm_addr[7:0]] = sdata;		// save byte of page data
+					pgm_addr[7:0] = pgm_addr[7:0] + 1;	// increment to next addr of page Memory, wrap on 256 byte bountry
+				end
+			end
+			else begin						// protected Memory abort program
+				valid_addr = False;				// default valid address
+				valid_data = False;				// default valid data
+				SPI_QUAD_PP_active = False;			// abort program on protected Memory
+				$display("\t%m Warning attempting to program protected page address=%h, PP(h32) cmd aborted time=%0.2f",
+				pgm_addr,$realtime);
+			end
+			SPI_QUAD_PP_active = False;
+		end
+		else begin
+			$display("\t%m Warning Nested Page Program not allowed in program suspend mode time=%0.2f",$realtime);
+		end
+	end
+end
+
+//---------------------------------------------------------------------------
+// Page program read in address, data, place program data into pgm_addr array
+// When CEb goes high program loop is called using pgm_addr and pmem
+//---------------------------------------------------------------------------
+always @(SPI_PP_trg) begin : SPI_PP_label
+reg [8:0] pcount;
+reg [7:0] sdata;
+
+	valid_addr = False;						// default valid address
+	valid_data = False;						// default valid data
+	time_left = Tpp;						// time left to program
+	if(WEL === True && WSP === False) begin 			// check WSP status before programing, no programing on program suspend active
+		SPI_PP_active = True;					// program loop is active
+		for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF;  // clear program data to all 1's
+
+		repeat(24) begin					// read in address, set valid address flag when complete
+			pgm_addr = pgm_addr <<1;
+			@(posedge clock) pgm_addr[0] = SIO[0];
+		end
+		pgm_addr = pgm_addr & (Memsize-1);			// clear upper unused address bits
+		valid_addr = True;					// address read complete set valid address flag
+		if(Write_proT(pgm_addr)===False && ERASE_PGM(resume_addr,pgm_addr)===False) begin	// check for proteced Memory
+			valid_data = False;				// no data valid
+			forever begin					// Read program data, loop through all data abort on CEb rising
+				repeat(8) @(posedge clock ) begin	// read in byte of data
+						valid_data = True;	// at least 1 valid data clock
+						sdata = sdata <<1;
+						sdata[0] = SIO[0];
+				end
+				pmem[pgm_addr[7:0]] = sdata;		// save byte of page data
+				pgm_addr[7:0] = pgm_addr[7:0] + 1;	// increment to next addr of page Memory, wrap on 256 byte bountry
+			end
+		end
+		else begin						// protected Memory abort program
+			valid_addr = False;				// default valid address
+			valid_data = False;				// default valid data
+			SPI_PP_active = False;				// abort program on protected Memory
+			if(ERASE_PGM(resume_addr,pgm_addr)===True) begin
+				$display("\t%m Warning attempting to program erase suspended Memory address=%h, PP(h02) cmd aborted time=%0.2f",
+				pgm_addr,$realtime);
+			end
+			else $display("\t%m Warning attempting to program protected page address=%h, PP(h02) cmd aborted time=%0.2f",pgm_addr,$realtime);
+		end
+		SPI_PP_active = False;
+	end
+	else begin
+		$display("\t%m Warning Nested Page Program not allowed in program suspend mode time=%0.2f",$realtime);
+	end
+end
+
+//---------------------------------------------------------------------------
+// SPI Read block protection register
+//---------------------------------------------------------------------------
+always @(SPI_RBPR_trg) begin :SPI_RBPR_label
+reg [PROTECT_REG_MSB:0] tmp_protect;					// protection register definishion max size for 32M-bit
+	SPI_RBPR_active = True;						// read l status loop is active
+	tmp_protect = protect_or;					// copy protection reg
+	if(SQI_SPI_mode === True) begin
+		@(negedge clock) ;                              // wait here for clock falling
+		@(negedge clock) ;                              // wait here for clock falling
+		forever begin						// out put SPI data bit by 
+			@(negedge clock) ;                              // wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True};     	// Turn on IO control SIO[3:0]
+			#Tv begin					// output nibble of data
+				SIO_OUT[3]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1;	// shift data left
+				SIO_OUT[2]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1;	// shift data left
+				SIO_OUT[1]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1;	// shift data left
+				SIO_OUT[0]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1;	// shift data left
+			end
+		end
+	end
+	else begin
+		forever begin						// out put SPI data bit by 
+			@(negedge clock) ;                              // wait here for clock falling
+            		SIO_IO <= #Tclz  {False,False,True,False};     	// Turn on IO control SIO[1]
+			#Tv SIO_OUT[1]=tmp_protect[PROTECT_REG_MSB];	// shift out protection data
+                	tmp_protect = tmp_protect <<1;                 	// shift data left
+		end
+	end
+	SPI_RBPR_active = False;					// read status loop is inactive
+end
+
+//----------------------------------------------------------------------------
+// Program Block Protection Register volatile -volitale
+//----------------------------------------------------------------------------
+always @(SPI_WBPR_trg ) begin :SPI_WBPR_label
+reg [8:0] ncount;
+reg [7:0] bit_count;			// number of bits to read
+	if(suspend_act===False && WBPR_protection_lck===False && WPLD===False) begin	// check truth table table 2 in spec
+		SPI_WBPR_active = True;				// this loop is active
+		ncount = PROTECT_REG_MSB;
+		if(SQI_SPI_mode === True) begin			// SQI mode
+			repeat((PROTECT_REG_MSB+1)/4) begin
+				@(posedge clock) begin
+					protect[ncount] = SIO[3];  ncount = ncount - 1;
+					protect[ncount] = SIO[2];  ncount = ncount - 1;
+					protect[ncount] = SIO[1];  ncount = ncount - 1;
+					protect[ncount] = SIO[0];  ncount = ncount - 1;
+				end
+			end
+		end
+		else begin							// SPI mode
+			repeat(PROTECT_REG_MSB+1) begin
+				@(posedge clock) protect[ncount] = SIO[0];	// save protection data
+				ncount = ncount - 1;				// count the number of clocks
+			end
+		end
+		WEL = False;						// clear WEL on WBPR command
+		forever @(posedge clock) ;				// if to many clocks wait here for CEb to go inactive
+	end
+	else begin
+		if(WEL === False)
+			$display("\t%m Warning status flag WEL=0, WBPR(h42) cmd aborted time=%0.2f",$realtime);
+		else if(suspend_act===True)
+			$display("\t%m Warning WBPR not allowed in suspend mode, WBPR[h42) cmd aborted time=%0.2f",$realtime);
+		else if(WBPR_protection_lck === True)
+			$display("\t%m Warning Block Protection Reg protected, WBPR(h42) cmd aborted time=%0.2f",$realtime);
+	end
+	SPI_WBPR_active = False;		// this loop is inactive
+end
+//----------------------------------------------------------------------------
+// Program Block Protection Register non-volitale
+//----------------------------------------------------------------------------
+always @(SPI_nVWLDR_trg) begin :SPI_nVWLDR_cmd_label
+reg [8:0] ncount;
+reg [7:0] bit_count;				// number of bits to read
+	t_wlldr_mem = wlldr_mem;		// save current value of wlldr_mem
+	if(suspend_act===False && WBPR_protection_lck === False) begin		// check truth table table 2 in spec
+		SPI_nVWLDR_cmd_active = True;	// this loop is active
+		ncount = PROTECT_REG_MSB;
+
+		if(SQI_SPI_mode === True) begin	// SQI mode
+			repeat((PROTECT_REG_MSB+1)/4) begin
+				@(posedge clock) begin
+					t_wlldr_mem[ncount] = SIO[3]; ncount = ncount - 1;
+					t_wlldr_mem[ncount] = SIO[2]; ncount = ncount - 1;
+					t_wlldr_mem[ncount] = SIO[1]; ncount = ncount - 1;
+					t_wlldr_mem[ncount] = SIO[0]; ncount = ncount - 1;
+				end
+			end
+		end
+		else begin				//SPI mode
+			repeat(PROTECT_REG_MSB+1) begin
+				@(posedge clock) t_wlldr_mem[ncount] = SIO[0];			// save non-volatile data
+				ncount = ncount - 1;	// count the number of clocks
+			end
+		end
+
+		forever @(posedge clock) ;	// if to many clocks wait here for CEb to go inactive
+	end
+	else begin
+		if(WBPR_protection_lck === True)
+			$display("\t%m Warning nVWLDR(hE8) cmd aborted (protected) time=%0.2f",$realtime);
+		else if(suspend_act===True)
+			$display("\t%m Warning nVWLDR(E8) not allowed in suspend mode, nVWLDR(hE8) cmd aborted time=%0.2f",$realtime);
+	end
+	SPI_nVWLDR_cmd_active = False;		// this loop is inactive
+end
+
+//---------------------------------------------------------------------------
+// nVWLDR program command, program wlldr_mem[], wait for program complete
+//---------------------------------------------------------------------------
+always @(posedge CEb) begin :SPI_nVWLDR_label
+reg [7:0]nn;
+	if(SPI_nVWLDR_cmd_active===True && suspend_act===False) begin
+		disable SPI_nVWLDR_cmd_label;
+		SPI_nVWLDR_cmd_active = False;
+		SPI_nVWLDR_active = True;		// set busy
+		// make sure read protect flags are never set, clear the read flags
+		nn=0; repeat(8) begin t_wlldr_mem[PROTECT_REG_MSB-nn]=False; nn=nn+2;  end
+		#Tpp wlldr_mem = wlldr_mem | t_wlldr_mem;	// copy tmp data to final data, wait for program to complete
+		SPI_nVWLDR_active = False;		// clear busy
+		WEL = False;				// clear WEL on WBPR command
+	end
+end
+
+//---------------------------------------------------------------------------
+// SPI Read configuration register
+//---------------------------------------------------------------------------
+always @(SPI_RDCR_trg) begin :SPI_RDCR_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+	SPI_RDCR_active = True;						// read l status loop is active
+	if(SQI_SPI_mode === True) begin					// SQI mode ?
+		repeat(2) @(negedge clock) ;				// dummy cycle
+		forever begin						// out put SPI data bit by 
+			data = config_reg;				// byte boundry, save config register
+			@(negedge clock) ;                              // wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True};     	// Turn on IO control SIO[1]
+                	#Tv SIO_OUT[3:0] = data[7:4];                 	// output high nibble
+			@(negedge clock) ;                              // wait here for clock falling
+                	#Tv SIO_OUT[3:0] = data[3:0];                 	// output low nibble
+		end
+	end
+	else begin							// SPI mode
+		forever begin						// out put SPI data bit by bit
+			data = config_reg;				// byte boundry, read configuration register
+			repeat(8) begin
+				@(negedge clock) ;                      // wait here for clock falling
+            			SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
+                		#Tv SIO_OUT[1] = data[7];             	// output 1 bit data
+                		data = data <<1;                       	// shift data left
+			end
+		end
+	end
+	SPI_RDCR_active = False;					// read status loop is inactive
+end
+
+//---------------------------------------------------------------------------
+// SPI Read status register
+//---------------------------------------------------------------------------
+always @(SPI_RDSR_trg) begin :SPI_RDSR_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+	SPI_RDSR_active = True;						// read l status loop is active
+	if(SQI_SPI_mode === True) begin					// SQI mode ?
+		repeat(2) @(negedge clock) ;				// dummy cycle
+		forever begin						// out put SPI data bit by 
+			data = status;					// byte boundry, save status register
+			@(negedge clock) ;                              // wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True};		// Turn on IO control SIO[1]
+                	#Tv SIO_OUT[3:0] = data[7:4];                   // output high nibble data
+			@(negedge clock) ;                              // wait here for clock falling
+                	#Tv SIO_OUT[3:0] = data[3:0];                   // output low nibble data
+		end
+	end
+	else begin							// SPI mode
+		forever begin						// out put SPI data bit by 
+			data = status;					// byte boundry, save status register
+			repeat(8) begin
+				@(negedge clock) ;                      // wait here for clock falling
+            			SIO_IO <= #Tclz  {False,False,True,False}; // Turn on IO control SIO[1]
+                		#Tv SIO_OUT[1] = data[7];               // output 1 bit data
+                		data = data <<1;                        // shift data left
+			end
+		end
+	end
+	SPI_RDSR_active = False;					// read status loop is inactive
+end
+
+
+//----------------------------------------------------------------------------
+// SPI Mode Read Serial Flash Discoverable Parameters
+//----------------------------------------------------------------------------
+always @(SPI_SFDP_trg ) begin :SPI_SFDP_label
+reg [7:0] data;
+reg [10:0] addr;		// max value 2K-1
+	SPI_SFDP_active = True;
+	repeat(24) begin
+        	@(posedge clock)  begin                         // wait for clk rising
+                        addr = addr <<1;                        // shift left address
+                        addr[0] = SIO[0];                       // read in address bit
+                end
+        end
+	repeat(8) @(posedge clock) ;				// dummy cycle
+	forever begin						// output SPI serial data
+		data = SFDP[addr];				// read from SFDP Memory
+		addr = addr + 1;				// increment address
+		repeat(8) begin
+			@(negedge clock) ;			// wait here for clock falling
+ 			SIO_IO <= #Tclz  {False,False,True,False}; // Turn on IO control SIO[1]
+			#Tv SIO_OUT[1] = data[7];		// output 1 bit data
+			data = data <<1;			// shift data left
+		end
+	end
+	SPI_SFDP_active = False;
+end
+
+
+//----------------------------------------------------------------------------
+// SPI Mode Read JDEC registers
+//----------------------------------------------------------------------------
+always @(SPI_JEDEC_ID_trg ) begin :SPI_JEDEC_ID_label
+reg [1:0] ptr;
+reg [7:0] data;
+	SPI_JEDEC_ID_active = True;
+	ptr = 0;
+	if(SQI_SPI_mode === True)
+	begin	
+		@(negedge clock) ;
+		@(negedge clock) ;
+	end	
+	forever begin						// output SPI serial data
+		if(ptr === 2'b00)       data = MANUFACTURE;
+		else if(ptr === 2'b01)  data = Memory_Type;
+		else if(ptr === 2'b10 ) data = Memory_Capacity;
+		if( ptr === 2'b10) ptr = 0; else ptr = ptr + 1;
+		if(SQI_SPI_mode === True) begin					// SQI mode
+				@(negedge clock) ;				// wait here for clock falling
+ 				SIO_IO <= #Tclz  {True,True,True,True};		// Turn on IO control SIO[3:0]
+				#Tv SIO_OUT[3:0] = data[7:4];			// output nibble bit data
+				@(negedge clock) ;				// wait here for clock falling
+				#Tv SIO_OUT[3:0] = data[3:0];			// output 1 nibble data
+		end
+		else begin							// SPI mode
+			repeat(8) begin
+				@(negedge clock) ;				// wait here for clock falling
+ 				SIO_IO <= #Tclz  {False,False,True,False};	// Turn on IO control SIO[1]
+				#Tv SIO_OUT[1] = data[7];			// output 1 bit data
+				data = data <<1;				// shift data left
+			end
+		end
+	end
+	SPI_JEDEC_ID_active = False;
+end
+
+//----------------------------------------------------------------------------
+// Deep Power Down Reset Read device ID
+//----------------------------------------------------------------------------
+always @(SPI_DPD_RST_trg ) begin :SPI_DPD_RST_RDID_label
+reg [7:0] data;
+	SPI_DPD_RST_RDID_active = True;
+	forever begin						// output SPI serial data
+		data = Memory_Capacity;
+		if(SQI_SPI_mode === True) begin					// SQI mode
+				@(negedge clock) ;				// wait here for clock falling
+ 				SIO_IO <= #Tclz  {True,True,True,True};		// Turn on IO control SIO[3:0]
+				#Tv SIO_OUT[3:0] = data[7:4];			// output nibble bit data
+				@(negedge clock) ;				// wait here for clock falling
+				#Tv SIO_OUT[3:0] = data[3:0];			// output 1 nibble data
+		end
+		else begin							// SPI mode
+			repeat(8) begin
+				@(negedge clock) ;				// wait here for clock falling
+ 				SIO_IO <= #Tclz  {False,False,True,False};	// Turn on IO control SIO[1]
+				#Tv SIO_OUT[1] = data[7];			// output 1 bit data
+				data = data <<1;				// shift data left
+			end
+		end
+	end
+	SPI_DPD_RST_RDID_active = False;
+end
+
+//----------------------------------------------------------------------------
+// Deep Power Down Reset - Recovery from Deep Power Down Mode
+//----------------------------------------------------------------------------
+always @(SPI_DPD_RST_trg ) begin :SPI_DPD_RST_label
+reg [7:0] data;
+	@(posedge CEb)
+	#Tsbr DPD = False;
+end
+
+//---------------------------------------------------------------------------
+// SPI Read with Burst 
+//---------------------------------------------------------------------------
+always @(SPI_RBSPI_trg) begin :SPI_RBSPI_label
+reg [AF_MSB:0] addr;						// address storage
+reg [7:0] data;							// tmp storage of data
+	if(IOC === False && SQI_SPI_mode === False) $display("\t%m Warning SPI BURST READ(hEC) command aborted when IOC=0 time=%0.2f",$realtime);
+	else begin
+		SPI_RBSPI_active = True;				// read loop is active
+		repeat(6) begin
+        		@(posedge clock) ;				// wait for clk rising
+                	addr = addr <<4;				// shift left address
+                	addr[3:0] = SIO[3:0];				// read in address nibble
+        	end
+		// read mode
+		repeat(6) @(posedge clock) ;				// 3 dummy cycles
+	
+		forever begin									// output SPI data 1 byte
+                	data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+			@(negedge clock) ;                                      		// wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True}; 	     			// Turn on IO control SIO[3:0]
+                	#Tv SIO_OUT[3:0] = data[7:4];                       			// output 4 bit data
+			@(negedge clock) ;                                      		// wait here for clock falling
+                	#Tv SIO_OUT[3:0] = data[3:0];                       			// output 4 bit data
+			if(burst_length===Burst8) addr[Burst8_MSB:0]=addr[Burst8_MSB:0] + 1;	// inc address with wrap
+			else if(burst_length===Burst16) addr[Burst16_MSB:0]=addr[Burst16_MSB:0] + 1;
+			else if(burst_length===Burst32) addr[Burst32_MSB:0]=addr[Burst32_MSB:0] + 1;
+			else if(burst_length===Burst64) addr[Burst64_MSB:0]=addr[Burst64_MSB:0] + 1;
+		end
+		SPI_RBSPI_active = False;					// read loop is inactive
+	end
+end
+
+
+
+//---------------------------------------------------------------------------
+// SPI Set Burst Count
+//---------------------------------------------------------------------------
+always @(SPI_SB_trg) begin :SPI_SB_label
+reg [7:0] bl;
+	SPI_SB_active = True;
+	if(SQI_SPI_mode === True) begin
+		@(posedge clock) bl[7:4] = SIO[3:0];
+		@(posedge clock) bl[3:0] = SIO[3:0];
+	end
+	else begin
+		@(posedge clock) bl[7] = SIO[0];	// MSB bit of burst count
+		@(posedge clock) bl[6] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[5] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[4] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[3] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[2] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[1] = SIO[0];	// --- bit of burst count
+		@(posedge clock) bl[0] = SIO[0];	// LSB bit of burst count
+	end
+	burst_length = bl;			// set register
+
+	if( |burst_length[7:2] !== 1'b0) begin		// check for legal values of burst count 
+		$display("\t%m Warning SPI Set Burst Instruction has invalid data=%h, time=%0.2f", burst_length,$realtime);
+		$display("\t%m Setting bits[7:2] of Burst Count Register to 0");
+		burst_length[7:2] = 6'b000000;		// clear upper bits
+	end
+	forever @(posedge clock) ;			// wait for end of operation, Disable cmd will exit this line
+	SPI_SB_active = False;
+end
+
+
+
+//---------------------------------------------------------------------------
+// SQI High Speed Read
+//---------------------------------------------------------------------------
+always @(SQI_HS_READ_trg) begin :SQI_HS_READ_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+reg [7:0] count;
+	SQI_HS_READ_active = True;					// Read loop is active
+        if(Mode_Configuration[7:4]===4'hA) begin			// if no command header read in 1st address nibble
+		addr[3:0] = SIO[3:0]; count = 5; 		        // read in first address nibble
+	end
+	else count = 6;							// read 6 times if command header
+	repeat(count) begin
+        	@(posedge clock)  					// wait for clk rising
+                addr = addr <<4;					// shift left address
+                addr[3:0] = SIO[3:0];					// read in address nibble
+        end
+	// read mode
+	@(posedge clock) Mode_Configuration[7:4]=SIO[3:0];		// read in Mode configuration
+	@(posedge clock) Mode_Configuration[3:0]=SIO[3:0];		// read in Mode configuration
+
+	// 4 dummy nibbles
+	repeat(4) @(posedge clock) ;					// 2 dummy  bytes
+
+	forever begin									// output SPI data 1 byte
+                data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+		@(negedge clock) ;                                      		// wait here for clock falling
+            	SIO_IO <= #Tclz  {True,True,True,True}; 	     			// Turn on IO control SIO[3:0]
+                #Tv SIO_OUT[3:0] = data[7:4];                       			// output 4 bit data
+		@(negedge clock) ;                                      		// wait here for clock falling
+                #Tv SIO_OUT[3:0] = data[3:0];                       			// output 4 bit data
+                addr = addr + 1;	                                		// increment to next address on byte boundry
+	end
+	SQI_HS_READ_active = False;
+end
+
+
+
+//---------------------------------------------------------------------------
+// SPI_READ DUAL IO
+//---------------------------------------------------------------------------
+always @(SPI_SDIOR_trg) begin :SPI_SDIOR_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+reg [7:0] count;
+	SPI_SDIOR_active = True;
+        if(Mode_Configuration[7:4]===4'hA) begin			// if no command header read in 1st address nibble
+		addr[1:0] = SIO[1:0]; count = 11; 		        // read in first address 2-bits
+	end
+	else count = 12;							// read 6 times if command header
+	repeat(count) begin
+        	@(posedge clock)  begin                                 // wait for clk rising
+                        addr = addr <<2;                                // shift left address
+                        addr[1:0] = SIO[1:0];                           // read in address nibble
+                end
+        end
+	// read mode
+	@(posedge clock) Mode_Configuration[7:6]=SIO[1:0];		// read in Mode configuration
+	@(posedge clock) Mode_Configuration[5:4]=SIO[1:0];		// read in Mode configuration
+	@(posedge clock) Mode_Configuration[3:2]=SIO[1:0];		// read in Mode configuration
+	@(posedge clock) Mode_Configuration[1:0]=SIO[1:0];		// read in Mode configuration
+
+	forever begin									// output SPI data 1 byte
+                data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+		@(negedge clock) ;                                      		// wait here for clock falling
+            	SIO_IO <= #Tclz  {False,False,True,True}; 	     			// Turn on IO control SIO[3:0]
+                #Tv SIO_OUT[3:0] = data[7:6];                       			// output 4 bit data
+		@(negedge clock) #Tv SIO_OUT[3:0] = data[5:4];
+		@(negedge clock) #Tv SIO_OUT[3:0] = data[3:2];
+		@(negedge clock) #Tv SIO_OUT[3:0] = data[1:0];
+                addr = addr + 1;	                                		// increment to next address on byte boundry
+	end
+	SPI_SDIOR_active = False;
+end
+
+
+//---------------------------------------------------------------------------
+// SPI_READ QUAD IO
+//---------------------------------------------------------------------------
+always @(SPI_QUAD_IO_READ_trg) begin :SPI_QUAD_IO_READ_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+reg [7:0] count;
+	if(IOC === False) $display("\t%m Warning SPI IO QUAD READ(hEB) command aborted when IOC=0 time=%0.2f",$realtime);
+	else begin
+		SPI_QUAD_IO_READ_active = True;
+        	if(Mode_Configuration[7:4]===4'hA) begin			// if no command header read in 1st address nibble
+			addr[3:0] = SIO[3:0]; count = 5; 		        // read in first address nibble
+		end
+		else count = 6;							// read 6 times if command header
+		repeat(count) begin
+        		@(posedge clock)  begin                                 // wait for clk rising
+                        	addr = addr <<4;                                // shift left address
+                        	addr[3:0] = SIO[3:0];                           // read in address nibble
+                	end
+        	end
+		// read mode
+		@(posedge clock) Mode_Configuration[7:4]=SIO[3:0];		// read in Mode configuration
+		@(posedge clock) Mode_Configuration[3:0]=SIO[3:0];		// read in Mode configuration
+	
+		// 2 dummy bytes
+		repeat(4) @(posedge clock) ;					// 2 dummy  bytes
+	
+		forever begin									// output SPI data 1 byte
+                	data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+			@(negedge clock) ;                                      		// wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True}; 	     			// Turn on IO control SIO[3:0]
+                	#Tv SIO_OUT[3:0] = data[7:4];                       			// output 4 bit data
+			@(negedge clock) ;                                      		// wait here for clock falling
+                	#Tv SIO_OUT[3:0] = data[3:0];                       			// output 4 bit data
+                	addr = addr + 1;	                                		// increment to next address on byte boundry
+		end
+		SPI_QUAD_IO_READ_active = False;
+	end
+end
+
+//---------------------------------------------------------------------------
+// SPI_READ QUAD 
+//---------------------------------------------------------------------------
+always @(SPI_QUAD_READ_trg) begin :SPI_QUAD_READ_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+	if(IOC === False) $display("\t%m Warning SPI QUAD READ(h6B) command aborted when IOC=0 time=%0.2f",$realtime);
+	else begin
+		SPI_READ_QUAD_active = True;					// this loop is active
+		repeat(24) begin
+        		@(posedge clock)  begin                                 // wait for clk rising
+                        	addr = addr <<1;                                // shift left address
+                        	addr[0] = SIO[0];                               // read in address bit
+                	end
+        	end
+		// run 8 dummy cycles
+		repeat(8) @(negedge clock) ;
+		forever begin									// output SPI data 1 byte
+                	data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+			@(negedge clock) ;                                      		// wait here for clock falling
+            		SIO_IO <= #Tclz  {True,True,True,True}; 	     			// Turn on IO control SIO[3:0]
+                	#Tv SIO_OUT[3:0] = data[7:4];                       			// output 4 bit data
+			@(negedge clock) ;                                      		// wait here for clock falling
+                	#Tv SIO_OUT[3:0] = data[3:0];                       			// output 4 bit data
+                	addr = addr + 1;	                                		// increment to next address on byte boundry
+		end
+		SPI_READ_QUAD_active = False;							// this loop is active
+	end
+end
+
+//---------------------------------------------------------------------------
+// SPI_READ dual, SDOR
+//---------------------------------------------------------------------------
+always @(SPI_SDOR_trg) begin :SPI_SDOR_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+	SPI_SDOR_active = True;						// read loop is active
+	repeat(24) begin
+        	@(posedge clock)  begin                                 // wait for clk rising
+                        addr = addr <<1;                                // shift left address
+                        addr[0] = SIO[0];                               // read in address bit
+                end
+        end
+	repeat(8) @(negedge clock) ;					// dummy cycle for read
+	forever begin							// out put SPI data 2 bits at a time
+		data=(Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]];     // get data at addr
+		addr = addr + 1;					// increment to next address on byte boundry
+		@(negedge clock) ;                                      // wait here for clock falling
+        	SIO_IO <= #Tclz  {False,False,True,True};	      	// Turn on IO control SIO[1:0]
+                #Tv SIO_OUT[1:0] = data[7:6];                       	// output 2 bits data
+		@(negedge clock) #Tv SIO_OUT[1:0] = data[5:4];         	// output 2 bits data
+		@(negedge clock) #Tv SIO_OUT[1:0] = data[3:2];         	// output 2 bits data
+		@(negedge clock) #Tv SIO_OUT[1:0] = data[1:0];         	// output 2 bits data
+	end
+	SPI_SDOR_active = False;					// read loop is inactive
+end
+
+//---------------------------------------------------------------------------
+// SPI_READ 80/50Mhz
+//---------------------------------------------------------------------------
+always @(SPI_READ_trg) begin :SPI_READ_label
+reg [AF_MSB:0] addr;							// address storage
+reg [7:0] data;								// tmp storage of data
+	SPI_READ_active = True;						// read loop is active
+	repeat(24) begin
+        	@(posedge clock)  begin                                 // wait for clk rising
+                        addr = addr <<1;                                // shift left address
+                        addr[0] = SIO[0];                               // read in address bit
+                end
+        end
+	if(l_spi_cmd === SPI_HS_READ) repeat(8) @(negedge clock) ;	// added dummy cycle for high speed read
+	if(l_spi_cmd === SPI_READ) read_slow_flag = True;		// set timing checks to slow read for SCK timing check
+	forever begin							// out put SPI data bit by 
+                data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
+                addr = addr + 1;                                	// increment to next address on byte boundry
+		repeat(8) begin
+			@(negedge clock) ;                              // wait here for clock falling
+            		SIO_IO <= #Tclz  {False,False,True,False};     	// Turn on IO control SIO[1]
+                	#Tv SIO_OUT[1] = data[7];                     	// output 1 bit data
+                	data = data <<1;                          	// shift data left
+		end
+	end
+	read_slow_flag = False;						// set timing checks back to normal
+	SPI_READ_active = False;					// read loop is inactive
+end
+
+//---------------------------------------------------------------------------
+// chip por setup
+//---------------------------------------------------------------------------
+initial begin
+
+	for(cnt=0;cnt<(Kilo*2);cnt=cnt+1) security_id[cnt] = 8'hFF;	// init security Memory
+	for(cnt=0;cnt<Memsize;cnt=cnt+8) begin				// init flash Memory
+		 memory[cnt+0] = 8'hFF; memory[cnt+1] = 8'hFF; memory[cnt+2] = 8'hFF; memory[cnt+3] = 8'hFF;
+		 memory[cnt+4] = 8'hFF; memory[cnt+5] = 8'hFF; memory[cnt+6] = 8'hFF; memory[cnt+7] = 8'hFF;
+	end
+
+	wlldr_mem = WLLD_value;			// set contents of write-lock lock down register, non-volatile
+	SEC = SECURITY_LOCKOUT_VALUE;		// Security ID Status, non-volatile
+	WPLD = False;				// write protection lockdown status, non-volatile
+	clock = 1'b0;
+	WPEN = INIT_WPEN;			// write protect pin enable, non-volatile bit
+	PE = False;				// default unused configuration register bits
+	EE = False;				// default unused configuration register bits
+	DPD = False;				// deep power down mode
+	pgm_sus_reset = False;			// part is busy if reset while programming is suspended
+	#0 ->reset;				// call reset block
+
+	// set volatile protect register to initial condition
+	for(cnt=0;cnt<=PROTECT_REG_MSB;cnt=cnt+1) protect[cnt] = 1'b1;				// all protect bits set
+	for(cnt=PROTECT_REG_MSB;cnt > (PROTECT_REG_MSB-16); cnt=cnt-2) protect[cnt] = 1'b0;	// read protect bits cleared
+
+	// init serial flash discoverable parameters
+	for(cnt=0;cnt<(Kilo*2);cnt=cnt+1) SFDP[cnt] = 8'hFF;		// init to all FF
+
+	SFDP['h000] = 8'h53;
+	SFDP['h001] = 8'h46;
+	SFDP['h002] = 8'h44;
+	SFDP['h003] = 8'h50;
+	SFDP['h004] = 8'h06;
+	SFDP['h005] = 8'h01;
+	SFDP['h006] = 8'h02;
+	SFDP['h007] = 8'hFF;
+	SFDP['h008] = 8'h00;
+	SFDP['h009] = 8'h06;
+	SFDP['h00A] = 8'h01;
+	SFDP['h00B] = 8'h10;
+	SFDP['h00C] = 8'h30;
+	SFDP['h00D] = 8'h00;
+	SFDP['h00E] = 8'h00;
+	SFDP['h00F] = 8'hFF;
+	SFDP['h010] = 8'h81;
+	SFDP['h011] = 8'h00;
+	SFDP['h012] = 8'h01;
+	SFDP['h013] = 8'h06;
+	SFDP['h014] = 8'h00;
+	SFDP['h015] = 8'h01;
+	SFDP['h016] = 8'h00;
+	SFDP['h017] = 8'hFF;
+	SFDP['h018] = 8'hBF;
+	SFDP['h019] = 8'h00;
+	SFDP['h01A] = 8'h01;
+	SFDP['h01B] = 8'h18;
+	SFDP['h01C] = 8'h00;
+	SFDP['h01D] = 8'h02;
+	SFDP['h01E] = 8'h00;
+	SFDP['h01F] = 8'h01;
+	SFDP['h030] = 8'hFD;
+	SFDP['h031] = 8'h20;
+	SFDP['h032] = 8'hF1;
+	SFDP['h033] = 8'hFF;
+	SFDP['h034] = 8'hFF;
+	SFDP['h035] = 8'hFF;
+	SFDP['h036] = 8'h7F;
+	SFDP['h037] = 8'h00;
+	SFDP['h038] = 8'h44;
+	SFDP['h039] = 8'hEB;
+	SFDP['h03A] = 8'h08;
+	SFDP['h03B] = 8'h6B;
+	SFDP['h03C] = 8'h08;
+	SFDP['h03D] = 8'h3B;
+	SFDP['h03E] = 8'h80;
+	SFDP['h03F] = 8'hBB;
+	SFDP['h040] = 8'hFE;
+	SFDP['h041] = 8'hFF;
+	SFDP['h042] = 8'hFF;
+	SFDP['h043] = 8'hFF;
+	SFDP['h044] = 8'hFF;
+	SFDP['h045] = 8'hFF;
+	SFDP['h046] = 8'h00;
+	SFDP['h047] = 8'hFF;
+	SFDP['h048] = 8'hFF;
+	SFDP['h049] = 8'hFF;
+	SFDP['h04A] = 8'h44;
+	SFDP['h04B] = 8'h0B;
+	SFDP['h04C] = 8'h0C;
+	SFDP['h04D] = 8'h20;
+	SFDP['h04E] = 8'h0D;
+	SFDP['h04F] = 8'hD8;
+	SFDP['h050] = 8'h0F;
+	SFDP['h051] = 8'hD8;
+	SFDP['h052] = 8'h10;
+	SFDP['h053] = 8'hD8;
+	SFDP['h054] = 8'h20;
+	SFDP['h055] = 8'h91;
+	SFDP['h056] = 8'h48;
+	SFDP['h057] = 8'h24;
+	SFDP['h058] = 8'h80;
+	SFDP['h059] = 8'h6F;
+	SFDP['h05A] = 8'h1D;
+	SFDP['h05B] = 8'h81;
+	SFDP['h05C] = 8'hED;
+	SFDP['h05D] = 8'h0F;
+	SFDP['h05E] = 8'h77;
+	SFDP['h05F] = 8'h38;
+	SFDP['h060] = 8'h30;
+	SFDP['h061] = 8'hB0;
+	SFDP['h062] = 8'h30;
+	SFDP['h063] = 8'hB0;
+	SFDP['h064] = 8'hF7;
+	SFDP['h065] = 8'hA9;
+	SFDP['h066] = 8'hD5;
+	SFDP['h067] = 8'h5C;
+	SFDP['h068] = 8'h29;
+	SFDP['h069] = 8'hC2;
+	SFDP['h06A] = 8'h5C;
+	SFDP['h06B] = 8'hFF;
+	SFDP['h06C] = 8'hF0;
+	SFDP['h06D] = 8'h30;
+	SFDP['h06E] = 8'hC0;
+	SFDP['h06F] = 8'h80;
+	SFDP['h100] = 8'hFF;
+	SFDP['h101] = 8'h00;
+	SFDP['h102] = 8'h04;
+	SFDP['h103] = 8'hFF;
+	SFDP['h104] = 8'hF3;
+	SFDP['h105] = 8'h7F;
+	SFDP['h106] = 8'h00;
+	SFDP['h107] = 8'h00;
+	SFDP['h108] = 8'hF5;
+	SFDP['h109] = 8'h7F;
+	SFDP['h10A] = 8'h00;
+	SFDP['h10B] = 8'h00;
+	SFDP['h10C] = 8'hF9;
+	SFDP['h10D] = 8'hFF;
+	SFDP['h10E] = 8'h0D;
+	SFDP['h10F] = 8'h00;
+	SFDP['h110] = 8'hF5;
+	SFDP['h111] = 8'h7F;
+	SFDP['h112] = 8'h00;
+	SFDP['h113] = 8'h00;
+	SFDP['h114] = 8'hF3;
+	SFDP['h115] = 8'h7F;
+	SFDP['h116] = 8'h00;
+	SFDP['h117] = 8'h00;
+	SFDP['h200] = 8'hBF;
+	SFDP['h201] = 8'h26;
+	SFDP['h202] = 8'h58;
+	SFDP['h203] = 8'hFF;
+	SFDP['h204] = 8'hB9;
+	SFDP['h205] = 8'hDF;
+	SFDP['h206] = 8'hFD;
+	SFDP['h207] = 8'hFF;
+	SFDP['h208] = 8'h65;
+	SFDP['h209] = 8'hF1;
+	SFDP['h20A] = 8'h95;
+	SFDP['h20B] = 8'hF1;
+	SFDP['h20C] = 8'h32;
+	SFDP['h20D] = 8'hFF;
+	SFDP['h20E] = 8'h0A;
+	SFDP['h20F] = 8'h12;
+	SFDP['h210] = 8'h23;
+	SFDP['h211] = 8'h46;
+	SFDP['h212] = 8'hFF;
+	SFDP['h213] = 8'h0F;
+	SFDP['h214] = 8'h19;
+	SFDP['h215] = 8'h32;
+	SFDP['h216] = 8'h0F;
+	SFDP['h217] = 8'h19;
+	SFDP['h218] = 8'h19;
+	SFDP['h219] = 8'h03;
+	SFDP['h21A] = 8'h0A;
+	SFDP['h21B] = 8'hFF;
+	SFDP['h21C] = 8'hFF;
+	SFDP['h21D] = 8'hFF;
+	SFDP['h21E] = 8'hFF;
+	SFDP['h21F] = 8'hFF;
+	SFDP['h220] = 8'h00;
+	SFDP['h221] = 8'h66;
+	SFDP['h222] = 8'h99;
+	SFDP['h223] = 8'h38;
+	SFDP['h224] = 8'hFF;
+	SFDP['h225] = 8'h05;
+	SFDP['h226] = 8'h01;
+	SFDP['h227] = 8'h35;
+	SFDP['h228] = 8'h06;
+	SFDP['h229] = 8'h04;
+	SFDP['h22A] = 8'h02;
+	SFDP['h22B] = 8'h32;
+	SFDP['h22C] = 8'hB0;
+	SFDP['h22D] = 8'h30;
+	SFDP['h22E] = 8'h72;
+	SFDP['h22F] = 8'h42;
+	SFDP['h230] = 8'h8D;
+	SFDP['h231] = 8'hE8;
+	SFDP['h232] = 8'h98;
+	SFDP['h233] = 8'h88;
+	SFDP['h234] = 8'hA5;
+	SFDP['h235] = 8'h85;
+	SFDP['h236] = 8'hC0;
+	SFDP['h237] = 8'h9F;
+	SFDP['h238] = 8'hAF;
+	SFDP['h239] = 8'h5A;
+	SFDP['h23A] = 8'hB9;
+	SFDP['h23B] = 8'hAB;
+	SFDP['h23C] = 8'h06;
+	SFDP['h23D] = 8'hEC;
+	SFDP['h23E] = 8'h06;
+	SFDP['h23F] = 8'h0C;
+	SFDP['h240] = 8'h00;
+	SFDP['h241] = 8'h03;
+	SFDP['h242] = 8'h08;
+	SFDP['h243] = 8'h0B;
+	SFDP['h244] = 8'hFF;
+	SFDP['h245] = 8'hFF;
+	SFDP['h246] = 8'hFF;
+	SFDP['h247] = 8'hFF;
+	SFDP['h248] = 8'hFF;
+	SFDP['h249] = 8'h07;
+	SFDP['h24A] = 8'hFF;
+	SFDP['h24B] = 8'hFF;
+	SFDP['h24C] = 8'h02;
+	SFDP['h24D] = 8'h02;
+	SFDP['h24E] = 8'hFF;
+	SFDP['h24F] = 8'h06;
+	SFDP['h250] = 8'h03;
+	SFDP['h251] = 8'h00;
+	SFDP['h252] = 8'hFD;
+	SFDP['h253] = 8'hFD;
+	SFDP['h254] = 8'h04;
+	SFDP['h255] = 8'h04;
+	SFDP['h256] = 8'h00;
+	SFDP['h257] = 8'hFC;
+	SFDP['h258] = 8'h03;
+	SFDP['h259] = 8'h00;
+	SFDP['h25A] = 8'hFE;
+	SFDP['h25B] = 8'hFE;
+	SFDP['h25C] = 8'h02;
+	SFDP['h25D] = 8'h02;
+	SFDP['h25E] = 8'h07;
+	SFDP['h25F] = 8'h0E;
+end
+
+always @(reset) begin
+		
+	IOC = True; //False;			// clear IOC status
+	WSE = False;			// erase suspend status
+	RSTEN = False;			// enable reset disabled
+	read_slow_flag = False;
+	RES = False;			// reserved status bit 6
+	SIO_OUT = 4'h0;			// turn off SIO drivers
+	spi_count = 0;			// clear spi clock counter
+	spi_cmd = SPI_NOP;		// clear SPI command register
+	sqi_cmd = SQI_NOP;		// clear SQI command register
+	l_sqi_cmd = SQI_NOP;
+	l_spi_cmd = SPI_NOP;
+	RSTQIO_cmd  = SPI_NOP;
+	SQI_SPI_mode = False;		// set to spi mode
+	s_BE_flag=False; s_SE_flag=False;
+	SPI_READ_active = False;
+	SPI_READ_QUAD_active=False;
+	SPI_SDOR_active = False;
+	SPI_RDSR_active = False;
+	SPI_QUAD_IO_READ_active=False;
+	Mode_Configuration = 8'hFF;     // default Mode Configuration
+	CE_flag=False; SE_flag=False; BE_flag=False;
+	SPI_SB_active = False;
+	SPI_JEDEC_ID_active = False;
+	SPI_SFDP_active = False;
+	burst_length=0;                 // set burst length to 8
+	SPI_PSID_ip = False;
+	SQI_HS_READ_active=False;
+	SPI_WRSR_PGM_active = False;
+	SPI_RBSPI_active=False;
+	SPI_WBPR_active = False;
+	SPI_nVWLDR_cmd_active=False;
+	SPI_RBPR_active = False;
+	SPI_SDIOR_active = False;
+	SPI_PP_active = False;
+	SQI_PP_active = False;
+	valid_addr = False;					// default valid address
+	valid_data = False;					// default valid data
+	SPI_RDCR_active = False;
+	SPI_QUAD_PP_active = False;
+	SPI_RSID_active = False;
+	erase_active = False;					// erase loop is active
+	WEL = False;						// clear Write enable latch in status register
+	#0 if(BUSY===True) begin				// if busy abort erase/program in progress
+		if(page_program_active === True) begin		// abort PP program 
+		   disable page_program_label;			// abort spi page program in progress
+		   disable Sec_ID_pgm_label;			// Security ID program
+		   #Trp page_program_active = False;		// clear busy on program , wait for program abort time
+		   SPI_PSID_ip = False;				// abort security ID program loop
+		end
+		else if( erase_ip === True) begin		// abort erase SE,BE,CE
+			WSP = False;				// write suspend status
+			disable erase_label;
+			#Tre erase_ip = False;
+		end
+		else if(SPI_LSID_active === True) begin		// abort Security ID lockout
+			disable SPI_LSID_label;
+			#Trp SPI_LSID_active = False;
+			SEC = 1'bx;
+		end
+		else if(SPI_WRSR_active===True) begin		// reset during status register programing time
+			disable SPI_WRSR_PGM;
+			if(wsr_creg[7]===False) begin	// WPEN <- 0
+				#Trp SPI_WRSR_active=False;
+			end
+			else begin			// WPEN <- 1
+				#Tre SPI_WRSR_active=False;
+			end
+		end
+		else if(SPI_nVWLDR_active===True) begin		// reset during WLLDR programing time
+			disable SPI_nVWLDR_label;
+			#Trp SPI_nVWLDR_active=False;
+		end
+		else if(SPI_WRSU_active===True) begin		// reset during suspend #Tws time
+			disable SPI_WRSU_label;			// exit suspent loop
+			#Trp SPI_WRSU_active=False;		// clear suspend loop active flag
+		end
+	end
+	if (WSP === True) begin
+		WSP = False;					// write suspend status
+		pgm_sus_reset = True;
+		#Trp pgm_sus_reset = False;
+	end
+
+	WSP = False;					// write suspend status
+	suspend_addr=0; resume_addr=0;
+	SPI_WRSU_active = False;
+	SPI_PSID_active = False;
+	page_program_active = False;
+	erase_ip = False;
+	SPI_LSID_active = False;
+	SPI_WRSR_active=False;
+	SPI_nVWLDR_active=False;
+	SPI_LSID_active = False;
+	WEL = False;						// clear Write enable latch in status register
+end
+
+//-------------------------------------------------------------
+// protection functions, return True/False for read protection
+// given and address
+//-------------------------------------------------------------
+function Read_proT;
+input [AF_MSB:0] addr;                      // address
+reg return_value;
+reg [AF_MSB+1:0] taddr;
+begin
+	// clear upper address bits that are unused
+	taddr = addr & (Memsize-1);
+
+	return_value = True;		// set default return value
+        if(taddr < (Memsize-(Kilo*32)) && taddr >= (Kilo*32)) return_value = False;		// check Memory that has no protection
+        else if(taddr < (Kilo*8)) return_value  = protect[PROTECT_REG_MSB-14];	// check lower 8K
+        else if(taddr < (Kilo*16)) return_value = protect[PROTECT_REG_MSB-12];	// next 8k
+        else if(taddr < (Kilo*24)) return_value = protect[PROTECT_REG_MSB-10];	// next 8k
+        else if(taddr < (Kilo*32)) return_value = protect[PROTECT_REG_MSB-8];	// next 8k
+
+        else if(taddr >= (Memsize-(Kilo*8))) return_value = protect[PROTECT_REG_MSB-0];		// check top 8K
+        else if(taddr >= (Memsize-(Kilo*16))) return_value = protect[PROTECT_REG_MSB-2];	// next 8K
+        else if(taddr >= (Memsize-(Kilo*24))) return_value = protect[PROTECT_REG_MSB-4];	// next 8K
+        else if(taddr >= (Memsize-(Kilo*32))) return_value = protect[PROTECT_REG_MSB-6];	// next 8K
+
+	Read_proT = return_value;	// return True/False for read protection at this address
+end
+endfunction
+
+//-------------------------------------------------------------
+// protection functions, return True/False for write protection
+// given and address True = protected
+//-------------------------------------------------------------
+function Write_proT;
+input [AF_MSB:0] addr;                  // address
+reg [AF_MSB+1:0] address;               // address
+reg return_value;
+reg [7:0] index;
+reg [AF_MSB+1:0] taddr;
+begin
+	// clear upper address bits that are unused
+	taddr = addr; taddr = taddr & (Memsize-1);
+
+	return_value = True;		// set default return value
+	if(taddr < (Memsize-(Kilo*64)) && taddr >= (Kilo*64)) begin				// check address 64K --> Memsize-64K
+		index = 0;									// index to bottom of table
+		address=(Kilo*64);								// starting address at bottom of table
+		while(address < (Memsize-(Kilo*64)) && return_value===True) begin		// loop through each 64K block
+			if(taddr >= address && taddr < (address + (Kilo*64))) begin
+				if(protect_or[index] === False) return_value = False;		// check protect flag
+			end
+			index = index + 1;							// increment protect array pointer
+			address = address+(Kilo*64);						// increment to next 64K protection block
+		end
+	end
+	// check lower 64k of Memory
+        else if(taddr < (Kilo*8) ) begin if(protect_or[PROTECT_REG_MSB-15]==False) return_value = False; end // check lower 8K
+        else if(taddr < (Kilo*16)) begin if(protect_or[PROTECT_REG_MSB-13]==False) return_value = False; end // next 8k
+        else if(taddr < (Kilo*24)) begin if(protect_or[PROTECT_REG_MSB-11]==False) return_value = False; end // next 8k
+        else if(taddr < (Kilo*32)) begin if(protect_or[PROTECT_REG_MSB- 9]==False) return_value = False; end // next 8k
+        else if(taddr < (Kilo*64)) begin if(protect_or[PROTECT_REG_MSB-17]==False) return_value = False; end // next 32k
+	// check upper 64k of Memory
+        else if(taddr >= (Memsize-(Kilo*8)) ) begin if(protect_or[PROTECT_REG_MSB-1]==False) return_value = False; end // check top 8K
+        else if(taddr >= (Memsize-(Kilo*16))) begin if(protect_or[PROTECT_REG_MSB-3]==False) return_value = False; end // next 8K
+        else if(taddr >= (Memsize-(Kilo*24))) begin if(protect_or[PROTECT_REG_MSB-5]==False) return_value = False; end // next 8K
+        else if(taddr >= (Memsize-(Kilo*32))) begin if(protect_or[PROTECT_REG_MSB-7]==False) return_value = False; end // next 8K
+        else if(taddr >= (Memsize-(Kilo*64))) begin if(protect_or[PROTECT_REG_MSB-16]==False) return_value = False; end // next 32K
+	Write_proT = return_value;	// return True/False for read protection at this address
+end
+endfunction
+
+//----------------------------------------------------------------
+// check chip for any protection return False if OK to erase chip
+//----------------------------------------------------------------
+function Chip_proT;
+input [AF_MSB:0] addr;                      // address, always 0
+reg return_value;
+begin
+	return_value = |protect_or[PROTECT_REG_MSB:0];
+	Chip_proT = return_value;
+end
+endfunction
+
+
+//----------------------------------------------------------------------------------------
+// check for program address matches erase address during suspend of block or sector erase
+// in suspend mode verify that the address to be programed does not match suspended
+// sector or block, return True if match
+//----------------------------------------------------------------------------------------
+function ERASE_PGM;
+input [AF_MSB:0] erase_address_in;	// erase address of suspended block/sector
+input [AF_MSB:0] pgm_address_in;	// address to program, check if it's in suspended sector/block
+reg return_value;
+reg [AF_MSB+1:0] erase_address,pgm_address;
+begin
+	// clear unused upper address bits
+	erase_address = erase_address_in & (Memsize-1);
+	pgm_address = pgm_address_in & (Memsize-1);
+
+	return_value = False;			// default to no match
+	if(WSE===True) begin			// make sure you are in erase suspend mode
+		if(s_SE_flag === True) begin	// check if SE when suspended, I.E. 4K Sector Size
+			if(erase_address[ADDR_MSB:12] === pgm_address[ADDR_MSB:12]) return_value = True;
+		end
+		// ---------------------------------------------------------------------
+		// if block erase you must check address for block size
+		// ---------------------------------------------------------------------
+		else if(s_BE_flag === True) begin				// check if BE when suspended
+			if(erase_address < (Kilo * 32)) begin 			// 8K block size
+				if(erase_address[ADDR_MSB:13] === pgm_address[ADDR_MSB:13]) return_value = True;
+			end
+			else if( erase_address < (Kilo * 64)) begin		// 32k Block size
+				if(erase_address[ADDR_MSB:15] === pgm_address[ADDR_MSB:15]) return_value = True;
+			end
+			else if( erase_address >= (Memsize-(Kilo * 32))) begin	// 8K block size
+				if(erase_address[ADDR_MSB:13] === pgm_address[ADDR_MSB:13]) return_value = True;
+			end
+			else if( erase_address >= Memsize-(Kilo * 64)) begin	// 32k Block size
+				if(erase_address[ADDR_MSB:15] === pgm_address[ADDR_MSB:15]) return_value = True;
+			end
+			else begin						// 64K block size
+				if(erase_address[ADDR_MSB:16] === pgm_address[ADDR_MSB:16]) return_value = True;
+			end
+		end
+	end
+	ERASE_PGM = return_value;
+end
+endfunction
+
+//----------------------------------------------------------------------------------------
+// check for program address matches erase address during suspend of page program.
+// In suspend mode verify that the address to be erased does not match suspended
+// program address, return True if addresses match I.E. abort programing
+//----------------------------------------------------------------------------------------
+function PGM_ERASE;
+input [AF_MSB:0] erase_address_in;	// erase address of block/sector to be erased
+input [AF_MSB:0] pgm_address_in;	// suspended page program address
+reg return_value;
+reg [AF_MSB+1:0] erase_address, pgm_address;
+begin
+	// clear upper unused address bits
+	erase_address = erase_address_in & (Memsize-1);
+	pgm_address = pgm_address_in & (Memsize-1);
+
+	return_value = False;	// default to no match
+	if(WSP===True) begin	// make sure you are in program suspend mode
+		if(l_spi_cmd===SPI_SE || l_sqi_cmd===SQI_SE) begin	// sector erase 4K size ?
+			if(erase_address[AF_MSB:12] === pgm_address[AF_MSB:12]) return_value = True;
+		end
+		// ---------------------------------------------------------------------
+		// if block erase you must check address for block size
+		// ---------------------------------------------------------------------
+		else if(l_spi_cmd===SPI_BE || l_sqi_cmd===SQI_BE) begin
+			if(erase_address < (Kilo * 32)) begin 			// 8K block size
+				if(erase_address[AF_MSB:13] === pgm_address[AF_MSB:13]) return_value = True;
+			end
+			else if( erase_address < (Kilo * 64)) begin		// 32k Block size
+				if(erase_address[AF_MSB:15] === pgm_address[AF_MSB:15]) return_value = True;
+			end
+			else if( erase_address >= (Memsize-(Kilo * 32))) begin	// 8K block size
+				if(erase_address[AF_MSB:13] === pgm_address[AF_MSB:13]) return_value = True;
+			end
+			else if( erase_address >= Memsize-(Kilo * 64)) begin	// 32k Block size
+				if(erase_address[AF_MSB:15] === pgm_address[AF_MSB:15]) return_value = True;
+			end
+			else begin						// 64K block size
+				if(erase_address[AF_MSB:16] === pgm_address[AF_MSB:16]) return_value = True;
+			end
+		end
+	end
+	PGM_ERASE = return_value;
+end
+endfunction
+`endprotect
+
+endmodule
\ No newline at end of file
diff --git a/verilog/dv/caravel/user_proj_example/sw/Makefile b/verilog/dv/caravel/user_proj_example/sw/Makefile
new file mode 100644
index 0000000..32b5433
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/Makefile
@@ -0,0 +1,14 @@
+#GCC_PATH := /ef/apps/bin
+GCC_PATH ?= /usr/local/opt/riscv-gnu-toolchain/bin
+GCC_PREFIX ?= riscv32-unknown-elf
+SIM_SOC ?= 1
+
+name := test
+
+%.hex: $(name).c n5_drv.c crt0.S link.ld
+	$(GCC_PATH)/$(GCC_PREFIX)-gcc -DSIM_SOC=$(SIM_SOC) -g -Wall  -falign-functions=4 -march=rv32imc -mabi=ilp32 -nostdlib -mstrict-align -T link.ld -o $(name).elf -lgcc crt0.S n5_drv.c $(name).c  -lgcc
+	$(GCC_PATH)/$(GCC_PREFIX)-objcopy -O binary $(name).elf $(name).bin
+	$(GCC_PATH)/$(GCC_PREFIX)-objcopy -O verilog $(name).elf $(name).hex
+	$(GCC_PATH)/$(GCC_PREFIX)-objdump -dS $(name).elf > $(name).lst
+
+all: $(name).hex
diff --git a/verilog/dv/caravel/user_proj_example/sw/README.md b/verilog/dv/caravel/user_proj_example/sw/README.md
new file mode 100644
index 0000000..05e26fe
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/README.md
@@ -0,0 +1,73 @@
+## I/O Map (WIP)
+### GPIO Port (Base: 0x48000000)
+|Register|Offset|Description|
+|----------|---|------------|
+| Data In | 0x00 | 14 pins only|
+| Data Out| 0x04| 14 pins only|
+| Pull Up Enable|0x08| Currently not used|
+| Pull Down Enable|0x0C| Currently not used|
+| Direction|0x10| 1: Input, 0: Output|
+| Interrupt Mask| 0x14||
+
+
+### UART Modules (UART0 Base: 0x40100000, UART1 Base: 0x40200000)
+
+|Register|Offset|Description|
+|----------|---|------------|
+| TX/RX Data | 0x00 | Read/Write from/to RX/TX FIFOs |
+| STATUS |0x04| Read only|
+| CONTROL| 0x04| Write only|
+| PRESCALER|0x08| Prescaler for the baud rate generator|
+| Interrupts Mask| 0x0C||
+| TX FIFO Threshold|0x10||
+| RX FIFO Threshold|0x14||
+
+
+### SPI Modules (SPI0 Base: 0x40200000, SPI1 Base: 0x40300000 )
+
+|Register|Offset|Description|
+|----------|---|------------|
+| Data |||
+| Control |||
+| Configuration |||
+| Status |||
+| Interrupt Mask|||
+
+### I2C Modules (I2C0 Base: 0x40400000, I2C1 Base: 0x40500000)
+
+|Register|Offset|Description|
+|----------|---|------------|
+
+
+### Pulse Width Modulors (PWM0 Base: 0x40600000, PWM1 Base: 0x40700000)
+
+|Register|Offset|Description|
+|----------|---|------------|
+| CMP1 | 0x04| Compare 1 Register (period)|
+| CMP2 | 0x08 | Compare 2 Register (level change) |
+| PRESCALER| 0x10| Prescaler Register, tmr_clk = clk / (PRESCALER+1)|
+| CTRL| 0x20| Control Register, 0: Enable PWM| 
+
+
+``PWM Period = (CMP1 + 1)/tmr_clk = (CMP1 + 1)*(PRESCALER + 1)/clk``
+
+``PWM Duty Cyle = 1 - (CMP1 + 1)/(CMP2 + 1)``
+
+
+### 32-bit Timer Modules (Base: 0x40800000, 0x40900000, 0x40A00000, 0x40B00000)
+
+|Register|Offset|Description|
+|----------|---|------------|
+| TIMER | 0x00| Current Count |
+| Prescaler | 0x04| |
+| Compare | 0x08| Compare Register|
+| Status|||
+| Overflow Clear |||
+| Timer Enable |||
+| Interrupt Mask |||
+
+
+
+### 32-bit Watch Dog Timer Modules (Base: 0x40C00000, 0x40D00000)
+|Register|Offset|Description|
+|----------|---|------------|
diff --git a/verilog/dv/caravel/user_proj_example/sw/crt0.S b/verilog/dv/caravel/user_proj_example/sw/crt0.S
new file mode 100755
index 0000000..3f62dde
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/crt0.S
@@ -0,0 +1,170 @@
+#define	EXT_MUL
+
+#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \
+.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0))
+
+#define ext_mul(_rd, _rs1, _rs2) \
+r_type_insn(0b0000000, _rs2, _rs1, 0b111, _rd, 0b0001011)
+
+.macro wrtmrcmp reg
+	csrrw   zero, 0xC03, \reg
+.endm
+
+.macro wrmie reg
+	csrrw   zero, mie, \reg
+.endm
+
+.section .text
+	.global _start
+	
+
+	.org 0
+reset_vector:
+	j		reset_hand
+	
+	.org 4
+nmi_vector:
+	j		nmi_hand
+	
+	.org 8
+tmr_vector:
+	j		tmr_hand
+	
+	.org	12
+ecall_vector:
+	j		ecall_hand 
+	
+	.org	16
+ebreak_vector:
+	j		ebreak_hand
+	
+	.org	24
+	j		.
+
+	.org	28
+	j		.
+	
+	.org	64		# IRQ 0
+	j		IRQ
+
+	.org	68		# IRQ 1
+	j		IRQ
+	
+	.org	72
+	j		IRQ
+
+	.org	76
+	j		IRQ
+
+	.org	80
+	j		IRQ
+
+	.org	84
+	j		IRQ
+
+	.org	88
+	j		IRQ
+
+	.org	92		# IRQ 7
+	j		IRQ
+
+	.org	96		# IRQ 8
+	j		IRQ
+
+	.org	100		# IRQ 9
+	j		IRQ
+
+	.org	104		# IRQ 10
+	j		IRQ
+
+	.org 128
+reset_hand:
+	li t0, 1<<(4*2+1)
+	csrw 0x7c0,t0  # make IO region (4) side effect
+	# disable interrupts
+	li t0, 0	#disable interrupts; use 5 to enable interrupts and IRQ
+	wrmie t0
+  _start:
+	li	s0, 0x20000000
+	li	s1, 55
+	sw	s1, 0(s0)
+	addi s1, s1, 10
+	sw	s1, 4(s0)
+	lw	s2, 0(s0)
+	lw	s3, 4(s0)
+	
+	li	s0, 0
+	li	s1, 0
+	li	s2, 0
+	li	s3, 0
+	li	s4, 0
+	li	s5, 0
+	li	s6, 0
+	li	s7, 0
+	li	s8, 0
+	li	s9, 0
+	li	s10, 0
+	li	s11, 0
+	j		___App
+
+	.align 8
+
+nmi_hand:
+tmr_hand:
+ecall_hand:
+ebreak_hand:
+irq0_hand:
+	j IRQ
+	#li t0, 0xBAD00BED
+	#mret
+
+.align 8
+#ifdef EXT_MUL
+.global __mulsi3
+__mulsi3:
+        #ext_mul(10, 10, 11)
+		la	t0, 0x49000000
+		sw	a0, 0(t0)
+		sw	a1, 4(t0)
+		lw	a0, 0(t0)
+        ret
+#endif
+
+.align 8
+___App:
+	# Initialize the BSS section with 0s
+init_bss:	
+	la a0, __bss_start__
+	la a1, __bss_end__
+	bge a0, a1, end_init_bss
+loop_init_bss:
+	sw zero, 0(a0)
+	addi a0, a0, 4
+	blt a0, a1, loop_init_bss
+end_init_bss:
+
+	# Move initialized data to RAM
+init_data:
+	la a0, __idata__
+	la a1, __data_start__
+	la a2, __data_end__
+	bge a1, a2, end_init_data
+loop_init_data:
+	lw a3, 0(a0)
+	sw a3, 0(a1)
+	addi a0, a0, 4
+	addi a1, a1, 4
+	blt a1, a2, loop_init_data
+end_init_data:
+
+	# Initialize the stack pointer!
+	lui sp, %hi(_fstack)
+	addi sp, sp, %lo(_fstack)
+	jal main
+
+end_loop:
+	j 	end_loop
+
+	.align 8
+
+
diff --git a/verilog/dv/caravel/user_proj_example/sw/link.ld b/verilog/dv/caravel/user_proj_example/sw/link.ld
new file mode 100644
index 0000000..e31cece
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/link.ld
@@ -0,0 +1,43 @@
+ENTRY(_start)
+MEMORY
+{
+    FLASH (x) :
+        ORIGIN = 0x0
+        LENGTH = 1M
+    SRAM (rwx) :
+        ORIGIN = 0x20000000,
+        LENGTH = 8K
+    stack (rw):
+        ORIGIN = 0x20000000 + 6K
+        LENGTH = 2K
+}
+
+PROVIDE(_fstack = ORIGIN(stack) + LENGTH(stack) - 4);
+
+SECTIONS
+{
+    .text : {
+        *(.text*)
+        *(.rodata*)
+        . = ALIGN(8);
+        __idata__ = .;
+    } > FLASH
+    
+    .bss (NOLOAD) :
+    {
+        __bss_start__ = .;
+        *(.bss*)
+        *(COMMON)
+        __bss_end__ = .;
+    } > SRAM
+
+    .data : 
+    {
+        __data_start__ = .;
+        *(.data*);
+        __data_end__ = .;
+        
+    } > SRAM AT > FLASH
+
+}
+
diff --git a/verilog/dv/caravel/user_proj_example/sw/n5_drv.c b/verilog/dv/caravel/user_proj_example/sw/n5_drv.c
new file mode 100644
index 0000000..41e1cb6
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/n5_drv.c
@@ -0,0 +1,415 @@
+#include "n5_regs.h"
+#include "n5_drv.h"
+
+/* GPIO */
+void gpio_set_dir(unsigned int d) { 
+    *GPIO_DIR = d; 
+}
+
+void gpio_write(unsigned int d) { 
+    *GPIO_DOUT = d;
+}
+
+unsigned int gpio_read() {  
+    return *GPIO_DIN;
+}
+
+void gpio_pull (unsigned char d){
+    *GPIO_PD = 0;
+    *GPIO_PU = 0;
+    if(d==0) *GPIO_PD = 1;
+    else *GPIO_PU = 1;
+}
+
+void gpio_im(unsigned int im){
+    *GPIO_IM = im;
+}
+
+/* UART */
+int uart_init(unsigned int n, unsigned int prescaler){
+    if(n>1) return -1;
+    if(n==1){
+        *UART1_PRESCALER = prescaler;
+        *UART1_IM = 0;
+        *UART1_CTRL = 1;
+    }
+    else {
+        *UART0_PRESCALER = prescaler;
+        *UART0_IM = 0;
+        *UART0_CTRL = 1;
+    }
+}
+
+int uart_puts(unsigned int n, unsigned char *msg, unsigned int len){
+    int i;
+    if(n>1) return -1;
+    if(n==0){
+        for(i=0; i<len; i++){
+            while(*UART0_STATUS&1); // TX Not Full
+            *UART0_DATA = msg[i]; 
+        }
+    }  else {
+        for(i=0; i<len; i++){
+            while(*UART1_STATUS&1); // TX Not Full
+            *UART1_DATA = msg[i]; 
+        }
+    }   
+    return 0;
+}
+
+int uart_gets(unsigned int n, unsigned char *msg, unsigned int len){
+    int i;
+    if(n>1) return -1;
+    if(n==0){
+        for(i=0; i<len; i++){
+            while(*UART0_STATUS&8); // RX Not Empty
+            msg[i] = *UART0_DATA;  
+        }
+    } else {
+        for(i=0; i<len; i++){
+            while(*UART1_STATUS&8); // RX Not Empty
+            msg[i] = *UART1_DATA;  
+        }
+    }    
+    return 0;
+}
+
+/* SPI */
+int spi_init(unsigned int n, unsigned char cpol, unsigned char cpha, unsigned char clkdiv){
+  unsigned int cfg_value = 0;
+  cfg_value |=  cpol;
+  cfg_value |=  (cpha << 1);
+  cfg_value |=  ((unsigned int)clkdiv << 2);
+  if(n>1) return -1;
+  if(n==0)  *SPI0_CFG = cfg_value;
+  else *SPI1_CFG = cfg_value;
+}
+
+unsigned int spi_status(unsigned int n){
+    if(n>1) return -1;
+    if(n==0)    
+        return *SPI0_STATUS & 1;
+    else 
+        return *SPI1_STATUS & 1;
+}
+
+unsigned char spi_read(unsigned int n){
+    if(n>1) return -1;
+    if(n==0)  
+        return *SPI0_DATA;
+    else 
+        return *SPI1_DATA;
+}
+
+int spi_write(unsigned int n, unsigned char data){
+    if(n>1) return -1;
+    if(n==0) {
+        *SPI0_DATA =  data;
+        SET_BIT(*SPI0_CTRL, SPI_GO_BIT);
+        CLR_BIT(*SPI0_CTRL, SPI_GO_BIT);
+        while(!spi_status(n));
+    } else{
+        *SPI1_DATA =  data;
+        SET_BIT(*SPI1_CTRL, SPI_GO_BIT);
+        CLR_BIT(*SPI1_CTRL, SPI_GO_BIT);
+        while(!spi_status(n));
+    }
+    return 0;
+}
+
+int spi_start(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) {    
+        SET_BIT(*SPI0_CTRL, SPI_SS_BIT);
+    } else {
+        SET_BIT(*SPI1_CTRL, SPI_SS_BIT);
+    }
+    return 0;
+}
+
+int spi_end(unsigned int n){
+    if(n>1) return -1;
+    if(n==0)    
+        CLR_BIT(*SPI0_CTRL, SPI_SS_BIT);
+    else 
+        CLR_BIT(*SPI1_CTRL, SPI_SS_BIT);
+    return 0;
+}
+
+/* i2c */
+int i2c_init(unsigned int n, unsigned int pre){
+    if(n>1) return -1;
+    if(n==0) {
+        *(I2C0_PRE_LO) = pre & 0xff;
+        *(I2C0_PRE_HI) = pre & 0xff00;
+        *(I2C0_CTRL) = I2C_CTRL_EN | I2C_CTRL_IEN;
+    } else {
+        *(I2C1_PRE_LO) = pre & 0xff;
+        *(I2C1_PRE_HI) = pre & 0xff00;
+        *(I2C1_CTRL) = I2C_CTRL_EN | I2C_CTRL_IEN;
+    }
+}
+
+int i2c_start(unsigned int n, unsigned char control)
+{
+    if(n>1) return -1;
+    if(n==0) {
+        *(I2C0_TX) = control;
+        *(I2C0_CMD) = I2C_CMD_STA | I2C_CMD_WR;
+        while( ((*I2C0_STAT) & I2C_STAT_TIP) != 0 );
+
+        if( ((*I2C0_STAT) & I2C_STAT_RXACK)) {
+            *(I2C0_CMD) = I2C_CMD_STO;
+            return 0;
+        }
+        return 1;
+
+    } else {
+        *(I2C1_TX) = control;
+        *(I2C1_CMD) = I2C_CMD_STA | I2C_CMD_WR;
+        while( ((*I2C1_STAT) & I2C_STAT_TIP) != 0 );
+
+        if( ((*I2C1_STAT) & I2C_STAT_RXACK)) {
+            *(I2C1_CMD) = I2C_CMD_STO;
+            return 0;
+        }
+        return 1;
+    }
+}
+
+int i2c_sendByte(unsigned char b){
+        *(I2C0_TX) = b;
+        *(I2C0_CMD) = I2C_CMD_WR;
+        while( (*I2C0_STAT) & I2C_STAT_TIP );
+        if( ((*I2C0_STAT) & I2C_STAT_RXACK )){
+            *(I2C0_CMD) = I2C_CMD_STO;
+            return 0;
+        }  
+        return 1;
+}
+
+int i2c_sendHWord(unsigned int hw){
+        if(i2c_sendByte(hw>>8) == 0)
+            return 0;
+        return i2c_sendByte(hw&0xFF);
+}
+
+int i2c_readByte(){
+    *(I2C0_CMD) = I2C_CMD_RD;
+    while( ((*I2C0_STAT) & I2C_STAT_TIP) != 0 );
+    return *(I2C0_RX);
+}
+
+int i2c_stop(){
+    *(I2C0_CMD) = I2C_CMD_STO;
+}
+
+
+int i2c_send(unsigned int n, unsigned char saddr, unsigned char sdata){
+    if(n>1) return -1;
+    if(n==0) {
+        *(I2C0_TX) = saddr;
+        *(I2C0_CMD) = I2C_CMD_STA | I2C_CMD_WR;
+        while( ((*I2C0_STAT) & I2C_STAT_TIP) != 0 );
+        //(*I2C_STAT) & I2C_STAT_TIP ;
+
+        if( ((*I2C0_STAT) & I2C_STAT_RXACK)) {
+            *(I2C0_CMD) = I2C_CMD_STO;
+            return 0;
+        }
+        *(I2C0_TX) = sdata;
+        *(I2C0_CMD) = I2C_CMD_WR;
+        while( (*I2C0_STAT) & I2C_STAT_TIP );
+        *(I2C0_CMD) = I2C_CMD_STO;
+        if( ((*I2C0_STAT) & I2C_STAT_RXACK ))
+            return 0;
+        else
+            return 1;
+    } else {
+        *(I2C1_TX) = saddr;
+        *(I2C1_CMD) = I2C_CMD_STA | I2C_CMD_WR;
+        while( ((*I2C1_STAT) & I2C_STAT_TIP) != 0 );
+        //(*I2C_STAT) & I2C_STAT_TIP ;
+
+        if( ((*I2C1_STAT) & I2C_STAT_RXACK)) {
+            *(I2C1_CMD) = I2C_CMD_STO;
+            return 0;
+        }
+        *(I2C1_TX) = sdata;
+        *(I2C1_CMD) = I2C_CMD_WR;
+        while( (*I2C1_STAT) & I2C_STAT_TIP );
+        *(I2C1_CMD) = I2C_CMD_STO;
+        if( ((*I2C1_STAT) & I2C_STAT_RXACK ))
+            return 0;
+        else
+            return 1;
+    }
+}
+
+/* PWM */
+int pwm_init(unsigned int n, unsigned int cmp1, unsigned int cmp2, unsigned int pre){
+  if(n>1) return -1;
+    if(n==0) {
+        *PWM0_CMP1 = cmp1;
+        *PWM0_CMP2 = cmp2;
+        *PWM0_PRE = pre;
+    } else {
+        *PWM1_CMP1 = cmp1;
+        *PWM1_CMP2 = cmp2;
+        *PWM1_PRE = pre;
+    }
+    return 0;
+}
+
+int pwm_enable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *PWM0_CTRL = 0x1;
+    else
+        *PWM0_CTRL = 0x1;
+    return 0;
+}
+
+int pwm_disable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *PWM0_CTRL = 0x0;
+    else
+        *PWM0_CTRL = 0x0;
+    return 0;
+}
+
+/* Timers */
+int tmr_init(unsigned int n, unsigned int pre, unsigned int cmp){
+  if(n>1) return -1;
+    if(n==0) {
+        *TMR0_CMP = cmp;
+        *TMR0_PRE = pre;
+        *TMR0_OVCLR = 1;
+        *TMR0_OVCLR = 0;        
+    } else {
+        *TMR1_CMP = cmp;
+        *TMR1_PRE = pre;
+        *TMR1_OVCLR = 1;
+        *TMR1_OVCLR = 0;        
+    }
+    return 0;
+}
+
+int tmr_enable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *TMR0_EN = 0x1;
+    else
+        *TMR1_EN = 0x1;
+    return 0;
+}
+
+int tmr_disable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *TMR0_EN = 0x0;
+    else
+        *TMR1_EN = 0x0;
+    return 0;
+}
+
+int tmr_wait(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        while(*TMR0_STATUS == 0);
+    else
+        while(*TMR1_STATUS == 0);
+    return 0;
+}
+
+int tmr_ei(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *TMR0_IM == 1;
+    else
+        *TMR1_IM == 1;
+    return 0;
+}
+
+int tmr_di(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *TMR0_IM == 0;
+    else
+        *TMR1_IM == 0;
+    return 0;
+}
+
+int tmr_clrov(unsigned int n){
+    if(n>1) return -1;
+    if(n==0)  {
+        *TMR0_OVCLR == 1;
+        *TMR0_OVCLR == 0;
+    }
+    else{
+        *TMR1_OVCLR == 1;
+        *TMR1_OVCLR == 0;
+    }
+    return 0;
+}
+
+int tmr_read(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        return *TMR0;
+    else
+        return *TMR1;
+    return 0;
+}
+
+/* Watch dog Timers */
+int wdt_init(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) {
+        *WDT0_OVCLR = 1;
+        *WDT0_OVCLR = 0;        
+    } else {
+        *WDT1_OVCLR = 1;
+        *WDT1_OVCLR = 0;          
+    }
+    return 0;
+}
+
+int wdt_enable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *WDT0_EN = 0x1;
+    else
+        *WDT1_EN = 0x1;
+    return 0;
+}
+
+int wdt_load(unsigned int n, unsigned int val) {
+    if(n>1) return -1;
+    if(n==0) {
+        *WDT0_LOAD = val;
+    } else {
+        *WDT1_LOAD = val;
+    }
+    return 0;
+}
+
+int wdt_read(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) {
+        return *WDT0_TMR;
+    } else {
+        return *WDT1_TMR;
+    }
+    return 0;
+}
+
+int wdt_disable(unsigned int n){
+    if(n>1) return -1;
+    if(n==0) 
+        *WDT0_EN = 0x0;
+    else
+        *WDT1_EN = 0x0;
+    return 0;
+}
\ No newline at end of file
diff --git a/verilog/dv/caravel/user_proj_example/sw/n5_drv.h b/verilog/dv/caravel/user_proj_example/sw/n5_drv.h
new file mode 100644
index 0000000..e6817ef
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/n5_drv.h
@@ -0,0 +1,33 @@
+/* GPIO */
+void gpio_set_dir(unsigned int );
+void gpio_write(unsigned int );
+unsigned int gpio_read(); 
+void gpio_pull (unsigned char );
+void gpio_im(unsigned int );
+
+/* UART */
+int uart_init(unsigned int , unsigned int );
+int uart_puts(unsigned int , unsigned char *, unsigned int );
+int uart_gets(unsigned int , unsigned char *, unsigned int );
+
+/* SPI */
+int spi_init(unsigned int , unsigned char , unsigned char , unsigned char );
+unsigned int spi_status(unsigned int );
+unsigned char spi_read(unsigned int );
+int spi_write(unsigned int , unsigned char );
+int spi_start(unsigned int );
+int spi_end(unsigned int );
+
+/* i2c */
+int i2c_init(unsigned int , unsigned int );
+int i2c_send(unsigned int , unsigned char , unsigned char );
+int i2c_start(unsigned int , unsigned char );
+int i2c_sendByte(unsigned char );
+int i2c_sendHWord(unsigned int );
+int i2c_readByte();
+int i2c_stop();
+
+/* PWM */
+int pwm_init(unsigned int, unsigned int, unsigned int, unsigned int);
+int pwm_enable(unsigned int);
+int pwm_disable(unsigned int);
diff --git a/verilog/dv/caravel/user_proj_example/sw/n5_int.h b/verilog/dv/caravel/user_proj_example/sw/n5_int.h
new file mode 100644
index 0000000..e097417
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/n5_int.h
@@ -0,0 +1,4 @@
+
+void IRQ (void) __attribute__ ((interrupt ("machine")));
+
+
diff --git a/verilog/dv/caravel/user_proj_example/sw/n5_regs.h b/verilog/dv/caravel/user_proj_example/sw/n5_regs.h
new file mode 100644
index 0000000..5646a67
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/n5_regs.h
@@ -0,0 +1,222 @@
+#define     SET_BIT(reg, bit)       (reg) = ((reg) | (1<<bit))
+#define     CLR_BIT(reg, bit)       (reg) = ((reg) & (~(1<<bit)))
+#define     CHK_BIT(reg, bit)       ((reg) & (1<<bit))
+
+
+#define AHB_GPIO_BASE_ADDR      0x48000000
+
+#define APB_UART_0_BASE_ADDR    0x40000000
+#define APB_UART_1_BASE_ADDR    0x40100000
+#define APB_SPI_0_BASE_ADDR     0x40200000
+#define APB_SPI_1_BASE_ADDR     0x40300000
+#define APB_I2C_0_BASE_ADDR     0x40400000
+#define APB_I2C_1_BASE_ADDR     0x40500000
+#define APB_PWM32_0_BASE_ADDR   0x40600000
+#define APB_PWM32_1_BASE_ADDR   0x40700000
+#define APB_TIMER32_0_BASE_ADDR 0x40800000
+#define APB_TIMER32_1_BASE_ADDR 0x40900000
+#define APB_TIMER32_2_BASE_ADDR 0x40a00000
+#define APB_TIMER32_3_BASE_ADDR 0x40b00000
+#define APB_WDT32_0_BASE_ADDR   0x40c00000
+#define APB_WDT32_1_BASE_ADDR   0x40d00000
+
+/* GPIO */
+#define GPIO_DIN_REG            0x00000000
+#define GPIO_DOUT_REG           0x00000008
+#define GPIO_PU_REG             0x00000010
+#define GPIO_PD_REG             0x00000018
+#define GPIO_DIR_REG            0x00000020
+#define GPIO_IM_REG             0x00000028
+
+unsigned int volatile *const GPIO_DIN = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_DIN_REG);
+unsigned int volatile *const GPIO_DOUT = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_DOUT_REG);
+unsigned int volatile *const GPIO_PU = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_PU_REG);
+unsigned int volatile *const GPIO_PD = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_PD_REG);
+unsigned int volatile *const GPIO_DIR = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_DIR_REG);
+unsigned int volatile *const GPIO_IM = (unsigned int *)(AHB_GPIO_BASE_ADDR + GPIO_IM_REG);
+
+/* UART Modules */
+#define UART_DATA_REG           0x00000000
+#define UART_STATUS_REG         0x00000008
+#define UART_CTRL_REG           0x00000010
+#define UART_PRESCALER_REG      0x00000018
+#define UART_IM_REG             0x00000020
+#define UART_TXFIFOTR_REG       0x00000028
+#define UART_RXFIFOTR_REG       0x00000030
+
+unsigned int volatile *const UART0_DATA = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_DATA_REG);
+unsigned int volatile *const UART0_STATUS = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_STATUS_REG);
+unsigned int volatile *const UART0_CTRL = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_CTRL_REG);
+unsigned int volatile *const UART0_PRESCALER = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_PRESCALER_REG);
+unsigned int volatile *const UART0_IM = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_IM_REG);
+unsigned int volatile *const UART0_TXTH = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_TXFIFOTR_REG);
+unsigned int volatile *const UART0_RXTH = (unsigned int *)(APB_UART_0_BASE_ADDR + UART_RXFIFOTR_REG);
+
+unsigned int volatile *const UART1_DATA = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_DATA_REG);
+unsigned int volatile *const UART1_STATUS = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_STATUS_REG);
+unsigned int volatile *const UART1_CTRL = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_CTRL_REG);
+unsigned int volatile *const UART1_PRESCALER = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_PRESCALER_REG);
+unsigned int volatile *const UART1_IM = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_IM_REG);
+unsigned int volatile *const UART1_TXTH = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_TXFIFOTR_REG);
+unsigned int volatile *const UART1_RXTH = (unsigned int *)(APB_UART_1_BASE_ADDR + UART_RXFIFOTR_REG);
+
+/* SPI Master Controllers */
+#define SPI_DATA_REG            0x00000000
+#define SPI_CFG_REG             0x00000008
+#define SPI_STATUS_REG          0x00000010
+#define SPI_CTRL_REG            0x00000018
+#define SPI_IM_REG              0x00000020
+
+// CTRL register fields
+#define     SPI_GO_BIT          0x0
+#define     SPI_GO_SIZE         0x1
+#define     SPI_SS_BIT          0x1
+#define     SPI_SS_SIZE         0x1
+
+// CFG register fields
+#define     SPI_CPOL_BIT        0x0
+#define     SPI_CPOL_SIZE       0x1
+#define     SPI_CPHA_BIT        0x1
+#define     SPI_CPHA_SIZE       0x1
+#define     SPI_CLKDIV_BIT      0x2
+#define     SPI_CLKDIV_SIZE     0x8
+
+// status register fields
+#define     SPI_DONE_BIT        0x0
+#define     SPI_DONE_SIZE       0x1
+
+unsigned int volatile *const SPI0_CTRL = (unsigned int *)(APB_SPI_0_BASE_ADDR + SPI_CTRL_REG);
+unsigned int volatile *const SPI0_DATA = (unsigned int *)(APB_SPI_0_BASE_ADDR + SPI_DATA_REG);
+unsigned int volatile *const SPI0_STATUS = (unsigned int *)(APB_SPI_0_BASE_ADDR + SPI_STATUS_REG);
+unsigned int volatile *const SPI0_CFG = (unsigned int *)(APB_SPI_0_BASE_ADDR + SPI_CFG_REG);
+unsigned int volatile *const SPI0_IM = (unsigned int *)(APB_SPI_0_BASE_ADDR + SPI_IM_REG);
+
+unsigned int volatile *const SPI1_CTRL = (unsigned int *)(APB_SPI_1_BASE_ADDR + SPI_CTRL_REG);
+unsigned int volatile *const SPI1_DATA = (unsigned int *)(APB_SPI_1_BASE_ADDR + SPI_DATA_REG);
+unsigned int volatile *const SPI1_STATUS = (unsigned int *)(APB_SPI_1_BASE_ADDR + SPI_STATUS_REG);
+unsigned int volatile *const SPI1_CFG = (unsigned int *)(APB_SPI_1_BASE_ADDR + SPI_CFG_REG);
+unsigned int volatile *const SPI1_IM = (unsigned int *)(APB_SPI_1_BASE_ADDR + SPI_IM_REG);
+
+/* I2C Modules: I2C00 & I2C1 */
+#define     I2C_PRE_LO_REG      0x00000000
+#define     I2C_PRE_HI_REG      0x00000008
+#define     I2C_CTRL_REG        0x00000010
+#define     I2C_TX_REG          0x00000018
+#define     I2C_RX_REG          0x00000020
+#define     I2C_CMD_REG         0x00000028
+#define     I2C_STAT_REG        0x00000030
+#define     I2C_IM_REG          0x00000038
+
+#define     I2C_CMD_STA         0x80
+#define     I2C_CMD_STO         0x40
+#define     I2C_CMD_RD          0x20
+#define     I2C_CMD_WR          0x10
+#define     I2C_CMD_ACK         0x08
+#define     I2C_CMD_IACK        0x01
+
+#define     I2C_CTRL_EN         0x80
+#define     I2C_CTRL_IEN        0x40
+
+#define     I2C_STAT_RXACK      0x80
+#define     I2C_STAT_BUSY       0x40
+#define     I2C_STAT_AL         0x20
+#define     I2C_STAT_TIP        0x02
+#define     I2C_STAT_IF         0x01
+
+unsigned int volatile * const I2C0_PRE_LO = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_PRE_LO_REG);
+unsigned int volatile * const I2C0_PRE_HI = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_PRE_HI_REG);
+unsigned int volatile * const I2C0_CTRL = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_CTRL_REG);
+unsigned int volatile * const I2C0_TX = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_TX_REG);
+unsigned int volatile * const I2C0_RX = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_RX_REG);
+unsigned int volatile * const I2C0_CMD = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_CMD_REG);
+unsigned int volatile * const I2C0_STAT = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_STAT_REG);
+unsigned int volatile * const I2C0_IM = (unsigned int *) (APB_I2C_0_BASE_ADDR + I2C_IM_REG);
+
+
+unsigned int volatile * const I2C1_PRE_LO = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_PRE_LO_REG);
+unsigned int volatile * const I2C1_PRE_HI = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_PRE_HI_REG);
+unsigned int volatile * const I2C1_CTRL = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_CTRL_REG);
+unsigned int volatile * const I2C1_TX = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_TX_REG);
+unsigned int volatile * const I2C1_RX = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_RX_REG);
+unsigned int volatile * const I2C1_CMD = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_CMD_REG);
+unsigned int volatile * const I2C1_STAT = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_STAT_REG);
+unsigned int volatile * const I2C1_IM = (unsigned int *) (APB_I2C_1_BASE_ADDR + I2C_IM_REG);
+
+/* PWM Modules: PWM0 & PWM1 */
+#define PWM_CMP1_REG            0x00000008
+#define PWM_CMP2_REG            0x00000010
+#define PWM_CTRL_REG            0x00000018
+#define PWM_PRE_REG             0x00000020
+
+unsigned int volatile *const PWM0_CTRL = (unsigned int *)(APB_PWM32_0_BASE_ADDR + PWM_CTRL_REG);
+unsigned int volatile *const PWM0_PRE = (unsigned int *)(APB_PWM32_0_BASE_ADDR + PWM_PRE_REG);
+unsigned int volatile *const PWM0_CMP1 = (unsigned int *)(APB_PWM32_0_BASE_ADDR + PWM_CMP1_REG);
+unsigned int volatile *const PWM0_CMP2 = (unsigned int *)(APB_PWM32_0_BASE_ADDR + PWM_CMP2_REG);
+
+unsigned int volatile *const PWM1_CTRL = (unsigned int *)(APB_PWM32_1_BASE_ADDR + PWM_CTRL_REG);
+unsigned int volatile *const PWM1_PRE = (unsigned int *)(APB_PWM32_1_BASE_ADDR + PWM_PRE_REG);
+unsigned int volatile *const PWM1_CMP1 = (unsigned int *)(APB_PWM32_1_BASE_ADDR + PWM_CMP1_REG);
+unsigned int volatile *const PWM1_CMP2 = (unsigned int *)(APB_PWM32_1_BASE_ADDR + PWM_CMP2_REG);
+
+/* TIMER32: TMR0, TM1, TMR2 and TMR3 */
+#define     TMR_REG             0x00000000
+#define     TMR_PRE_REG         0x00000008
+#define     TMR_CMP_REG         0x00000010
+#define     TMR_STATUS_REG      0x00000018
+#define     TMR_OVCLR_REG       0x00000020
+#define     TMR_EN_REG          0x00000028
+#define     TMR_IM_REG          0x00000030
+
+unsigned int volatile * const TMR0_EN = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_EN_REG);
+unsigned int volatile * const TMR0 = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_REG);
+unsigned int volatile * const TMR0_STATUS = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_STATUS_REG);
+unsigned int volatile * const TMR0_PRE = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_PRE_REG);
+unsigned int volatile * const TMR0_CMP = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_CMP_REG);
+unsigned int volatile * const TMR0_OVCLR = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_OVCLR_REG);
+unsigned int volatile * const TMR0_IM = (unsigned int *) (APB_TIMER32_0_BASE_ADDR + TMR_IM_REG);
+
+unsigned int volatile * const TMR1_EN = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_EN_REG);
+unsigned int volatile * const TMR1 = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_REG);
+unsigned int volatile * const TMR1_STATUS = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_STATUS_REG);
+unsigned int volatile * const TMR1_PRE = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_PRE_REG);
+unsigned int volatile * const TMR1_CMP = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_CMP_REG);
+unsigned int volatile * const TMR1_OVCLR = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_OVCLR_REG);
+unsigned int volatile * const TMR1_IM = (unsigned int *) (APB_TIMER32_1_BASE_ADDR + TMR_IM_REG);
+
+unsigned int volatile * const TMR2_EN = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_EN_REG);
+unsigned int volatile * const TMR2 = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_REG);
+unsigned int volatile * const TMR2_STATUS = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_STATUS_REG);
+unsigned int volatile * const TMR2_PRE = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_PRE_REG);
+unsigned int volatile * const TMR2_CMP = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_CMP_REG);
+unsigned int volatile * const TMR2_OVCLR = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_OVCLR_REG);
+unsigned int volatile * const TMR2_IM = (unsigned int *) (APB_TIMER32_2_BASE_ADDR + TMR_IM_REG);
+
+unsigned int volatile * const TMR3_EN = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_EN_REG);
+unsigned int volatile * const TMR3 = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_REG);
+unsigned int volatile * const TMR3_STATUS = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_STATUS_REG);
+unsigned int volatile * const TMR3_PRE = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_PRE_REG);
+unsigned int volatile * const TMR3_CMP = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_CMP_REG);
+unsigned int volatile * const TMR3_OVCLR = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_OVCLR_REG);
+unsigned int volatile * const TMR3_IM = (unsigned int *) (APB_TIMER32_3_BASE_ADDR + TMR_IM_REG);
+
+/* WDT32: WDT0, WDT1 */
+#define     WDT_TMR_REG      0x00000000
+#define     WDT_LOAD_REG     0x00000008
+#define     WDT_OV_REG       0x00000010
+#define     WDT_OVCLR_REG    0x00000018
+#define     WDT_EN_REG       0x00000020
+#define     WDT_IRQEN_REG    0x00000028
+
+unsigned int volatile *const WDT0_TMR   = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_TMR_REG);
+unsigned int volatile *const WDT0_LOAD  = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_LOAD_REG);
+unsigned int volatile *const WDT0_OV    = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_OV_REG);
+unsigned int volatile *const WDT0_OVCLR = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_OVCLR_REG);
+unsigned int volatile *const WDT0_EN    = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_EN_REG);
+unsigned int volatile *const WDT0_IRQEN = (unsigned int *)(APB_WDT32_0_BASE_ADDR + WDT_IRQEN_REG);
+
+unsigned int volatile *const WDT1_TMR   = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_TMR_REG);
+unsigned int volatile *const WDT1_LOAD  = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_LOAD_REG);
+unsigned int volatile *const WDT1_OV    = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_OV_REG);
+unsigned int volatile *const WDT1_OVCLR = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_OVCLR_REG);
+unsigned int volatile *const WDT1_EN    = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_EN_REG);
+unsigned int volatile *const WDT1_IRQEN = (unsigned int *)(APB_WDT32_1_BASE_ADDR + WDT_IRQEN_REG);
diff --git a/verilog/dv/caravel/user_proj_example/sw/test.c b/verilog/dv/caravel/user_proj_example/sw/test.c
new file mode 100644
index 0000000..42ee022
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/sw/test.c
@@ -0,0 +1,138 @@
+#include "n5_drv.h"
+#include "n5_int.h"
+
+unsigned int A[100];
+
+void IRQ() {
+    gpio_write(0x0099);        
+}
+
+int fact(int n){
+    int f = 1;
+    for(int i=2; i<=n; i++)
+        f = f * i;
+    return f;
+}
+
+int strlen(char *s){
+    int i=0;
+    while (*s){
+        i++;
+    }
+    return i;
+}
+
+void M23LC_write_byte(int n, unsigned int addr, unsigned int data){
+  spi_start(n);
+  spi_write(n, 0x2);
+  spi_write(n, addr >> 8);     // Address high byte
+  spi_write(n, addr & 0xFF);   // Address low byte
+  spi_write(n, data);
+  spi_end(n);
+}
+
+unsigned char M23LC_read_byte(int n, unsigned short addr){
+  spi_start(n);
+  spi_write(n, 0x3);
+  spi_write(n, addr >> 8);     // Address high byte
+  spi_write(n, addr & 0xFF);   // Address low byte
+  spi_write(n, 0);             // just write a dummy data to get the data out
+  spi_end(n);
+  return spi_read(n);
+}
+
+#define     DELAY(n)   for(int i=0; i<n; i++)
+
+int main(){
+    // Initialization
+    uart_init (0, 0);
+    gpio_set_dir(0x00FF);
+
+    spi_init(0, 0,0,40);
+    
+    // Start the test
+    uart_puts (0, "Hello World!\n", 13);
+    
+    // I2C testing
+    uart_puts (0, "I2C Test: ", 10);
+    i2c_init(0, 16000);
+    i2c_start(0, 0xA0);    // slave address = 1010_000, R/W_b=0 (write)
+    i2c_sendByte(0x0);
+    i2c_sendByte(0x0);
+    i2c_sendByte(0x55);
+    i2c_stop();
+    DELAY(20);
+    i2c_start(0, 0xA0);    // slave address = 1010_000, R/W_b=0 (write)
+    i2c_sendByte(0x0);
+    i2c_sendByte(0x0);
+    i2c_start(0, 0xA1);    // slave address = 1010_000, R/W_b=1 (read)
+    int i2c_data = i2c_readByte();
+    gpio_write(i2c_data);
+    if(i2c_data == 0x55)
+        uart_puts(0,"Passed!\n", 8);
+    else
+        uart_puts(0,"Failed!\n", 8);
+    i2c_stop();
+
+    // GPIO
+    uart_puts (0, "GPIO Test: ", 11);
+
+    int gpio_val = 0x55;
+    if (SIM_SOC == 0){
+        gpio_val = 0x15; // Wrapper has a lower number of GPIOs
+    }
+    
+    gpio_write(gpio_val);
+    DELAY(50);
+    int gpio_data = gpio_read();
+    if((gpio_data >> 8) == gpio_val)
+        uart_puts(0,"Passed!\n", 8);
+    else
+        uart_puts(0,"Failed!\n", 8);
+    
+    // SPI
+    uart_puts (0, "SPI Test: ", 10);
+    M23LC_write_byte(0, 0, 0xA5);
+    unsigned int spi_data = M23LC_read_byte(0, 0);
+    DELAY(10);
+    if(spi_data==0xA5)
+        uart_puts(0,"Passed!\n", 8);
+    else 
+        uart_puts(0,"Failed!\n", 8);
+
+    // Timer
+    uart_puts (0, "TMR Test: ", 10);
+    tmr_init(0, 800, 10);
+    tmr_enable(0);
+    tmr_wait(0);
+    tmr_disable(0);
+    if(tmr_read(0) == 0)
+        uart_puts(0,"Passed!\n", 8);
+    else 
+        uart_puts(0,"Failed!\n", 8);
+
+    // WDT
+    uart_puts (0, "WDT Test: ", 10);
+    wdt_init(0);
+    wdt_enable(0);
+    wdt_load(0, 50);
+    while(wdt_read(0) != 0);
+    wdt_disable(0);
+    if (wdt_read(0) == 0)
+        uart_puts(0,"Passed!\n", 8);
+    else
+        uart_puts(0,"Failed!\n", 8);
+
+    // PWM
+    pwm_init(0, 250, 99, 5);
+ 	pwm_enable(0);
+    DELAY(30);
+    pwm_disable(0); 
+
+    // Some Delay
+    DELAY(50);
+   
+    // Done!
+    uart_puts(0, "Done!\n\n", 7);
+    return 0;
+}