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