From 2727f62ab8eec27ab50e3fa4040a394917f74cb5 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 28 Jul 2015 16:31:29 -0700 Subject: [PATCH] fpga: created axi stream controled spi core --- fpga/control_lib/Makefile.srcs | 1 + fpga/control_lib/axis_spi_core.v | 243 +++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 fpga/control_lib/axis_spi_core.v diff --git a/fpga/control_lib/Makefile.srcs b/fpga/control_lib/Makefile.srcs index 579e1026..5ade3135 100644 --- a/fpga/control_lib/Makefile.srcs +++ b/fpga/control_lib/Makefile.srcs @@ -58,5 +58,6 @@ gpio_atr.v \ user_settings.v \ settings_fifo_ctrl.v \ simple_spi_core.v \ +axis_spi_core.v \ simple_i2c_core.v \ )) diff --git a/fpga/control_lib/axis_spi_core.v b/fpga/control_lib/axis_spi_core.v new file mode 100644 index 00000000..91a7b886 --- /dev/null +++ b/fpga/control_lib/axis_spi_core.v @@ -0,0 +1,243 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// Simple SPI core, the simplest, yet complete spi core I can think of + +// Settings register controlled. +// 2 settings regs, control and data +// 1 32-bit readback and status signal + +// Settings reg map: +// +// BASE+0 divider setting +// bits [15:0] spi clock divider +// +// BASE+1 configuration input +// bits [23:0] slave select, bit0 = slave0 enabled +// bits [29:24] num bits (1 through 32) +// bit [30] data input edge = in data bit latched on rising edge of clock +// bit [31] data output edge = out data bit latched on rising edge of clock +// +// BASE+2 input data +// Writing this register begins a spi transaction. +// Bits are latched out from bit 0. +// Therefore, load this register in reverse. +// +// Readback +// Bits are latched into bit 0. +// Therefore, data will be in-order. + +module axis_spi_core + #( + //set to 1 for ILA + parameter DEBUG = 0, + + //tdest width for number of core users + parameter DESTW = 1, + + //width of serial enables (up to 24 is possible) + parameter WIDTH = 8, + + //idle state of the spi clock + parameter CLK_IDLE = 0, + + //idle state of the serial enables + parameter SEN_IDLE = 24'hffffff + ) + ( + //clock and synchronous reset + input clock, input reset, + + //configuration settings bus + input [DESTW-1:0] CONFIG_tdest, + input [79:0] CONFIG_tdata, + input CONFIG_tvalid, + output CONFIG_tready, + + //32-bit data readback + output reg [DESTW-1:0] READBACK_tdest, + output [31:0] READBACK_tdata, + output READBACK_tvalid, + input READBACK_tready, + + //spi interface, slave selects, clock, data in, data out + output [WIDTH-1:0] sen, + output sclk, + output mosi, + input miso + ); + + //state + localparam WAIT_CONFIG = 0; + localparam PRE_IDLE = 1; + localparam CLK_REG = 2; + localparam CLK_INV = 3; + localparam POST_IDLE = 4; + localparam IDLE_SEN = 5; + localparam WAIT_READBACK = 6; + reg [2:0] state; + + //configuration settings + reg [15:0] sclk_divider; + reg [23:0] slave_select; + reg [5:0] num_bits; + reg datain_edge, dataout_edge; + + //output ready/valid signals + assign CONFIG_tready = (state == WAIT_CONFIG); + assign READBACK_tvalid = (state == WAIT_READBACK); + + //serial clock either idles or is in one of two clock states + reg sclk_reg; + assign sclk = sclk_reg; + + //serial enables either idle or enabled based on state + wire sen_is_idle = (state == WAIT_CONFIG) || (state == IDLE_SEN); + wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select); + reg [WIDTH-1:0] sen_reg; + always @(posedge clock) sen_reg <= sen24[WIDTH-1:0]; + assign sen = sen_reg; + + //data output shift register + reg [31:0] dataout_reg; + wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0}; + assign mosi = dataout_reg[31]; + + //data input shift register + reg [31:0] datain_reg; + wire [31:0] datain_next = {datain_reg[30:0], miso}; + assign READBACK_tdata = datain_reg; + + //counter for spi clock + reg [15:0] sclk_counter; + wire sclk_counter_done = (sclk_counter == sclk_divider); + wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1; + + //counter for latching bits miso/mosi + reg [6:0] bit_counter; + wire [6:0] bit_counter_next = bit_counter + 1; + wire bit_counter_done = (bit_counter_next == num_bits); + + always @(posedge clock) begin + if (reset) begin + state <= WAIT_CONFIG; + sclk_reg <= CLK_IDLE; + end + else begin + case (state) + + WAIT_CONFIG: begin + if (CONFIG_tvalid && CONFIG_tready) begin + state <= PRE_IDLE; + end + {sclk_divider, dataout_edge, datain_edge, num_bits, slave_select, dataout_reg} <= CONFIG_tdata; + READBACK_tdest <= CONFIG_tdest; + sclk_counter <= 0; + bit_counter <= 0; + sclk_reg <= CLK_IDLE; + end + + PRE_IDLE: begin + if (sclk_counter_done) state <= CLK_REG; + sclk_counter <= sclk_counter_next; + sclk_reg <= CLK_IDLE; + end + + CLK_REG: begin + if (sclk_counter_done) begin + state <= CLK_INV; + if (datain_edge != CLK_IDLE) datain_reg <= datain_next; + if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next; + sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0 + end + sclk_counter <= sclk_counter_next; + end + + CLK_INV: begin + if (sclk_counter_done) begin + state <= (bit_counter_done)? POST_IDLE : CLK_REG; + bit_counter <= bit_counter_next; + if (datain_edge == CLK_IDLE) datain_reg <= datain_next; + if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next; + sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0 + end + sclk_counter <= sclk_counter_next; + end + + POST_IDLE: begin + if (sclk_counter_done) state <= IDLE_SEN; + sclk_counter <= sclk_counter_next; + sclk_reg <= CLK_IDLE; + end + + IDLE_SEN: begin + if (sclk_counter_done) state <= WAIT_READBACK; + sclk_counter <= sclk_counter_next; + sclk_reg <= CLK_IDLE; + end + + WAIT_READBACK: begin + if (READBACK_tready && READBACK_tvalid) begin + state <= WAIT_CONFIG; + end + end + + default: state <= WAIT_CONFIG; + + endcase //state + end + end + + /******************************************************************* + * Debug + ******************************************************************/ + generate + if (DEBUG == 1) begin + wire [35:0] CONTROL0; + chipscope_icon chipscope_icon + ( + .CONTROL0(CONTROL0) + ); + wire [255:0] DATA; + wire [7:0] TRIG0; + chipscope_ila chipscope_ila + ( + .CONTROL(CONTROL0), + .CLK(clock), + .DATA(DATA), + .TRIG0(TRIG0) + ); + assign TRIG0 = + { + 4'b0, + CONFIG_tvalid, CONFIG_tready, + READBACK_tvalid, READBACK_tready + }; + + assign DATA[79:0] = CONFIG_tdata; + assign DATA[111:80] = READBACK_tdata; + + assign DATA[112] = CONFIG_tvalid; + assign DATA[113] = CONFIG_tready; + assign DATA[114] = READBACK_tvalid; + assign DATA[115] = READBACK_tready; + + assign DATA[127:120] = state; + end + endgenerate + +endmodule //axis_spi_core