blob: 8b3693e139f4705663461be3c78b1214e51b40e8 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// SPI Serial-to-Parallel interface
module spi_s2p
import spi_device_pkg::*;
(
input clk_i,
input rst_ni, // inverted CSb input
// SPI data
input [3:0] s_i,
// to following logic
output logic data_valid_o,
output spi_byte_t data_o,
output logic [11:0] bitcnt_o, // up to 256B payload
// Configuration
input order_i,
input spi_device_pkg::io_mode_e io_mode_i
);
/////////////////
// Definitions //
/////////////////
// Maximum Length of a transaction is:
// 8 bit opcode + 24 or 32 bit address +
// max 8 bit dummy cycle + 256B payload
localparam int unsigned Length = 8 + 32 + 8 + 2048;
localparam int unsigned BitCntW = $clog2(Length+1);
localparam int unsigned Bits = $bits(spi_byte_t);
localparam int unsigned BitWidth = $clog2(Bits);
typedef logic [BitWidth-1:0] count_t;
typedef logic [$clog2(Length+1)-1:0] bitcount_t;
count_t cnt;
bitcount_t bitcnt;
spi_byte_t data_d, data_q;
always_comb begin
unique case (io_mode_i)
SingleIO: begin
data_d = (order_i) ? {s_i[0], data_q[7:1]} : {data_q[6:0], s_i[0]};
end
DualIO: begin
data_d = (order_i) ? {s_i[1:0], data_q[7:2]} : {data_q[5:0], s_i[1:0]};
end
QuadIO: begin
data_d = (order_i) ? {s_i[3:0], data_q[7:4]} : {data_q[3:0], s_i[3:0]};
end
default: begin
data_d = data_q;
end
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
data_q <= '0;
end else begin
data_q <= data_d;
end
end
// send un-latched data
assign data_o = data_d;
// total bit count
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
bitcnt <= '0;
end else begin
unique case (io_mode_i)
SingleIO: bitcnt <= bitcnt + bitcount_t'('h1);
DualIO: bitcnt <= bitcnt + bitcount_t'('h2);
QuadIO: bitcnt <= bitcnt + bitcount_t'('h4);
default: bitcnt <= bitcnt;
endcase
end
end
assign bitcnt_o = bitcnt;
// Bitcount in a byte
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt <= count_t'(Bits-1);
end else if (cnt == '0) begin
cnt <= count_t'(Bits-1);
end else begin
unique case (io_mode_i)
SingleIO: cnt <= cnt - count_t'('h1);
DualIO: cnt <= cnt - count_t'('h2);
QuadIO: cnt <= cnt - count_t'('h4);
default: cnt <= cnt;
endcase
end
end
// data valid
always_comb begin
unique case (io_mode_i)
SingleIO: data_valid_o = (cnt == 'h0);
DualIO: data_valid_o = (cnt == 'h1);
QuadIO: data_valid_o = (cnt == 'h3);
default: data_valid_o = 1'b 0;
endcase
end
////////////////
// Assertions //
////////////////
// Right after reset (CSb assert), the io_mode_i shall be Single IO
// to decode SPI Opcode.
`ASSERT(IoModeDefault_A, $rose(rst_ni) |-> io_mode_i == SingleIO, clk_i, 0)
endmodule