// --------------------------- SRAM.V ---------------------------------------
// Filename : sram.v
// Created : derived from instruct.v
// Creators : Richard Clayton, Michael Bond
// Format : Verilog HDL
// Compilation : As part of Quartus II project
// Detail : The instruction decoder for the Nios/Desmim Engine interface
//          drives SRAM on the Excalibur board

//=============
module instruct
//=============
(
   notReset,            // global notReset (active LOW)
   clk,                 // clock
   pioCommand,          // the command PIO bus
   pioData,             // the data PIO bus
   pioStatus,           // the status PIO bus
   A,                   // A on the SRAM chips
   IO_0,                // IO on SRAM chip 0
   IO_1,                // IO on SRAM chip 1
   CS,                  // -CS on the SRAM chips
   OE,                  // -OE on the SRAM chips
   WE                   // -WE on the SRAM chips
, dA
, dIO
, dCS
, dWE
, dMatchLS32
, dDESstop
);

// ** debug start **

output [15:0] dA;
output [31:0] dIO;
output dCS;
output dWE;
output dMatchLS32;
output dDESstop;

wire [15:0] dA  = A;
wire dCS        = CS;
wire dWE        = WE;
wire dMatchLS32 = matchLS32;
wire dDESstop   = DESstop;
// need a conditional here - else direct connection is made
// which upsets the synthesis!
wire [31:0] dIO = ((WE == 0) && (OE == 0)) ? 32'b0 : IO_0;

// ** debug end **

// reset and clock signals

input         notReset;
wire          notReset;
input         clk;
wire          clk;

// the three PIOs, for command, data and status

input  [9:0]  pioCommand;
wire   [9:0]  pioCommand;

inout  [15:0] pioData;
wire   [15:0] pioData;

output [3:0]  pioStatus;
wire   [3:0]  pioStatus;

// the SRAM chip

output [15:0] A;
inout  [15:0] IO_0;
inout  [15:0] IO_1;
output        CS;
output        OE;
output        WE;

wire   [15:0] A;        // address
wire   [15:0] IO_0;     // data values
wire   [15:0] IO_1;     // data values
wire          CS;       // chip select   (LOW = selected)
wire          WE;       // write enable  (LOW = write)
wire          OE;       // output enable (LOW = output enabled)

// -------------
// state machine (one hot state encoding)
// -------------

// main state machine

reg state_command;
reg state_read;
reg state_write;
reg state_pipeline;

// -----------
// SRAM values
// -----------

wire ramDone;                 // read or write is complete
reg  [14:0] SRAMaddress;      // access address to SRAM
wire [31:0] SRAMdata;         // data read from SRAM

// ------------
// PIO commands
// ------------

// act upon instructions from the PIO (one hot state encoding of commands)
// only does things in command state (or notReset)

reg  [31:0] dataIn;           // 32 bit data value (multiplexed through PIO)
reg  [31:0] dataOut;          // 32 bit data value (multiplexed through PIO)

// 0004 : set data for NIOS to fetch is done by assignment for tri-state
// 0005 : set data for NIOS to fetch is done by assignment for tri-state

assign pioData = (!notReset || !pioCommand[2]) ? 16'bz
                             : (pioCommand[0]) ? dataOut[31:16]
                                               : dataOut[15: 0];

// NB: The pioData must be processed correctly inside the NIOS core, otherwise
//     asserting something on the Pio input can hang the entire core
//
//     The problem was fixed by enabling the synchronous edge
//     capture register setting in the pio

// ---------------
// crypto pipeline
// ---------------

// state

reg DESstate_stopped;
reg DESstate_start0;
reg DESstate_start1;
reg DESstate_running;
reg DESstate_match0;
reg DESstate_match1;

// basic DES mechanism

reg  DESreset;
reg  DESstop;

reg  [63:0] DESindata;

wire [63:0] DESresult;
wire [55:0] DESkeyval;

DES descore(clk, DESreset, DESstop, DESindata, DESresult, DESkeyval);

reg  [63:0] saveValue0;		// previous value taken from DES pipeline
reg  [63:0] saveValue1;		// latest value taken from DES pipeline
reg  [63:0] saveValue2;		// and an extra one for partial match events

// We produce a hash of the DESresult with which to access the SRAM.
//
// There are two SRAM chips. They are 16 bits wide and 2**15 long 
// in one clock we can therefore read 32 bits (16 from each)
// if this 32 bits matches half of DESresult we stop the pipeline
// and check the next 32 bits to see if that matches as well

wire [13:0] hashResult;
wire [13:0] hashSave0;
wire [13:0] hashSave2;

HASH HashF(DESresult,  hashResult);
HASH Hash0(saveValue0, hashSave0);
HASH Hash2(saveValue2, hashSave2);

reg  [31:0] SRAMvalueLS;		// latest value for LS 32 bits
reg  [31:0] SRAMvalueMS;		// latest value for MS 32 bits

// ----------
// PIO status
// ----------

assign pioStatus[0] = state_command;
assign pioStatus[1] = state_pipeline;
assign pioStatus[2] = DESstate_running;
assign pioStatus[3] = DESstate_match0;

// ------------
// match states
// ------------

wire matchLS32 = (SRAMvalueLS == saveValue0[31: 0]);
wire matchMS32 = (SRAMvalueMS == saveValue0[63:32]);

// -----------------
// clock edge action
// -----------------

always @(posedge clk or negedge notReset)
begin
   if (!notReset)
   begin
      state_command  <= 1;
      state_read     <= 0;
      state_write    <= 0;
      state_pipeline <= 0;

      dataIn         <= 0;
      dataOut        <= 0;
      SRAMaddress    <= 0;

      DESreset       <= 1;
      DESstop        <= 1;

      DESindata      <= 0;

      DESstate_stopped <= 1;
      DESstate_start0  <= 0;
      DESstate_start1  <= 0;
      DESstate_running <= 0;
      DESstate_match0  <= 0;
      DESstate_match1  <= 0;

      SRAMvalueLS     <= 0;
      SRAMvalueMS     <= 0;
      saveValue0      <= 0;
      saveValue1      <= 0;
      saveValue2      <= 0;
   end
   else
   begin
      if (state_command)
      begin
         if (pioCommand[1])         // 0002: 0003: load data from NIOS
         begin
            if (pioCommand[0])
               dataIn[31:16] <= pioData;
            else
               dataIn[15: 0] <= pioData;
         end
         else if (pioCommand[3])    // 0008: 0009: set data to be encrypted
         begin
            if (pioCommand[0])
               DESindata[63:32] <= dataIn;
            else
               DESindata[31: 0] <= dataIn;
         end
         else if (pioCommand[4])    // 0010: set (15 bit) SRAM access address
         begin
            SRAMaddress <= dataIn[14:0];
         end
         else if (pioCommand[5])    // 0020: write to SRAM & increment address
         begin
            state_write    <= 1;
            state_command  <= 0;
         end
         else if (pioCommand[6])    // 0040: read from SRAM & increment address
         begin
            state_read     <= 1;
            state_command  <= 0;
         end
         else if (pioCommand[7])    // 0080: run crypto routine
            begin
               state_pipeline <= 1;
               state_command  <= 0;
            end
         else if (pioCommand[8])    // 0100, 0101: fetch result
         begin
            if (pioCommand[0])
               dataOut <= saveValue0[63:32];
            else
               dataOut <= saveValue0[31: 0];
         end
         else if (pioCommand[9])    // 0200: 0201: get crypto result
         begin
            // for simplicity, producing key with 0's instead of parity
            if (pioCommand[0])
               dataOut <= {DESkeyval[55:49], 1'b0,
                           DESkeyval[48:42], 1'b0,
                           DESkeyval[41:35], 1'b0,
                           DESkeyval[34:28], 1'b0};
            else
               dataOut <= {DESkeyval[27:21], 1'b0,
                           DESkeyval[20:14], 1'b0,
                           DESkeyval[13: 7], 1'b0,
                           DESkeyval[ 6: 0], 1'b0};
         end
      end
      else if (state_pipeline)      // ----------- run the crypto ------------
      begin

         // state machine:
         //
         // time N+0:      SAVE value N+0, ask SRAM for HASH(value N+0)
         // time N+1:      SAVE value N+1, ask SRAM for HASH(value N+1)
         // time N+2:      if SRAM(N+0) matches
         //                   stop pipeline
         //                   ask for HASH(value N+0) part II
         //                   time N+3:   data back from SRAM
         //                   time N+4:   see if data matched - if so, done
         //                   otherwise treat as if N+2 didn't match
         //                else
         //                   start pipeline
         //                   shuffle SAVE values
         //                   ask SRAM for HASH(value N+2)

         if (DESstate_stopped)
         begin
             // we wish to start the DES pipeline, so reset it

            DESreset            <= 0;

            // can only start the pipeline when the NIOS is ready

            if (pioCommand[7] == 0)
            begin
               DESstop          <= 0;
               DESstate_stopped <= 0;
               DESstate_start0  <= 1;
            end
         end
         else if (DESstate_start0)
         begin
            // just getting going, so latch the data for time N
            // and ask the SRAM for the related value

            saveValue0  <= DESresult;
            SRAMaddress <= {hashResult, 1'b0};

            DESstate_start0 <= 0;
            DESstate_start1 <= 1;

         end
         else if (DESstate_start1)
         begin

            // SRAM has delivered - so record what it said
            // and ask for the next value

            SRAMvalueLS <= SRAMdata;

            saveValue1  <= DESresult;
            SRAMaddress <= {hashResult, 1'b0};

            DESstate_start1  <= 0;
            DESstate_running <= 1;

         end
         else if (DESstate_running)
         begin

            SRAMvalueLS <= SRAMdata;

            // check if found 32bit match with results from time N

            if (matchLS32)
            begin

               // there is a match so stop the DES pipeline

               DESstop          <= 1;

               // unfortunately it's still running so save current value

               saveValue2       <= DESresult;

               // change substate

               DESstate_running <= 0;
               DESstate_match0  <= 1;

               // and arrange to fetch part 2 of the matched value from SRAM

               SRAMaddress <= {hashSave0, 1'b1};
            end
            else
            begin
               // no match

               saveValue0  <= saveValue1;
               saveValue1  <= DESresult;

               SRAMaddress <= {hashResult, 1'b0};
            end
         end
         else if (DESstate_match0)
         begin
            SRAMvalueMS <= SRAMdata;

            DESstate_match0 <= 0;
            DESstate_match1 <= 1;
         end
         else if (DESstate_match1)
         begin
            if (matchMS32)
            begin
               // full match - so we can stop searching altogether
               // note that the DES pipeline is arranged to report the key
               // from three cycles ago - so we report the tick N key value!

               DESstate_stopped <= 1;
               state_pipeline   <= 0;
               state_command    <= 1;
            end
            else
            begin
               // no match - so need to carry on

               DESstop <= 0;

               saveValue0 <= saveValue1;
               saveValue1 <= saveValue2;

               SRAMaddress <= {hashSave2, 1'b0};

               DESstate_match1  <= 0;
               DESstate_running <= 1;
            end
         end
      end
      else if ((state_read || state_write) && ramDone)
      begin
         // at end of read/write we increment the address
         // but only when the NIOS has removed its request
         // (otherwise we just repeat the read)

         if (!pioCommand[5] && !pioCommand[6])
         begin
            SRAMaddress   <= SRAMaddress + 1;
            dataOut       <= SRAMdata;
            state_command <= 1;
            state_read    <= 0;
            state_write   <= 0;
         end
      end
   end
end

// -------------
// SRAM handling
// -------------

// SRAM address bus

assign A = {1'b0, SRAMaddress};

// SRAM functionality

SRAM ram(notReset, clk,
         state_read,
         state_write,
         state_pipeline,
         dataIn, SRAMdata,
         ramDone,
         IO_0, IO_1, CS, OE, WE
        );

endmodule

//=========
module SRAM
//=========
(
   notReset,         // global notReset (active LOW)
   clk,              // global clock
   readReq,          // read request
   writeReq,         // write request
   pReadReq,         // pipeline reads
   dataW,            // data to be written to SRAM
   dataR,            // data read from SRAM
   done,             // read (or write) completed
   IO_0,             // IO on SRAM chip 0
   IO_1,             // IO on SRAM chip 1
   CS,               // -CS on the SRAM chips
   OE,               // -OE on the SRAM chips
   WE                // -WE on the SRAM chips
);

input          notReset;
input          clk;
input          readReq;
input          writeReq;
input          pReadReq;
output         done;
input  [31:0]  dataW;
output [31:0]  dataR;

wire           notReset;
wire           clk;
wire           readReq;
wire           writeReq;
wire           pReadReq;
reg            done;
wire  [31:0]   dataW;
wire  [31:0]   dataR;

//  SRAM signals (note that -BLE, -BHE assumed permanently low)

inout  [15:0] IO_0;
inout  [15:0] IO_1;
output CS;
output OE;
output WE;

wire   [15:0] IO_0;     // data values to/from chip 0
wire   [15:0] IO_1;     // data values to/from chip 1
reg    CS;              // chip select   (LOW = selected)
reg    OE;              // output enable (LOW = output enabled)
reg    WE;              // write enable signal (LOW = write)

// two directional IO bus to the SRAM chips

assign dataR = {IO_1, IO_0};

assign IO_0  = writeReq ? dataW[15: 0] : 16'bZ;
assign IO_1  = writeReq ? dataW[31:16] : 16'bZ;

// SRAM sequencing

reg  stageTwo;

// clock edge actions

always @ (posedge clk or negedge notReset)
begin
   if (!notReset)
   begin
      CS   <= 1;              // chips deselected, standby
      OE   <= 1;
      WE   <= 1;
      stageTwo  <= 0;
      done      <= 0;
   end
   else
   begin
//----------------------------- service sram write request ------------------
      if (writeReq && !done )
      begin
         CS <= 0;

         // we supply address and data at the same time
         // second stage is needed to raise WE since it must go low for
         // at least 8ns then rise when the data and address are stable

         if (!stageTwo)
         begin
            WE   <= 0;
            stageTwo <= 1;
         end
         else
         begin
            WE   <= 1;
            done <= 1;
         end
      end
//----------------------------- service sram read request -------------------
      else if ((readReq && !done) | pReadReq)
      begin
         CS <= 0;
         OE <= 0;

         // remember - we assert the address and one clock later
         // we get the data back - hence 2 states here
         // in the pipelined case the external logic deals with delay

         if (readReq && !stageTwo)
            stageTwo <= 1;
         else
            done <= 1;
      end
//----------------------------- not servicing a request ---------------------
      else     // not active read or write
      begin
         CS <= 1;
         OE <= 1;
         stageTwo <= 0;

         // clear the done flag only when level above has
         // noticed that we have finished

         if (!readReq && !writeReq)
            done <= 0;
      end
   end
end

endmodule

//=========
module HASH
//=========
//
// the hash function specifies which SRAM location is expected
// to hold a particular result
(
   result,
   address
);

input  [63:0] result;
output [13:0] address;

wire [63:0] result;
wire [13:0] address = {         result[63]^result[62]^result[61]^result[60],
                                result[59]^result[58]^result[57]^result[56],
                                result[55]^result[54]^result[53]^result[52],
                                result[51]^result[50]^result[49]^result[48],
                                result[47]^result[46]^result[45]^result[44],
                                result[43]^result[42]^result[41]^result[40],
                     result[39]^result[38]^result[37]^result[31]^result[35],
                     result[34]^result[33]^result[32]^result[31]^result[30],
                     result[29]^result[28]^result[27]^result[26]^result[25],
                     result[24]^result[23]^result[22]^result[21]^result[20],
                     result[19]^result[18]^result[17]^result[16]^result[15],
                     result[14]^result[13]^result[12]^result[11]^result[10],
                     result[ 9]^result[ 8]^result[ 7]^result[ 6]^result[ 5],
                     result[ 4]^result[ 3]^result[ 2]^result[ 1]^result[ 0] };

endmodule

// end of sram.v
