[spi_device] Add serial to parallel logic This commit changes the serial-to-parllel logic in SPI_DEVICE to support Dual/ Quad IO modes. Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/spi_device/rtl/spi_device_pkg.sv b/hw/ip/spi_device/rtl/spi_device_pkg.sv index cf77709..b15626c 100644 --- a/hw/ip/spi_device/rtl/spi_device_pkg.sv +++ b/hw/ip/spi_device/rtl/spi_device_pkg.sv
@@ -11,10 +11,27 @@ typedef enum logic [1:0] { FwMode = 'h0, EepromRam = 'h1, - EepromFlash = 'h2, - PassThrough = 'h3 + PassThrough = 'h2 } spi_mode_e; + // SPI IO mode + typedef enum logic [1:0] { + SingleIO = 2'h 0, + DualIO = 2'h 1, + QuadIO = 2'h 2 + } io_mode_e; + + // SPI Line Mode (Mode0 <-> Mode3) + // This HWIP does not support Mode1 and Mode2 + typedef enum logic { + // Mode0: CPOL=0, CPHA=0 + // Data sampled on rising edge and shifted on falling edge + LineMode0 = 1'b 0, + // Mode3: CPOL=1, CPHA=1 + // Data sampled on falling edge and shifted on rising edge + LineMode3 = 1'b 1 + } line_mode_e; + // SPI Read mode. QUAD uses additional two pins to read // Bit 0: Single, Bit 1: Dual Bit 2: Quad typedef logic [2:0] spi_rdmode_t;
diff --git a/hw/ip/spi_device/rtl/spi_s2p.sv b/hw/ip/spi_device/rtl/spi_s2p.sv new file mode 100644 index 0000000..8b3693e --- /dev/null +++ b/hw/ip/spi_device/rtl/spi_s2p.sv
@@ -0,0 +1,126 @@ +// 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