blob: 4c0df3268408a57269712903468241af3c097229 [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 FW Mode: Intention of this mode is to download FW image. Doesn't parse Commands
//
module spi_fwmode (
// MOSI
input clk_in_i,
input rst_in_ni,
// MISO
input clk_out_i,
input rst_out_ni,
// Configurations
// No sync logic. Configuration should be static when SPI operating
input cpha_i,
input cfg_rxorder_i, // 1: 0->7 , 0:7->0
input cfg_txorder_i, // 1: 0->7 , 0:7->0
input spi_device_pkg::spi_mode_e mode_i, // Only works at mode_i == FwMode
// RX, TX FIFO interface
output logic rx_wvalid_o,
output spi_device_pkg::spi_byte_t rx_data_o,
output logic tx_rready_o,
input spi_device_pkg::spi_byte_t tx_data_i,
// SPI Interface: clock is given (ckl_in_i, clk_out_i)
input csb_i,
input mosi,
output logic miso,
output logic miso_oe
);
import spi_device_pkg::*;
localparam int unsigned BITS = $bits(spi_byte_t);
localparam int unsigned BITWIDTH = $clog2(BITS);
logic [BITWIDTH-1:0] rx_bitcount;
typedef enum {
TxIdle,
TxActive
} tx_state_e;
tx_state_e tx_state; // Only for handling CPHA
spi_byte_t rx_data_d, rx_data_q;
// Serial to Parallel
always_comb begin
if (cfg_rxorder_i) begin
rx_data_d = {mosi, rx_data_q[BITS-1:1]};
end else begin
rx_data_d = {rx_data_q[BITS-2:0], mosi};
end
end
always_ff @(posedge clk_in_i) begin
rx_data_q <= rx_data_d;
end
// As SCK shut off right after bytes are transferred,
// HW should give current MOSI and latched version of rx_data
// if not, FIFO request should be generated next cycle but it cannot be (as no clock exist)
// It means RX_FIFO should latch the write request at negedge of clk_in_i
assign rx_data_o = rx_data_d;
// Counter to generate write signal
always_ff @(posedge clk_in_i or negedge rst_in_ni) begin
if (!rst_in_ni) begin
rx_bitcount <= BITWIDTH'(BITS-1);
end else begin
if (rx_bitcount == '0) begin
rx_bitcount <= BITWIDTH'(BITS-1);
end else begin
rx_bitcount <= rx_bitcount -1;
end
end
end
assign rx_wvalid_o = (rx_bitcount == '0);
// TX Serialize
logic [BITWIDTH-1:0] tx_bitcount;
logic first_bit, last_bit;
spi_byte_t miso_shift;
assign first_bit = (tx_bitcount == BITWIDTH'(BITS-1)) ? 1'b1 : 1'b0;
assign last_bit = (tx_bitcount == '0) ? 1'b1 : 1'b0;
// Pop the entry from the FIFO at bit 1.
// This let the module pop the entry correctly when CPHA == 1
// If CPHA is set, there is no clock posedge after bitcnt is 0.
// So TX Async FIFO pop signal cannot be latched inside FIFO.
// It is safe to pop between bitcnt 6 to 1. If pop signal is asserted
// when bitcnt 7 it can pop twice if CPHA is 1.
assign tx_rready_o = (tx_bitcount == BITWIDTH'(1)); // Pop at second bit transfer
always_ff @(posedge clk_out_i or negedge rst_out_ni) begin
if (!rst_out_ni) begin
tx_bitcount <= BITWIDTH'(BITS-1);
end else begin
if (last_bit) begin
tx_bitcount <= BITWIDTH'(BITS-1);
end else if (tx_state != TxIdle || cpha_i == 1'b0) begin
tx_bitcount <= tx_bitcount - 1'b1;
end
end
end
always_ff @(posedge clk_out_i or negedge rst_out_ni) begin
if (!rst_out_ni) begin
tx_state <= TxIdle;
end else begin
tx_state <= TxActive;
end
end
assign miso = (cfg_txorder_i) ? ((~first_bit) ? miso_shift[0] : tx_data_i[0]) :
(~first_bit) ? miso_shift[7] : tx_data_i[7] ;
assign miso_oe = ~csb_i;
always_ff @(posedge clk_out_i) begin
if (cfg_txorder_i) begin
if (first_bit) begin
miso_shift <= {1'b0, tx_data_i[7:1]};
end else begin
miso_shift <= {1'b0, miso_shift[7:1]};
end
end else begin
if (first_bit) begin
// Dummy byte cannot be used. empty signal could be delayed two clocks to cross
// async clock domain. It means even FW writes value to FIFO, empty signal deasserts
// after two negative edge of SCK. HW module already in the middle of sending DUMMY.
miso_shift <= {tx_data_i[6:0], 1'b0};
end else begin
miso_shift <= {miso_shift[6:0], 1'b0};
end
end
end
endmodule