summaryrefslogtreecommitdiffhomepage
path: root/source/spi.v
diff options
context:
space:
mode:
Diffstat (limited to 'source/spi.v')
-rw-r--r--source/spi.v300
1 files changed, 300 insertions, 0 deletions
diff --git a/source/spi.v b/source/spi.v
new file mode 100644
index 0000000..7d6004e
--- /dev/null
+++ b/source/spi.v
@@ -0,0 +1,300 @@
+/*
+ * spi.v
+ *
+ * Copyright (C) 2018, 2019 Mind Chasers Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * function: SPI slave controller
+ *
+ * refer to SPI protocol figure at https://mindchasers.com/dev/hw-spi
+ *
+ *
+ */
+
+`timescale 1ns /10ps
+
+ module spi(
+ input rstn,
+ input clk,
+
+ // SPI signals
+ input spi_cs,
+ input spi_clk,
+ input spi_d_in,
+ output reg spi_d_o,
+ output spi_d_oe,
+
+ // internal FPGA memory interface
+ output [10:0] mem_addr,
+ output [4:0] mux_sel,
+ input [8:0] d_i, // data out of FPGA memories
+ output reg [8:0] d_o, // data into FPGA memories
+
+ // individual memory selects
+ output reg we,
+ output oe,
+ output reg dpram_tx_sel,
+ output reg dpram_rx_sel,
+ output reg dpram_ptrs_sel,
+ output reg [3:0] param_sel,
+ output reg pkt_filter_sel_01,
+ output reg pkt_filter_sel_02,
+ output reg pkt_filter_sel_03,
+ output reg pkt_filter_sel_10,
+ output reg pkt_filter_sel_12,
+ output reg pkt_filter_sel_13,
+ output reg pkt_filter_sel_20,
+ output reg pkt_filter_sel_21,
+ output reg pkt_filter_sel_23,
+ output reg pkt_filter_sel_2u,
+ output reg pkt_filter_sel_30,
+ output reg pkt_filter_sel_31,
+ output reg pkt_filter_sel_32,
+ output reg pkt_filter_sel_u2,
+ output reg interrupts_sel,
+ output reg[1:0] sci_sel_dual,
+ output reg[3:0] sci_sel_ch
+ );
+
+ localparam start_code = 9'h76;
+
+ wire bit_cnt_rstn; // async reset at start of SPI cycle
+
+ reg spi_clk_m1, spi_clk_m2; // detect / debounce the SPI CLK
+ reg spi_clk_high, spi_clk_high_m1; // one shot clocks for sequencing events
+
+ reg [4:0] bit_cnt; // cnt the bits
+ reg [6:0] word_cnt; // number of bytes transferred, OK to roll over
+
+ // capture these from SPI bus
+ reg [7:0] dev_ad; // device address
+ reg rwn; // follows address
+ reg [8:0] addr; // address
+
+ wire mem_sel, pkt_filter_sel; //
+
+ // async reset at the start of each SPI transfer
+ assign bit_cnt_rstn = spi_cs & rstn;
+
+ // debounce and capture the asynch spi_clk
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn ) begin
+ spi_clk_m1 <= 1'b0;
+ spi_clk_m2 <= 1'b0;
+ end
+ else if ( spi_cs ) begin
+ spi_clk_m1 <= spi_clk;
+ spi_clk_m2 <= spi_clk_m1;
+ end
+ end
+
+ // create two seq one shots for actions
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ spi_clk_high <= 0;
+ else if ( spi_cs && spi_clk && spi_clk_m1 && !spi_clk_m2 )
+ spi_clk_high <= 1'b1;
+ else
+ spi_clk_high <= 1'b0;
+ end
+
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ spi_clk_high_m1 <= 0;
+ else
+ spi_clk_high_m1 <= spi_clk_high;
+ end
+
+
+ /*
+ bit_cnt indicates the state of the SPI transfer
+
+ 0:7: dev_ad
+ 8: rwn
+ 9:17: addr
+ 18:26: data (repeats)
+ */
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ bit_cnt <= 5'h0;
+ else if ( spi_cs && spi_clk_high ) begin
+ if ( bit_cnt == 5'd26 )
+ bit_cnt <= 5'd18;
+ else
+ bit_cnt <= bit_cnt + 1;
+ end
+ end
+
+ // word_cnt
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if (!bit_cnt_rstn)
+ word_cnt <= 0;
+ else if ( spi_cs && spi_clk_high && bit_cnt == 5'd26 )
+ word_cnt <= word_cnt + 1;
+ end
+
+ /* Logic to capture common dev_ad, rwn, and address */
+
+ // capture dev_ad
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ dev_ad <= 'h0;
+ else if ( spi_cs && spi_clk_high && bit_cnt < 5'd8 )
+ dev_ad <= { dev_ad[6:0], spi_d_in };
+ end
+
+ // capture rwn bit
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ rwn <= 1'b1;
+ else if ( spi_cs && spi_clk_high && bit_cnt == 5'd8 )
+ rwn <= spi_d_in;
+ end
+
+ // capture addr
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if (!bit_cnt_rstn)
+ addr <= 'h0;
+ else if (spi_cs ) begin
+ if ( spi_clk_high && bit_cnt >= 5'd9 && bit_cnt <= 5'd17 )
+ addr <= { addr[7:0], spi_d_in };
+ // delay advancing addr until write completes
+ else if ( spi_clk_high_m1 && bit_cnt == 5'd26 )
+ addr <= addr + 1;
+ end
+ end
+
+ assign mem_addr = { dev_ad[1:0], addr };
+
+ /* Logic for write data into FPGA */
+
+ // capture write data
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if (!bit_cnt_rstn)
+ d_o <= 8'h0;
+ else if (spi_cs && spi_clk_high && !rwn && bit_cnt >= 5'd18)
+ d_o <= { d_o[7:0], spi_d_in };
+ end
+
+ // we
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if (!bit_cnt_rstn)
+ we <= 1'b0;
+ else if ( spi_cs ) begin
+ if ( spi_clk_high && !rwn && bit_cnt == 5'd25 )
+ we <= 1'b1;
+ else
+ we <= 1'b0;
+ end;
+ end
+
+ /* SPI data output enable */
+ assign spi_d_oe = spi_cs;
+
+
+ /*
+ * clock out msb first.
+ * First bit comes out on cs
+ */
+ always @(posedge clk or negedge bit_cnt_rstn)
+ begin
+ if ( !bit_cnt_rstn )
+ spi_d_o <= start_code[8];
+ else if ( spi_cs && spi_clk_high_m1 ) begin
+ if ( bit_cnt < 9 )
+ spi_d_o <= start_code['d8 - bit_cnt[3:0]];
+ else if ( !rwn && bit_cnt >= 'd18 )
+ spi_d_o <= word_cnt['d8 - bit_cnt[3:0]];
+ else if ( rwn && bit_cnt >= 'd18 )
+ spi_d_o <= d_i['d8 - bit_cnt[3:0]];
+ else
+ spi_d_o <= 1'b0;
+ end
+ end
+
+ assign oe = ( (dpram_rx_sel || dpram_tx_sel) && rwn) ? 1'b1 : 1'b0;
+
+
+ /* Address Decoding */
+ assign pkt_filter_sel = pkt_filter_sel_01 | pkt_filter_sel_02 | pkt_filter_sel_10 | pkt_filter_sel_12 |
+ pkt_filter_sel_20 | pkt_filter_sel_21 | pkt_filter_sel_23;
+
+ assign mem_sel = dpram_rx_sel | dpram_tx_sel | dpram_ptrs_sel | pkt_filter_sel | param_sel[0] |
+ param_sel[1] | param_sel[2] | param_sel[2] | interrupts_sel;
+
+
+ // use to steer data into this module
+ assign mux_sel = dev_ad[6:2];
+
+ // address decode
+ always @(*)
+ begin
+ sci_sel_dual = 2'b00;
+ sci_sel_ch = 4'b000;
+ dpram_rx_sel = 1'b0;
+ dpram_tx_sel = 1'b0;
+ dpram_ptrs_sel = 1'b0;
+ pkt_filter_sel_01 = 1'b0;
+ pkt_filter_sel_02 = 1'b0;
+ pkt_filter_sel_10 = 1'b0;
+ pkt_filter_sel_12 = 1'b0;
+ pkt_filter_sel_20 = 1'b0;
+ pkt_filter_sel_21 = 1'b0;
+ pkt_filter_sel_23 = 1'b0;
+ param_sel = 4'b0000;
+ interrupts_sel = 1'b0;
+ casez( dev_ad[7:0] )
+ 8'b00000000: sci_sel_ch = 4'b0001; // sgmii0
+ 8'b00000001: sci_sel_ch = 4'b0010; // sgmii1
+ 8'b00000010: sci_sel_dual = 2'b01; // DCU0 Dual
+ 8'b00000100: sci_sel_ch = 4'b0100; // sgmii2
+ 8'b00000101: sci_sel_ch = 4'b1000; // sgmii3
+ 8'b00000110: sci_sel_dual = 2'b10; // DCU1 Dual
+ 8'b000010??: dpram_rx_sel = 1'b1;
+ 8'b000011??: dpram_tx_sel = 1'b1;
+ 8'b00010000: dpram_ptrs_sel = 1'b1;
+ 8'b00010001: interrupts_sel = 1'b1;
+ 8'b001000??: param_sel[0] = 1'b1;
+ 8'b001001??: param_sel[1] = 1'b1;
+ 8'b001010??: param_sel[2] = 1'b1;
+ 8'b001011??: param_sel[3] = 1'b1;
+ 8'b01000001: pkt_filter_sel_01 = 1'b1;
+ 8'b01000010: pkt_filter_sel_02 = 1'b1;
+ 8'b01000011: pkt_filter_sel_03 = 1'b1;
+ 8'b01001000: pkt_filter_sel_10 = 1'b1;
+ 8'b01001010: pkt_filter_sel_12 = 1'b1;
+ 8'b01001011: pkt_filter_sel_13 = 1'b1;
+ 8'b01010000: pkt_filter_sel_20 = 1'b1;
+ 8'b01010001: pkt_filter_sel_21 = 1'b1;
+ 8'b01010011: pkt_filter_sel_23 = 1'b1;
+ 8'b01010111: pkt_filter_sel_2u = 1'b1;
+ 8'b01011000: pkt_filter_sel_30 = 1'b1;
+ 8'b01011001: pkt_filter_sel_31 = 1'b1;
+ 8'b01011010: pkt_filter_sel_32 = 1'b1;
+ 8'b01111010: pkt_filter_sel_u2 = 1'b1;
+ endcase
+ end
+
+ endmodule
+