../src/lowrisc_ip_spi_device_0.1/rtl/spi_fwmode.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // SPI FW Mode: Intention of this mode is to download FW image. Doesn't parse Commands
   6: //
   7: 
   8: module spi_fwmode (
   9:   // MOSI
  10:   input clk_in_i,
  11:   input rst_in_ni,
  12: 
  13:   // MISO
  14:   input clk_out_i,
  15:   input rst_out_ni,
  16: 
  17:   // Configurations
  18:   // No sync logic. Configuration should be static when SPI operating
  19:   input                             cpha_i,
  20:   input                             cfg_rxorder_i, // 1: 0->7 , 0:7->0
  21:   input                             cfg_txorder_i, // 1: 0->7 , 0:7->0
  22:   input  spi_device_pkg::spi_mode_e mode_i, // Only works at mode_i == FwMode
  23: 
  24:   // RX, TX FIFO interface
  25:   output logic                      rx_wvalid_o,
  26:   input                             rx_wready_i,
  27:   output spi_device_pkg::spi_byte_t rx_data_o,
  28: 
  29:   input                             tx_rvalid_i,
  30:   output logic                      tx_rready_o,
  31:   input  spi_device_pkg::spi_byte_t tx_data_i,
  32: 
  33:   output logic                      rx_overflow_o,
  34:   output logic                      tx_underflow_o,
  35: 
  36:   // SPI Interface: clock is given (ckl_in_i, clk_out_i)
  37:   input        csb_i,
  38:   input        mosi,
  39:   output logic miso,
  40:   output logic miso_oe
  41: );
  42: 
  43:   import spi_device_pkg::*;
  44: 
  45:   localparam int unsigned BITS     = $bits(spi_byte_t);
  46:   localparam int unsigned BITWIDTH = $clog2(BITS);
  47: 
  48:   logic [BITWIDTH-1:0] rx_bitcount;
  49: 
  50:   typedef enum logic {
  51:     TxIdle,
  52:     TxActive
  53:   } tx_state_e;
  54:   tx_state_e tx_state;   // Only for handling CPHA
  55: 
  56:   spi_byte_t rx_data_d, rx_data_q;
  57: 
  58:   // Serial to Parallel
  59:   always_comb begin
  60:     if (cfg_rxorder_i) begin
  61:       rx_data_d = {mosi, rx_data_q[BITS-1:1]};
  62:     end else begin
  63:       rx_data_d = {rx_data_q[BITS-2:0], mosi};
  64:     end
  65:   end
  66: 
  67:   always_ff @(posedge clk_in_i) begin
  68:     rx_data_q <= rx_data_d;
  69:   end
  70: 
  71:   // As SCK shut off right after bytes are transferred,
  72:   // HW should give current MOSI and latched version of rx_data
  73:   // if not, FIFO request should be generated next cycle but it cannot be (as no clock exist)
  74:   // It means RX_FIFO should latch the write request at negedge of clk_in_i
  75:   assign rx_data_o = rx_data_d;
  76: 
  77:   // Counter to generate write signal
  78:   always_ff @(posedge clk_in_i or negedge rst_in_ni) begin
  79:     if (!rst_in_ni) begin
  80:       rx_bitcount <= BITWIDTH'(BITS-1);
  81:     end else begin
  82:       if (rx_bitcount == '0) begin
  83:         rx_bitcount <= BITWIDTH'(BITS-1);
  84:       end else begin
  85:         rx_bitcount <= rx_bitcount -1;
  86:       end
  87:     end
  88:   end
  89: 
  90:   assign rx_wvalid_o = (rx_bitcount == '0);
  91: 
  92:   // TX Serialize
  93:   logic [BITWIDTH-1:0] tx_bitcount;
  94:   logic first_bit, last_bit;
  95:   spi_byte_t miso_shift;
  96: 
  97:   assign first_bit = (tx_bitcount == BITWIDTH'(BITS-1)) ? 1'b1 : 1'b0;
  98:   assign last_bit  = (tx_bitcount == '0) ? 1'b1 : 1'b0;
  99:   // Pop the entry from the FIFO at bit 1.
 100:   //    This let the module pop the entry correctly when CPHA == 1 If CPHA is set, there is no clock
 101:   //    posedge after bitcnt is 0 right before CSb is de-asserted.  So TX Async FIFO pop signal
 102:   //    cannot be latched inside FIFO.  It is safe to pop between bitcnt 6 to 1. If pop signal is
 103:   //    asserted when bitcnt 7 it can pop twice if CPHA is 1.
 104:   assign tx_rready_o = (tx_bitcount == BITWIDTH'(1)); // Pop at second bit transfer
 105:   always_ff @(posedge clk_out_i or negedge rst_out_ni) begin
 106:     if (!rst_out_ni) begin
 107:       tx_bitcount <= BITWIDTH'(BITS-1);
 108:     end else begin
 109:       if (last_bit) begin
 110:         tx_bitcount <= BITWIDTH'(BITS-1);
 111:       end else if (tx_state != TxIdle || cpha_i == 1'b0) begin
 112:         tx_bitcount <= tx_bitcount - 1'b1;
 113:       end
 114:     end
 115:   end
 116:   always_ff @(posedge clk_out_i or negedge rst_out_ni) begin
 117:     if (!rst_out_ni) begin
 118:       tx_state <= TxIdle;
 119:     end else begin
 120:       tx_state <= TxActive;
 121:     end
 122:   end
 123: 
 124:   assign miso = (cfg_txorder_i) ? ((~first_bit) ? miso_shift[0] : tx_data_i[0]) :
 125:                 (~first_bit) ? miso_shift[7] : tx_data_i[7] ;
 126:   assign miso_oe = ~csb_i;
 127: 
 128:   always_ff @(posedge clk_out_i) begin
 129:     if (cfg_txorder_i) begin
 130:       if (first_bit) begin
 131:         miso_shift <= {1'b0, tx_data_i[7:1]};
 132:       end else begin
 133:         miso_shift <= {1'b0, miso_shift[7:1]};
 134:       end
 135:     end else begin
 136:       if (first_bit) begin
 137:         // Dummy byte cannot be used. empty signal could be delayed two clocks to cross
 138:         // async clock domain. It means even FW writes value to FIFO, empty signal deasserts
 139:         // after two negative edge of SCK. HW module already in the middle of sending DUMMY.
 140:         miso_shift <= {tx_data_i[6:0], 1'b0};
 141:       end else begin
 142:         miso_shift <= {miso_shift[6:0], 1'b0};
 143:       end
 144:     end
 145:   end
 146: 
 147:   // Events: rx_overflow, tx_underflow
 148:   //    Reminder: Those events are not 100% accurate. If the event happens at
 149:   //    the end of the transaction right before CSb de-assertion, the event
 150:   //    cannot be propagated to the main clock domain due to the reset and lack
 151:   //    of SCK after CSb de-assertion.
 152:   //
 153:   //    For these events to be propagated to the main clock domain, it needds
 154:   //    one more clock edge to creates toggle signal in the pulse synchronizer.
 155:   assign rx_overflow_o  = rx_wvalid_o & ~rx_wready_i;
 156:   assign tx_underflow_o = tx_rready_o & ~tx_rvalid_i;
 157: 
 158: endmodule
 159: