1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
//
// Copyright 2020 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: simple_spi_core_64bit
// Description:
// Simple SPI core based on simple_spi_core.v. Extended to 64 bit transmissions
// while preserving the same control interface for 32 bit transmissions.
// Settings register controlled.
// 4 settings regs, control and data
// 1 up to 64-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 (0 through 63; value of 0 transmits 64 bits)
// 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 (bits 63...32)
// Writing this register begins a spi transaction. If up to 32 bits (MAX_BITS <=
// 32) are required only this input data register needs to be written. Bits are
// latched out from bit 63. Therefore, load this register aligning with MSBs.
//
// BASE+3 input data (bits 31...0, present if MAX_BITS>32)
// This register needs to be written before accessing BASE+2 input data
// register when MAX_BITS > 32.
//
// Readback
// Bits are latched into bit 0.
module simple_spi_core_64bit
#(
//settings register base address
parameter BASE = 0,
//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,
//maximum number of bits for single transmission (<=64)
parameter MAX_BITS = 32
)
(
//clock and synchronous reset
input clock, input reset,
//32-bit settings bus inputs
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//up to 64-bit data readback
output [MAX_BITS-1:0] readback,
output reg readback_stb,
//read is high when spi core can begin another transaction
output ready,
//spi interface, slave selects, clock, data in, data out
output [WIDTH-1:0] sen,
output reg sclk = CLK_IDLE,
output reg mosi,
input miso,
//optional debug output
output [23:0] debug
);
// assert for MAX_BITS
generate
if (MAX_BITS > 64 || MAX_BITS < 1) begin
MAX_BITS_must_be_between_1_and_64();
end
endgenerate
wire [15:0] sclk_divider;
setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(sclk_divider),.changed());
wire [23:0] slave_select;
wire [5:0] num_bits;
wire datain_edge, dataout_edge;
setting_reg #(.my_addr(BASE+1),.width(32)) ctrl_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
wire [63:0] mosi_data;
wire trigger_spi;
setting_reg #(.my_addr(BASE+2),.width(32)) data_upper_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(mosi_data[63:32]),.changed(trigger_spi));
setting_reg #(.my_addr(BASE+3),.width(32)) data_lower_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(mosi_data[31:0]),.changed());
localparam WAIT_TRIG = 0;
localparam PRE_IDLE = 1;
localparam CLK_REG = 2;
localparam CLK_INV = 3;
localparam POST_IDLE = 4;
localparam IDLE_SEN = 5;
reg [2:0] state = WAIT_TRIG;
reg ready_reg;
assign ready = ready_reg && ~trigger_spi;
//serial clock either idles or is in one of two clock states
//One pipeline stage to align output data with clock edge.
reg sclk_reg = CLK_IDLE;
always @(posedge clock) begin
sclk <= sclk_reg;
end
//serial enables either idle or enabled based on state
// IJB. One pipeline stage to break critical path from register in I/O pads.
wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
wire [23:0] sen24 = (sen_is_idle) ? SEN_IDLE : (SEN_IDLE ^ slave_select);
reg [WIDTH-1:0] sen_reg = SEN_IDLE[WIDTH-1:0];
always @(posedge clock) begin
if (reset) begin
sen_reg <= SEN_IDLE[WIDTH-1:0];
end else begin
sen_reg <= sen24[WIDTH-1:0];
end
end
assign sen = sen_reg;
//data output shift register
// IJB. One pipeline stage to break critical path from register in I/O pads.
reg [MAX_BITS-1:0] dataout_reg = {MAX_BITS {1'b0}};
wire [MAX_BITS-1:0] dataout_next = {dataout_reg[MAX_BITS-2:0], 1'b0};
always @(posedge clock) begin
mosi <= dataout_reg[MAX_BITS-1];
end
//data input shift register
// IJB. Two pipeline stages to break critical path from register in I/O pads.
reg miso_pipe, miso_pipe2;
always @(posedge clock) begin
miso_pipe2 <= miso;
miso_pipe <= miso_pipe2;
end
// Register to control input data capturing, compensating 2 miso_pipe
// registers and the output register on mosi/sclk.
//
// When sclk_counter_done is asserted the FSM below updates sclk_reg and
// dataout_reg (compensated by datain_capture_reg).
// One clock cycle later those values get propagated to sclk and mosi. On
// the active datain_edge miso would be capture into miso_pipe2. This clock
// cycle is compensated by datain_capture_pipe[0].
// Propagation of miso data to miso_pipe is compensated by
// datain_capture_pipe[1].
// On the next clock edge datain_capture_pipe[1] serves as enable signal for
// datain_reg which then consumes the aligned data from miso_pipe.
reg datain_capture_reg = 1'b0;
reg [1:0] datain_capture_pipe;
wire capturing_done = ~| {datain_capture_reg, datain_capture_pipe};
reg [MAX_BITS-1:0] datain_reg;
wire [MAX_BITS-1:0] datain_next = {datain_reg[MAX_BITS-2:0], miso_pipe};
assign readback = datain_reg;
always @(posedge clock) begin
datain_capture_pipe <= {datain_capture_pipe[0], datain_capture_reg};
if (datain_capture_pipe[1]) begin
datain_reg <= datain_next;
end
end
//counter for spi clock
reg [15:0] sclk_counter = 16'b0;
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 [5:0] bit_counter = 6'b0;
wire [5: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_TRIG;
sclk_reg <= CLK_IDLE;
ready_reg <= 0;
readback_stb <= 1'b0;
end else begin
datain_capture_reg <= 1'b0;
case (state)
WAIT_TRIG: begin
if (trigger_spi) begin
state <= PRE_IDLE;
end
readback_stb <= 1'b0;
ready_reg <= ~trigger_spi;
dataout_reg <= mosi_data[63:64-MAX_BITS];
sclk_counter <= 0;
bit_counter <= 0;
sclk_reg <= CLK_IDLE;
end
PRE_IDLE: begin
if (sclk_counter_done) begin
state <= CLK_REG;
end
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) begin
datain_capture_reg <= 1'b1;
end
if (dataout_edge != CLK_IDLE && bit_counter != 0) begin
dataout_reg <= dataout_next;
end
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) begin
datain_capture_reg <= 1'b1;
end
if (dataout_edge == CLK_IDLE && ~bit_counter_done) begin
dataout_reg <= dataout_next;
end
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) begin
state <= IDLE_SEN;
end
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
IDLE_SEN: begin
if (sclk_counter_done && capturing_done) begin
ready_reg <= 1'b1;
readback_stb <= 1'b1;
state <= WAIT_TRIG;
end
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
default: begin
state <= WAIT_TRIG;
end
endcase //state
end
end
assign debug = {
trigger_spi, state, //4
sclk, mosi, miso, ready, //4
2'b0, bit_counter[5:0], //8
sclk_counter_done, bit_counter_done, //2
sclk_counter[5:0] //6
};
endmodule //simple_spi_core
|