--- /dev/null
+/* ****************************************************************************\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