../src/lowrisc_ip_pinmux_component_0.1/rtl/pinmux.sv Cov: 94%

   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: // Pinmux toplevel.
   6: //
   7: 
   8: `include "prim_assert.sv"
   9: 
  10: module pinmux import pinmux_pkg::*; import pinmux_reg_pkg::*; (
  11:   input                            clk_i,
  12:   input                            rst_ni,
  13:   // Slow always-on clock
  14:   input                            clk_aon_i,
  15:   input                            rst_aon_ni,
  16:   // Wakeup request, running on clk_aon_i
  17:   output logic                     aon_wkup_req_o,
  18:   // Sleep enable, running on clk_i
  19:   input                            sleep_en_i,
  20:   // Strap sample request
  21:   input  lc_strap_req_t            lc_pinmux_strap_i,
  22:   output lc_strap_rsp_t            lc_pinmux_strap_o,
  23:   // Bus Interface (device)
  24:   input  tlul_pkg::tl_h2d_t        tl_i,
  25:   output tlul_pkg::tl_d2h_t        tl_o,
  26:   // Muxed Peripheral side
  27:   input        [NMioPeriphOut-1:0] periph_to_mio_i,
  28:   input        [NMioPeriphOut-1:0] periph_to_mio_oe_i,
  29:   output logic [NMioPeriphIn-1:0]  mio_to_periph_o,
  30:   // Dedicated Peripheral side
  31:   input        [NDioPads-1:0]      periph_to_dio_i,
  32:   input        [NDioPads-1:0]      periph_to_dio_oe_i,
  33:   output logic [NDioPads-1:0]      dio_to_periph_o,
  34:   // Pad side
  35:   // MIOs
  36:   output logic [NMioPads-1:0]      mio_out_o,
  37:   output logic [NMioPads-1:0]      mio_oe_o,
  38:   input        [NMioPads-1:0]      mio_in_i,
  39:   // DIOs
  40:   output logic [NDioPads-1:0]      dio_out_o,
  41:   output logic [NDioPads-1:0]      dio_oe_o,
  42:   input        [NDioPads-1:0]      dio_in_i
  43: );
  44: 
  45:   ////////////////////////////
  46:   // Parameters / Constants //
  47:   ////////////////////////////
  48: 
  49:   // TODO: these need to be parameterizable via topgen at some point.
  50:   // They have been placed here such that they do not generate
  51:   // warnings in the C header generation step, since logic is not supported
  52:   // as a data type yet.
  53:   localparam logic [pinmux_reg_pkg::NMioPeriphOut-1:0] MioPeriphHasSleepMode
  54:                    = {pinmux_reg_pkg::NMioPeriphOut{1'b1}};
  55:   localparam logic [pinmux_reg_pkg::NDioPads-1:0]      DioPeriphHasSleepMode
  56:                    = {pinmux_reg_pkg::NDioPads{1'b1}};
  57:   localparam logic [pinmux_reg_pkg::NDioPads-1:0]      DioPeriphHasWkup
  58:                    = {pinmux_reg_pkg::NDioPads{1'b1}};
  59: 
  60:   //////////////////////////////////
  61:   // Regfile Breakout and Mapping //
  62:   //////////////////////////////////
  63: 
  64:   pinmux_reg2hw_t reg2hw;
  65:   pinmux_hw2reg_t hw2reg;
  66: 
  67:   pinmux_reg_top i_reg_top (
  68:     .clk_i  ,
  69:     .rst_ni ,
  70:     .tl_i   ,
  71:     .tl_o   ,
  72:     .reg2hw ,
  73:     .hw2reg ,
  74:     .devmode_i(1'b1)
  75:   );
  76: 
  77:   /////////////////////
  78:   // Sleep registers //
  79:   /////////////////////
  80: 
  81:   logic sleep_en_q;
  82:   logic [NMioPads-1:0] mio_out_sleep_d, mio_oe_sleep_d;
  83:   logic [NMioPads-1:0] mio_out_sleep_q, mio_oe_sleep_q;
  84:   logic [NDioPads-1:0] dio_out_sleep_d, dio_oe_sleep_d;
  85:   logic [NDioPads-1:0] dio_out_sleep_q, dio_oe_sleep_q;
  86:   // these are external due to their WARL behavior
  87:   logic [NDioPads-1:0][1:0] dio_out_sleep_val_d, dio_out_sleep_val_q;
  88: 
  89:   // latch MIO/DIO state when going to sleep
  90:   // 0: drive low
  91:   // 1: drive high
  92:   // 2: high-z
  93:   // 3: previous value
  94:   for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_sleep
  95:     assign mio_out_sleep_d[k] = (reg2hw.mio_out_sleep_val[k].q == 0) ? 1'b0 :
  96:                                 (reg2hw.mio_out_sleep_val[k].q == 1) ? 1'b1 :
  97:                                 (reg2hw.mio_out_sleep_val[k].q == 2) ? 1'b0 : mio_out_o[k];
  98: 
  99:     assign mio_oe_sleep_d[k] = (reg2hw.mio_out_sleep_val[k].q == 0) ? 1'b1 :
 100:                                (reg2hw.mio_out_sleep_val[k].q == 1) ? 1'b1 :
 101:                                (reg2hw.mio_out_sleep_val[k].q == 2) ? 1'b0 : mio_oe_o[k];
 102:   end
 103: 
 104:   // since DIO pads are permanently mapped to a specific peripheral,
 105:   // we only need to support retention regs on non-always on peripherals,
 106:   // outputs / inouts.
 107:   for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_sleep
 108:     if (DioPeriphHasSleepMode[k]) begin : gen_warl_connect
 109:       assign hw2reg.dio_out_sleep_val[k].d = dio_out_sleep_val_q[k];
 110: 
 111:       assign dio_out_sleep_val_d[k] = (reg2hw.dio_out_sleep_val[k].qe) ?
 112:                                       reg2hw.dio_out_sleep_val[k].q :
 113:                                       dio_out_sleep_val_q[k];
 114: 
 115:       assign dio_out_sleep_d[k] = (dio_out_sleep_val_q[k] == 0) ? 1'b0 :
 116:                                   (dio_out_sleep_val_q[k] == 1) ? 1'b1 :
 117:                                   (dio_out_sleep_val_q[k] == 2) ? 1'b0 : dio_out_o[k];
 118: 
 119:       assign dio_oe_sleep_d[k] = (dio_out_sleep_val_q[k] == 0) ? 1'b1 :
 120:                                  (dio_out_sleep_val_q[k] == 1) ? 1'b1 :
 121:                                  (dio_out_sleep_val_q[k] == 2) ? 1'b0 : dio_oe_o[k];
 122:     end else begin : gen_warl_tie0
 123:       // these signals will be unused
 124:       assign hw2reg.dio_out_sleep_val[k].d = 2'b10; // default value defined in hjson
 125:       assign dio_out_sleep_val_d[k] = 2'b10; // default value defined in hjson
 126:       assign dio_out_sleep_d[k]     = '0;
 127:       assign dio_oe_sleep_d[k]      = '0;
 128:     end
 129:   end
 130: 
 131:   always_ff @(posedge clk_i or negedge rst_ni) begin : p_sleep
 132:     if (!rst_ni) begin
 133:       sleep_en_q          <= 1'b0;
 134:       dio_out_sleep_val_q <= {NDioPads{2'b10}}; // default value defined in hjson
 135:       mio_out_sleep_q     <= '0;
 136:       mio_oe_sleep_q      <= '0;
 137:       dio_out_sleep_q     <= '0;
 138:       dio_oe_sleep_q      <= '0;
 139:     end else begin
 140:       sleep_en_q          <= sleep_en_i;
 141:       dio_out_sleep_val_q <= dio_out_sleep_val_d;
 142: 
 143:       if (sleep_en_i & !sleep_en_q) begin
 144:         mio_out_sleep_q <= mio_out_sleep_d;
 145:         mio_oe_sleep_q  <= mio_oe_sleep_d;
 146:         dio_out_sleep_q <= dio_out_sleep_d;
 147:         dio_oe_sleep_q  <= dio_oe_sleep_d;
 148:       end
 149:     end
 150:   end
 151: 
 152:   ///////////////
 153:   // Input Mux //
 154:   ///////////////
 155: 
 156:   for (genvar k = 0; k < NMioPeriphIn; k++) begin : gen_mio_periph_in
 157:     logic [2**$clog2(NMioPads+2)-1:0] data_mux;
 158:     // stack input and default signals for convenient indexing below
 159:     // possible defaults: constant 0 or 1
 160:     assign data_mux = $bits(data_mux)'({mio_in_i, 1'b1, 1'b0});
 161:     // index using configured insel
 162:     assign mio_to_periph_o[k] = data_mux[reg2hw.periph_insel[k].q];
 163:   end
 164: 
 165:   ////////////////
 166:   // Output Mux //
 167:   ////////////////
 168: 
 169:   for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_out
 170:     logic sleep_en;
 171:     logic [2**$clog2(NMioPeriphOut+3)-1:0] data_mux, oe_mux, sleep_mux;
 172:     // stack output data/enable and default signals for convenient indexing below
 173:     // possible defaults: 0, 1 or 2 (high-Z)
 174:     assign data_mux  = $bits(data_mux)'({periph_to_mio_i, 1'b0, 1'b1, 1'b0});
 175:     assign oe_mux    = $bits(oe_mux)'({periph_to_mio_oe_i,  1'b0, 1'b1, 1'b1});
 176:     assign sleep_mux = $bits(sleep_mux)'({MioPeriphHasSleepMode,  1'b1, 1'b1, 1'b1});
 177: 
 178:     // check whether this peripheral can actually go to sleep
 179:     assign sleep_en = sleep_mux[reg2hw.mio_outsel[k].q] & sleep_en_q;
 180:     // index using configured outsel
 181:     assign mio_out_o[k] = (sleep_en) ? mio_out_sleep_q[k] : data_mux[reg2hw.mio_outsel[k].q];
 182:     assign mio_oe_o[k]  = (sleep_en) ? mio_oe_sleep_q[k]  : oe_mux[reg2hw.mio_outsel[k].q];
 183:   end
 184: 
 185:   /////////////////////
 186:   // DIO connections //
 187:   /////////////////////
 188: 
 189:   // Inputs are just fed through
 190:   assign dio_to_periph_o = dio_in_i;
 191: 
 192:   for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_out
 193:     // Since this is a DIO, this can be determined at design time
 194:     if (DioPeriphHasSleepMode[k]) begin : gen_sleep
 195:       assign dio_out_o[k] = (sleep_en_q) ? dio_out_sleep_q[k] : periph_to_dio_i[k];
 196:       assign dio_oe_o[k]  = (sleep_en_q) ? dio_oe_sleep_q[k]  : periph_to_dio_oe_i[k];
 197:     end else begin : gen_nosleep
 198:       assign dio_out_o[k] = periph_to_dio_i[k];
 199:       assign dio_oe_o[k]  = periph_to_dio_oe_i[k];
 200:     end
 201:   end
 202: 
 203:   //////////////////////
 204:   // Wakeup detectors //
 205:   //////////////////////
 206: 
 207:   localparam int AlignedMuxSize = (NMioPads + 2 > NDioPads) ? 2**$clog2(NMioPads + 2) :
 208:                                                               2**$clog2(NDioPads);
 209:   logic [NWkupDetect-1:0] aon_wkup_req;
 210:   logic [AlignedMuxSize-1:0] dio_data_mux, mio_data_mux;
 211:   assign mio_data_mux = AlignedMuxSize'({mio_in_i, 1'b0, 1'b0});
 212: 
 213:   // Only connect DIOs that are not excempt
 214:   for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_wkup
 215:     if (DioPeriphHasWkup[k]) begin : gen_dio_wkup_connect
 216:       assign dio_data_mux[k] = dio_in_i[k];
 217:     end else begin : gen_dio_wkup_tie_off
 218:       assign dio_data_mux[k] = 1'b0;
 219:     end
 220:   end
 221: 
 222:   for (genvar k = NDioPads; k < AlignedMuxSize; k++) begin : gen_dio_data_mux_tie_off
 223:       assign dio_data_mux[k] = 1'b0;
 224:   end
 225: 
 226:   for (genvar k = 0; k < NWkupDetect; k++) begin : gen_wkup_detect
 227:     logic pin_value;
 228:     assign pin_value = (reg2hw.wkup_detector[k].miodio.q)           ?
 229:                        dio_data_mux[reg2hw.wkup_detector_padsel[k]] :
 230:                        mio_data_mux[reg2hw.wkup_detector_padsel[k]];
 231: 
 232:     pinmux_wkup i_pinmux_wkup (
 233:       .clk_i,
 234:       .rst_ni,
 235:       .clk_aon_i,
 236:       .rst_aon_ni,
 237:       // config signals. these are synched to clk_aon internally
 238:       .wkup_en_i          ( reg2hw.wkup_detector_en[k].q                ),
 239:       .filter_en_i        ( reg2hw.wkup_detector[k].filter.q            ),
 240:       .wkup_mode_i        ( wkup_mode_e'(reg2hw.wkup_detector[k].mode.q)),
 241:       .wkup_cnt_th_i      ( reg2hw.wkup_detector_cnt_th[k].q            ),
 242:       .pin_value_i        ( pin_value                                   ),
 243:       // cause reg signals. these are synched from/to clk_aon internally
 244:       .wkup_cause_valid_i ( reg2hw.wkup_cause[k].qe                     ),
 245:       .wkup_cause_data_i  ( reg2hw.wkup_cause[k].q                      ),
 246:       .wkup_cause_data_o  ( hw2reg.wkup_cause[k].d                      ),
 247:       // wakeup request signals on clk_aon (level encoded)
 248:       .aon_wkup_req_o     ( aon_wkup_req[k]                             )
 249:     );
 250:   end
 251: 
 252:   // OR' together all wakeup requests
 253:   assign aon_wkup_req_o = |aon_wkup_req;
 254: 
 255:   ////////////////////
 256:   // Strap Sampling //
 257:   ////////////////////
 258: 
 259:   logic [NStraps-1:0] lc_strap_taps;
 260:   lc_strap_rsp_t lc_strap_d, lc_strap_q;
 261: 
 262:   for (genvar k = 0; k < NStraps; k++) begin : gen_strap_taps
 263:     assign lc_strap_taps[k] = mio_in_i[MioStrapPos[k]];
 264:   end
 265: 
 266:   assign lc_pinmux_strap_o = lc_strap_q;
 267:   assign lc_strap_d = (lc_pinmux_strap_i.sample_pulse)      ?
 268:                       '{valid: 1'b1, straps: lc_strap_taps} :
 269:                       lc_strap_q;
 270: 
 271:   always_ff @(posedge clk_i or negedge rst_ni) begin : p_strap_sample
 272:     if (!rst_ni) begin
 273:       lc_strap_q <= '0;
 274:     end else begin
 275:       lc_strap_q <= lc_strap_d;
 276:     end
 277:   end
 278: 
 279:   ////////////////
 280:   // Assertions //
 281:   ////////////////
 282: 
 283:   `ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid)
 284:   `ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready)
 285:   // `ASSERT_KNOWN(MioToPeriphKnownO_A, mio_to_periph_o)
 286:   `ASSERT_KNOWN(MioOeKnownO_A, mio_oe_o)
 287:   // `ASSERT_KNOWN(DioToPeriphKnownO_A, dio_to_periph_o)
 288:   `ASSERT_KNOWN(DioOeKnownO_A, dio_oe_o)
 289:   `ASSERT_KNOWN(LcPinmuxStrapKnownO_A, lc_pinmux_strap_o)
 290: 
 291:   // TODO: need to check why some outputs are not valid (e.g. SPI device MISO)
 292:   // for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_known_if
 293:   //   `ASSERT_KNOWN_IF(MioOutKnownO_A, mio_out_o[k], mio_oe_o[k])
 294:   // end
 295: 
 296:   // for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_known_if
 297:   //   `ASSERT_KNOWN_IF(DioOutKnownO_A, dio_out_o[k], dio_oe_o[k])
 298:   // end
 299: 
 300:   // running on slow AON clock
 301:   `ASSERT_KNOWN(AonWkupReqKnownO_A, aon_wkup_req_o, !rst_aon_ni, clk_aon_i)
 302: 
 303: endmodule : pinmux
 304: