https://blackmesalabs.wordpress.com/2016/10/24/sump2-96-msps-logic-analyzer-for-22/
[BML_sump2] / sump2 / source / spi_byte2bit.v
diff --git a/sump2/source/spi_byte2bit.v b/sump2/source/spi_byte2bit.v
new file mode 100755 (executable)
index 0000000..991124d
--- /dev/null
@@ -0,0 +1,387 @@
+/* ****************************************************************************\r
+-- Source file: spi_byte2bit.v\r
+-- Date:        June 1,2014   \r
+-- Author:      khubbard\r
+-- Description: Simple interface to SPI that xfers Bytes to Bits.\r
+--              Supports read stalling of MISO stream.\r
+-- Language:    Verilog-2001\r
+-- Simulation:  Mentor-Modelsim\r
+-- Synthesis:   Xilinst-XST\r
+-- License:     This project is licensed with the CERN Open Hardware Licence\r
+--              v1.2.  You may redistribute and modify this project under the\r
+--              terms of the CERN OHL v.1.2. (http://ohwr.org/cernohl).\r
+--              This project is distributed WITHOUT ANY EXPRESS OR IMPLIED\r
+--              WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY\r
+--              AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN OHL\r
+--              v.1.2 for applicable Conditions.\r
+--\r
+--                                 spi_byte2bit     \r
+--                                 ----------\r
+--            clk        -------->|          |\r
+--            xfer_start  ------->|          |\r
+--            xfer_tx_bytes[7:0]->|          |\r
+--            xfer_rx_bytes[7:0]->|          |\r
+--            mosi_byte_d[7:0]--->|          |--> miso_byte_d[7:0]\r
+--            mosi_byte_en ------>|          |--> miso_byte_rdy \r
+--            mosi_byte_req <-----|          |\r
+--                                |          |\r
+--                   spi_miso --->|          |--> spi_mosi\r
+--                                |          |--> spi_sck \r
+--                                |          |--> spi_cs_l\r
+--                                 ----------\r
+--\r
+-- spi_sck    _/\/\/\/\/\/\/\/\____/\/\/\/\/\/\/\/\/\/\/\/\/\r
+-- spi_cs_l              \_________________________/\r
+-- spi_mosi   -----------< B0 >----< B1 >------------------\r
+-- spi_miso   --------------------------< B2 >< B3>------\r
+--\r
+-- xfer_start_/   \_______________________________________\r
+-- xfer_stall___________________________________/   \_____\r
+-- tx_bytes  -< 2 >---------------------------------------\r
+-- rx_bytes  -< 2 >---------------------------------------\r
+--\r
+-- mosi_byte_en     __/  \__..._/  \_____\r
+-- mosi_byte_d[7:0] --<B0>--...-<B1>-----\r
+-- mosi_byte_req   ______/      \_______\r
+--\r
+-- miso_byte_rdy    ________________________/  \___/  \__\r
+-- miso_byte_d[7:0] ------------------------<B2>---<B3>--\r
+-- \r
+-- \r
+-- Note: Starving the MOSI stream is allowed. The block will gate the SPI clock\r
+--       and wait until the MOSI byte arrives.\r
+--\r
+-- Note: Stalling the MISO stream is allowed. Asserting xfer_stall will gate\r
+--       the entire block, allowing for the MISO stream to be stalled.\r
+--\r
+-- Revision History:\r
+-- Ver#  When      Who      What\r
+-- ----  --------  -------- --------------------------------------------------\r
+-- 0.1   06.01.14  khubbard Creation\r
+-- 0.1   06.25.14  khubbard Removed mosi_starved logic. Not worth complication\r
+-- 0.2   01.29.16  khubbard Fixed reset issue when ck_div is 0x00 at reset.\r
+-- ***************************************************************************/\r
+//`default_nettype none // Strictly enforce all nets to be declared\r
+\r
+module spi_byte2bit \r
+(\r
+  input  wire         reset,\r
+  input  wire         clk,\r
+  input  wire [7:0]   ck_divisor,\r
+  input  wire [3:0]   spi_ctrl,\r
+\r
+  output reg          spi_sck,\r
+  output wire         spi_cs_l,\r
+  output reg          spi_mosi,\r
+  input  wire         spi_miso,\r
+  output reg          spi_is_idle,\r
+\r
+  input  wire         xfer_start,\r
+  input  wire         xfer_stall,\r
+  input  wire [11:0]  xfer_tx_bytes,\r
+  input  wire [11:0]  xfer_rx_bytes,\r
+\r
+  input  wire [7:0]   mosi_byte_d,\r
+  input  wire         mosi_byte_en,\r
+  output wire         mosi_byte_req,\r
+  output reg          mosi_err,\r
+\r
+  output reg  [7:0]   miso_byte_d,\r
+  output reg          miso_byte_rdy\r
+);\r
+\r
+  reg  [7:0]              ck_cnt;\r
+  wire [7:0]              ck_div;\r
+  reg                     ck_loc;\r
+  reg                     ck_off_pre;\r
+  reg                     ck_off;\r
+  reg                     ck_en_fal;\r
+  reg                     ck_en_ris;\r
+  reg                     spi_cs_pre;\r
+  reg                     spi_cs_l_loc;\r
+  reg                     mosi_load;\r
+  reg                     mosi_loaded;\r
+  reg  [7:0]              mosi_sr;\r
+  wire [7:0]              mosi_sr_loc;\r
+  reg  [7:0]              mosi_byte_queue;\r
+  reg                     mosi_queued_jk;\r
+  reg                     mosi_refused;\r
+  reg                     mosi_byte_req_loc;\r
+  reg                     mosi_byte_en_p1;\r
+  reg                     miso_loc;\r
+  reg  [7:0]              miso_sr;\r
+  reg  [11:0]             xfer_cnt;\r
+  reg                     xfer_tx_jk;\r
+  reg                     xfer_tx_jk_p1;\r
+  reg                     xfer_rx_jk;\r
+  reg  [4:0]              xfer_rx_jk_sr;\r
+  reg                     xfer_start_jk;\r
+  reg  [11:0]             xfer_length;\r
+  reg  [11:0]             xfer_rx_length;\r
+  wire [8:0]              xfer_byte;\r
+  wire [2:0]              xfer_bit;\r
+  reg  [2:0]              xfer_bit_p1;\r
+  reg  [2:0]              xfer_bit_p2;\r
+  reg  [2:0]              xfer_bit_p3;\r
+  reg  [2:0]              xfer_bit_p4;\r
+\r
+//assign ck_div     = 8'd10;\r
+  assign ck_div     = ck_divisor[7:0];// Num of clocks in 1/2 spi clock period \r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// Generate spi_ck. Counts 1/2 periods and toggles a bit.       \r
+//   ck         _/ \_/ \_/ \_/ \_/ \_/ \_/\r
+//   cnt          1   2   1   2   1   2\r
+//   ck_loc     _/       \_______/       \_\r
+//   ck_en_ris  _/   \___________/   \_____\r
+//   ck_en_fal  _________/   \___________/   \r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_spi_ck   \r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+    ck_en_fal     <= 0;\r
+    ck_en_ris     <= 0;\r
+    // Clock Divider\r
+//  if ( ck_cnt != 8'hFF ) begin\r
+    if ( 1 == 1 ) begin\r
+      ck_cnt <= ck_cnt + 1;\r
+      if ( ck_cnt == ck_div ) begin\r
+        ck_loc <= ~ ck_loc;\r
+        ck_cnt <= 8'h1;\r
+        if ( ck_loc == 1 ) begin\r
+          ck_en_fal <= 1;\r
+        end else begin\r
+          ck_en_ris <= 1;\r
+        end\r
+      end\r
+    end\r
+//  end else begin\r
+//    ck_cnt <= ck_cnt[7:0];\r
+//    ck_loc <= 0;\r
+//  end\r
+  \r
+    if ( reset == 1 ) begin\r
+      ck_cnt <= 8'd1;\r
+      ck_loc <= 0;\r
+    end\r
+  end\r
+ end\r
+end // proc_spi_ck   \r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// Shift in the MISO data\r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_miso_sr  \r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+// miso_byte_d   <= 8'd0;\r
+   miso_byte_rdy <= 0;\r
+// if ( ck_en_fal == 1 ) begin\r
+   if ( ck_en_ris == 1 ) begin\r
+     xfer_bit_p1 <= xfer_bit[2:0];\r
+     xfer_bit_p2 <= xfer_bit_p1[2:0];\r
+     xfer_bit_p3 <= xfer_bit_p2[2:0];\r
+     xfer_bit_p4 <= xfer_bit_p3[2:0];\r
+     if ( xfer_rx_jk == 1 || xfer_rx_jk_sr != 5'd0 ) begin\r
+        miso_loc     <= spi_miso;\r
+        miso_sr[0]   <= miso_loc;\r
+        miso_sr[7:1] <= miso_sr[6:0];\r
+        if ( xfer_bit_p4 == 3'd0 && xfer_rx_jk_sr[4] == 1 ) begin\r
+          miso_byte_d   <= miso_sr[7:0];\r
+          miso_byte_rdy <= 1;\r
+        end\r
+     end\r
+   end \r
+  end \r
+  if ( reset == 1 ) begin\r
+    miso_byte_rdy <= 0;\r
+    xfer_bit_p1   <= 3'd0;\r
+    xfer_bit_p2   <= 3'd0;\r
+    xfer_bit_p3   <= 3'd0;\r
+    xfer_bit_p4   <= 3'd0;\r
+   end\r
+ end\r
+end // proc_miso_sr\r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// On rising edge of xfer_start_jk, load the number of bytes * 8bits into a \r
+// bit counter and start the counter. Count down to 0 then turn off xfer_tx_jk.\r
+//   xfer_start      __/ \_________________________/ \___\r
+//   tx_bytes     --<2>-------------------------<2>---\r
+//   rx_bytes     --<4>-------------------------<4>---\r
+//   mosi         -------<0><1>-----------------------\r
+//   miso         -------------<0><1><2><3>-----------\r
+//   xfer_tx_jk   _______/     \______________________\r
+//   xfer_rx_jk   _____________/           \__________\r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_xfer_req \r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+   if ( xfer_start == 1 ) begin\r
+     xfer_length <= xfer_tx_bytes[11:0];\r
+     xfer_rx_length <= xfer_rx_bytes[11:0];\r
+   end\r
+\r
+   if ( ck_en_fal == 1 ) begin\r
+     mosi_err           <= 0;\r
+     mosi_load          <= 0; \r
+     xfer_tx_jk_p1      <= xfer_tx_jk;\r
+     xfer_rx_jk_sr[0]   <= xfer_rx_jk;\r
+     xfer_rx_jk_sr[4:1] <= xfer_rx_jk_sr[3:0];\r
+     if ( xfer_start_jk != 0 ) begin\r
+       xfer_cnt <= { xfer_length[8:0] , 3'b000 };\r
+       xfer_tx_jk  <= 1;\r
+     end else if ( xfer_cnt != 12'd0 ) begin\r
+       if ( xfer_tx_jk == 1 ) begin\r
+         if ( xfer_bit == 2'd0 ) begin\r
+           if ( mosi_queued_jk == 1 ) begin\r
+             mosi_load       <= 1; // Time for a new Byte\r
+           end else begin\r
+             mosi_err <= 1; // Nothing to send.\r
+           end\r
+         end\r
+         xfer_cnt <= xfer_cnt - 1;\r
+         if ( xfer_cnt == 12'd1 ) begin\r
+           xfer_tx_jk <= 0;\r
+           if ( xfer_rx_length[8:0] != 9'd0 ) begin\r
+             xfer_rx_jk <= 1;\r
+             xfer_cnt   <= { xfer_rx_length[8:0] , 3'b000 };\r
+           end\r
+         end\r
+       end else if ( xfer_rx_jk == 1 ) begin\r
+         xfer_cnt <= xfer_cnt - 1;\r
+         if ( xfer_cnt == 12'd1 ) begin\r
+           xfer_rx_jk <= 0;\r
+         end\r
+       end\r
+     end\r
+   end\r
+\r
+   if ( reset == 1 ) begin\r
+     mosi_load       <= 0;\r
+     xfer_cnt        <= 12'd0;\r
+     xfer_tx_jk      <= 0;\r
+     xfer_rx_jk      <= 0;\r
+     xfer_tx_jk_p1   <= 0;\r
+     xfer_rx_jk_sr   <= 5'd0;\r
+   end \r
+  end \r
+ end\r
+end // proc_xfer_req\r
+  assign xfer_byte[8:0] = xfer_cnt[11:3];\r
+  assign xfer_bit[2:0]  = xfer_cnt[2:0];\r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// Latch the MOSI byte data and start shifting out\r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_mosi_sr \r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+   mosi_refused    <= 0;\r
+   mosi_byte_en_p1 <= mosi_byte_en;\r
+   if ( ck_en_fal == 1 ) begin\r
+     xfer_start_jk <= 0;\r
+   end \r
+\r
+   // When new mosi byte comes in, place in queue and drop request\r
+   if ( mosi_byte_en == 1 ) begin\r
+     xfer_start_jk     <= ~ xfer_tx_jk;      // A new Xfer\r
+     mosi_byte_queue   <= mosi_byte_d[7:0];\r
+     mosi_queued_jk    <= 1;\r
+     mosi_byte_req_loc <= 0; \r
+     // Assert refused if not expecting a byte.\r
+     if ( xfer_tx_jk == 1 ) begin\r
+       mosi_refused  <= ~ mosi_byte_req_loc; \r
+     end\r
+   end // if ( mosi_byte_en == 1 ) begin\r
+\r
+   if ( mosi_loaded == 1 || xfer_start == 1 ) begin\r
+     mosi_byte_queue   <= 8'd0;\r
+     mosi_queued_jk    <= 0;\r
+     if ( xfer_byte != 9'd0 || xfer_start == 1 ) begin\r
+       mosi_byte_req_loc <= 1;\r
+     end\r
+   end \r
+\r
+   if ( reset == 1 ) begin\r
+     mosi_queued_jk    <= 0;\r
+     mosi_byte_req_loc <= 0;\r
+     xfer_start_jk     <= 0;\r
+   end // if ( reset == 1 ) begin\r
+  end\r
+ end\r
+end // proc_mosi_sr \r
+\r
+  assign mosi_sr_loc[7:0] = mosi_sr[7:0];\r
+  assign mosi_byte_req = mosi_byte_req_loc;\r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// Either Latch a new MOSI byte or shift existing byte \r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_mosi_shift\r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+   mosi_loaded <= 0;\r
+   if ( ck_en_fal == 1 ) begin\r
+     if ( mosi_load == 1 ) begin\r
+       mosi_sr     <= mosi_byte_queue[7:0];  \r
+       mosi_loaded <= 1;\r
+     end else begin\r
+       mosi_sr <= { mosi_sr[6:0], 1'd0 }; // Shift the Byte Out\r
+     end\r
+   end // if ( ck_en_fal == 1 ) begin\r
+   if ( reset == 1 ) begin\r
+     mosi_sr <= 8'd0;\r
+   end \r
+  end \r
+ end\r
+end // proc mosi_shift\r
+\r
+\r
+//-----------------------------------------------------------------------------\r
+// SPI Master Output : The serial bits.\r
+//-----------------------------------------------------------------------------\r
+always @ ( posedge clk ) begin : proc_mosi_out\r
+ begin\r
+  if ( xfer_stall == 0 ) begin\r
+   if ( spi_ctrl[0] == 0 ) begin\r
+     spi_sck     <= ck_loc & ~ ck_off;\r
+   end else begin\r
+     spi_sck     <= ~ ck_loc & ~ ck_off;\r
+   end\r
+                             \r
+//  if ( ck_en_fal == 1 ) begin\r
+    if ( ( ck_en_fal == 1 && spi_ctrl[1] == 0 ) ||\r
+         ( ck_en_ris == 1 && spi_ctrl[1] == 1 )    ) begin\r
+      ck_off       <= ck_off_pre;\r
+      spi_cs_pre   <= xfer_tx_jk_p1 | xfer_rx_jk_sr[0];\r
+      spi_cs_l_loc <= ~ spi_cs_pre;\r
+      spi_is_idle  <= ~ spi_cs_pre;\r
+      spi_mosi     <= mosi_sr_loc[7];\r
+      if ( spi_cs_pre == 0 && \r
+           xfer_tx_jk_p1 == 0 && \r
+           xfer_rx_jk_sr[0] == 0 ) begin \r
+        ck_off_pre <= 1;\r
+      end else begin\r
+        ck_off_pre <= 0;\r
+        ck_off     <= 0;\r
+      end\r
+    end\r
+  end // if ( xfer_stall == 0 ) begin\r
+  if ( reset == 1 ) begin\r
+    ck_off_pre   <= 0;\r
+    ck_off       <= 0;\r
+    spi_cs_pre   <= 0;\r
+    spi_cs_l_loc <= 1;\r
+  end\r
+ end\r
+end // proc_mosi_out\r
+  assign spi_cs_l = spi_cs_l_loc;\r
+\r
+\r
+endmodule // spi_byte2bit\r