//======================================================================
//
// mkmif_spi.v
// -----------
// SPI interface for the master key memory. When enabled the
// interface waits for command to transmit and receive a given
// number of bytes. Data is transmitted onto the spi_di port
// from the MSB of the spi_data register. Simultaneously,
// data captured on the spi_do port is inserted at LSB in the
// spi_data register. The spi clock is generated when data is to be
// sent or recived.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2016, NORDUnet A/S All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of the NORDUnet nor the names of its contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
module mkmif_spi(
input wire clk,
input wire reset_n,
output wire spi_sclk,
output wire spi_cs_n,
input wire spi_do,
output wire spi_di,
input wire set,
input wire start,
input wire [2 : 0] length,
input wire [15 : 0] divisor,
output wire ready,
input wire [55 : 0] wr_data,
output wire [31 : 0] rd_data
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam CTRL_IDLE = 2'h0;
localparam CTRL_START = 2'h1;
localparam CTRL_WAIT = 2'h2;
localparam CTRL_DONE = 2'h3;
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg do_sample0_reg;
reg do_sample1_reg;
reg cs_n_reg;
reg cs_n_new;
reg cs_n_we;
reg ready_reg;
reg ready_new;
reg ready_we;
reg [55 : 0] data_reg;
reg [55 : 0] data_new;
reg data_set;
reg data_nxt;
reg data_we;
reg sclk_reg;
reg sclk_new;
reg sclk_rst;
reg sclk_en;
reg sclk_we;
reg [15 : 0] clk_ctr_reg;
reg [15 : 0] clk_ctr_new;
reg clk_ctr_we;
reg [5 : 0] bit_ctr_reg;
reg [5 : 0] bit_ctr_new;
reg bit_ctr_rst;
reg bit_ctr_inc;
reg bit_ctr_done;
reg bit_ctr_we;
reg [2 : 0] length_reg;
reg length_we;
reg [15 : 0] divisor_reg;
reg divisor_we;
reg [1 : 0] spi_ctrl_reg;
reg [1 : 0] spi_ctrl_new;
reg spi_ctrl_we;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign spi_sclk = sclk_reg;
assign spi_cs_n = cs_n_reg;
assign spi_di = data_reg[55];
assign rd_data = data_reg[31 : 0];
assign ready = ready_reg;
//----------------------------------------------------------------
// reg_update
// Update functionality for all registers in the core.
// All registers are positive edge triggered with asynchronous
// active low reset.
//----------------------------------------------------------------
always @ (posedge clk or negedge reset_n)
begin
if (!reset_n)
begin
do_sample0_reg <= 1'h0;
do_sample1_reg <= 1'h0;
cs_n_reg <= 1'h1;
ready_reg <= 1'h0;
length_reg <= 3'h0;
divisor_reg <= 16'h0;
data_reg <= 56'h0;
sclk_reg <= 1'h0;
clk_ctr_reg <= 16'h0;
bit_ctr_reg <= 6'h0;
spi_ctrl_reg <= CTRL_IDLE;
end
else
begin
do_sample0_reg <= spi_do;
do_sample1_reg <= do_sample0_reg;
if (cs_n_we)
cs_n_reg <= cs_n_new;
if (ready_we)
ready_reg <= ready_new;
if (data_we)
data_reg <= data_new;
if (length_we)
length_reg <= length;
if (divisor_we)
divisor_reg <= divisor;
if (sclk_we)
sclk_reg <= sclk_new;
if (clk_ctr_we)
clk_ctr_reg <= clk_ctr_new;
if (bit_ctr_we)
bit_ctr_reg <= bit_ctr_new;
if (spi_ctrl_we)
spi_ctrl_reg <= spi_ctrl_new;
end
end // reg_update
//----------------------------------------------------------------
// data_gen
//
// Generate the data bitstream to be written out to the external
// SPI connected memory. Basically a shift register.
// Note that we also shift in data received from the external
// memory.
//----------------------------------------------------------------
always @*
begin : data_gen
data_new = 56'h0;
data_we = 0;
if (data_set)
begin
data_new = wr_data;
data_we = 1;
end
if (data_nxt)
begin
data_new = {data_reg[54 : 0], do_sample1_reg};
data_we = 1;
end
end // data_gen
//----------------------------------------------------------------
// sclk_gen
//
// Generator of the spi_sclk clock.
//----------------------------------------------------------------
always @*
begin : sclk_gen
sclk_new = 0;
sclk_we = 0;
clk_ctr_new = 0;
clk_ctr_we = 0;
data_nxt = 0;
bit_ctr_rst = 0;
bit_ctr_inc = 0;
if (sclk_rst)
begin
clk_ctr_new = 0;
clk_ctr_we = 1;
bit_ctr_rst = 1;
sclk_new = 0;
sclk_we = 1;
end
if (sclk_en)
begin
if (clk_ctr_reg == divisor_reg)
begin
clk_ctr_new = 0;
clk_ctr_we = 1'b1;
sclk_new = ~sclk_reg;
sclk_we = 1;
if (sclk_reg)
begin
bit_ctr_inc = 1;
data_nxt = 1;
end
end
else
begin
clk_ctr_new = clk_ctr_reg + 1'b1;
clk_ctr_we = 1'b1;
end
end
end // sclk_gen
//----------------------------------------------------------------
// bit_ctr
//
// Bit counter used by the FSM to keep track of the number bits
// being read from or written to the memory.
//----------------------------------------------------------------
always @*
begin : bit_ctr
bit_ctr_new = 6'h0;
bit_ctr_we = 1'b0;
bit_ctr_done = 1'b0;
if (bit_ctr_reg == {length_reg, 3'h0})
bit_ctr_done = 1'b1;
if (bit_ctr_rst)
begin
bit_ctr_new = 6'h0;
bit_ctr_we = 1'b1;
end
if (bit_ctr_inc)
begin
bit_ctr_new = bit_ctr_reg + 1'b1;
bit_ctr_we = 1'b1;
end
end // bit_ctr
//----------------------------------------------------------------
// spi_ctrl
//
// Control FSM for the SPI interface.
//----------------------------------------------------------------
always @*
begin : spi_ctrl
sclk_en = 0;
sclk_rst = 0;
cs_n_new = 1;
cs_n_we = 0;
data_set = 0;
length_we = 0;
divisor_we = 0;
ready_new = 0;
ready_we = 0;
spi_ctrl_new = CTRL_IDLE;
spi_ctrl_we = 0;
case (spi_ctrl_reg)
CTRL_IDLE:
begin
ready_new = 1;
ready_we = 1;
if (set)
begin
data_set = 1;
length_we = 1;
divisor_we = 1;
end
if (start)
begin
ready_new = 0;
ready_we = 1;
sclk_rst = 1;
spi_ctrl_new = CTRL_START;
spi_ctrl_we = 1;
end
end
CTRL_START:
begin
cs_n_new = 0;
cs_n_we = 1;
spi_ctrl_new = CTRL_WAIT;
spi_ctrl_we = 1;
end
CTRL_WAIT:
begin
sclk_en = 1;
if (bit_ctr_done)
begin
spi_ctrl_new = CTRL_DONE;
spi_ctrl_we = 1;
end
end
CTRL_DONE:
begin
ready_new = 1;
ready_we = 1;
cs_n_new = 1;
cs_n_we = 1;
spi_ctrl_new = CTRL_IDLE;
spi_ctrl_we = 1;
end
default:
begin
end
endcase // case (spi_ctrl_reg)
end // spi_ctrl
endmodule // mkmif_spi
//======================================================================
// EOF mkmif_spi.v
//======================================================================