caravel/verilog/rtl/gpio_control_block.v

289 lines
9.2 KiB
Verilog

// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
`default_nettype none
/*
*---------------------------------------------------------------------
* See gpio_control_block for description. This module is like
* gpio_contro_block except that it has an additional two management-
* Soc-facing pins, which are the out_enb line and the output line.
* If the chip is configured for output with the oeb control
* register = 1, then the oeb line is controlled by the additional
* signal from the management SoC. If the oeb control register = 0,
* then the output is disabled completely. The "io" line is input
* only in this module.
*
*---------------------------------------------------------------------
*/
/*
*---------------------------------------------------------------------
*
* This module instantiates a shift register chain that passes through
* each gpio cell. These are connected end-to-end around the padframe
* periphery. The purpose is to avoid a massive number of control
* wires between the digital core and I/O, passing through the user area.
*
* See mprj_ctrl.v for the module that registers the data for each
* I/O and drives the input to the shift register.
*
* Modified 7/24/2022 by Tim Edwards
* Replaced the data delay with a negative edge-triggered flop
* so that the serial data bit out from the module only changes on
* the clock half cycle. This avoids the need to fine-tune the clock
* skew between GPIO blocks.
*
* Modified 10/05/2022 by Tim Edwards
*
*---------------------------------------------------------------------
*/
module gpio_control_block #(
parameter PAD_CTRL_BITS = 13
) (
`ifdef USE_POWER_PINS
inout vccd,
inout vssd,
inout vccd1,
inout vssd1,
`endif
// Power-on defaults
input [PAD_CTRL_BITS-1:0] gpio_defaults,
// Management Soc-facing signals
input resetn, // Global reset, locally propagated
output resetn_out,
input serial_clock, // Global clock, locally propatated
output serial_clock_out,
input serial_load, // Register load strobe
output serial_load_out,
output mgmt_gpio_in, // Management from pad (input only)
input mgmt_gpio_out, // Management to pad (output only)
input mgmt_gpio_oeb, // Management to pad (output only)
// Serial data chain for pad configuration
input serial_data_in,
output serial_data_out,
// User-facing signals
input user_gpio_out, // User space to pad
input user_gpio_oeb, // Output enable (user)
output user_gpio_in, // Pad to user space
// Pad-facing signals (Pad GPIOv2)
output pad_gpio_holdover,
output pad_gpio_slow_sel,
output pad_gpio_vtrip_sel,
output pad_gpio_inenb,
output pad_gpio_ib_mode_sel,
output pad_gpio_ana_en,
output pad_gpio_ana_sel,
output pad_gpio_ana_pol,
output [2:0] pad_gpio_dm,
output pad_gpio_outenb,
output pad_gpio_out,
input pad_gpio_in,
// to provide a way to automatically disable/enable output
// from the outside with needing a conb cell
output one,
output zero
);
/* Parameters defining the bit offset of each function in the chain */
localparam MGMT_EN = 0;
localparam OEB = 1;
localparam HLDH = 2;
localparam INP_DIS = 3;
localparam MOD_SEL = 4;
localparam AN_EN = 5;
localparam AN_SEL = 6;
localparam AN_POL = 7;
localparam SLOW = 8;
localparam TRIP = 9;
localparam DM = 10;
/* Internally registered signals */
reg mgmt_ena; // Enable management SoC to access pad
reg gpio_holdover;
reg gpio_slow_sel;
reg gpio_vtrip_sel;
reg gpio_inenb;
reg gpio_ib_mode_sel;
reg gpio_outenb;
reg [2:0] gpio_dm;
reg gpio_ana_en;
reg gpio_ana_sel;
reg gpio_ana_pol;
/* Derived output values */
wire pad_gpio_holdover;
wire pad_gpio_slow_sel;
wire pad_gpio_vtrip_sel;
wire pad_gpio_inenb;
wire pad_gpio_ib_mode_sel;
wire pad_gpio_ana_en;
wire pad_gpio_ana_sel;
wire pad_gpio_ana_pol;
wire [2:0] pad_gpio_dm;
wire pad_gpio_outenb;
wire pad_gpio_out;
wire pad_gpio_in;
wire one_unbuf;
wire zero_unbuf;
wire one;
wire zero;
wire user_gpio_in;
wire gpio_logic1;
reg serial_data_out;
/* Serial shift for the above (latched) values */
reg [PAD_CTRL_BITS-1:0] shift_register;
/* Latch the output on the clock negative edge */
always @(negedge serial_clock or negedge resetn) begin
if (resetn == 1'b0) begin
/* Clear the shift register output */
serial_data_out <= 1'b0;
end else begin
serial_data_out <= shift_register[PAD_CTRL_BITS-1];
end
end
/* Propagate the clock and reset signals so that they aren't wired */
/* all over the chip, but are just wired between the blocks. */
assign serial_clock_out = serial_clock;
assign resetn_out = resetn;
assign serial_load_out = serial_load;
always @(posedge serial_clock or negedge resetn) begin
if (resetn == 1'b0) begin
/* Clear shift register */
shift_register <= 'd0;
end else begin
/* Shift data in */
shift_register <= {shift_register[PAD_CTRL_BITS-2:0], serial_data_in};
end
end
always @(posedge serial_load or negedge resetn) begin
if (resetn == 1'b0) begin
/* Initial state on reset depends on applied defaults */
mgmt_ena <= gpio_defaults[MGMT_EN];
gpio_holdover <= gpio_defaults[HLDH];
gpio_slow_sel <= gpio_defaults[SLOW];
gpio_vtrip_sel <= gpio_defaults[TRIP];
gpio_ib_mode_sel <= gpio_defaults[MOD_SEL];
gpio_inenb <= gpio_defaults[INP_DIS];
gpio_outenb <= gpio_defaults[OEB];
gpio_dm <= gpio_defaults[DM+2:DM];
gpio_ana_en <= gpio_defaults[AN_EN];
gpio_ana_sel <= gpio_defaults[AN_SEL];
gpio_ana_pol <= gpio_defaults[AN_POL];
end else begin
/* Load data */
mgmt_ena <= shift_register[MGMT_EN];
gpio_outenb <= shift_register[OEB];
gpio_holdover <= shift_register[HLDH];
gpio_inenb <= shift_register[INP_DIS];
gpio_ib_mode_sel <= shift_register[MOD_SEL];
gpio_ana_en <= shift_register[AN_EN];
gpio_ana_sel <= shift_register[AN_SEL];
gpio_ana_pol <= shift_register[AN_POL];
gpio_slow_sel <= shift_register[SLOW];
gpio_vtrip_sel <= shift_register[TRIP];
gpio_dm <= shift_register[DM+2:DM];
end
end
/* These pad configuration signals are static and do not change */
/* after setup. */
assign pad_gpio_holdover = gpio_holdover;
assign pad_gpio_slow_sel = gpio_slow_sel;
assign pad_gpio_vtrip_sel = gpio_vtrip_sel;
assign pad_gpio_ib_mode_sel = gpio_ib_mode_sel;
assign pad_gpio_ana_en = gpio_ana_en;
assign pad_gpio_ana_sel = gpio_ana_sel;
assign pad_gpio_ana_pol = gpio_ana_pol;
assign pad_gpio_dm = gpio_dm;
assign pad_gpio_inenb = gpio_inenb;
/* Implement pad control behavior depending on state of mgmt_ena */
/* The pad value always goes back to the housekeeping module */
assign mgmt_gpio_in = pad_gpio_in;
/* For 2-wire interfaces, the mgmt_gpio_oeb line is tied high at */
/* the control block. In this case, the output enable state is */
/* determined by the OEB configuration bit. */
assign pad_gpio_outenb = (mgmt_ena) ? ((mgmt_gpio_oeb == 1'b1) ?
gpio_outenb : 1'b0) : user_gpio_oeb;
/* For 2-wire interfaces, if the pad is configured for pull-up or */
/* pull-down, drive the output value locally to achieve the */
/* expected pull. */
assign pad_gpio_out = (mgmt_ena) ? ((mgmt_gpio_oeb == 1'b1) ?
((gpio_dm[2:1] == 2'b01) ? ~gpio_dm[0] : mgmt_gpio_out) :
mgmt_gpio_out) : user_gpio_out;
/* Buffer user_gpio_in with an enable that is set by the user domain vccd */
gpio_logic_high gpio_logic_high (
`ifdef USE_POWER_PINS
.vccd1(vccd1),
.vssd1(vssd1),
`endif
.gpio_logic1(gpio_logic1)
);
/* If user project area is powered down, zero the pad input value */
/* going to the user project. */
assign user_gpio_in = pad_gpio_in & gpio_logic1;
(* keep *)
sky130_fd_sc_hd__macro_sparecell spare_cell (
`ifdef USE_POWER_PINS
.VPWR(vccd),
.VGND(vssd),
.VPB(vccd),
.VNB(vssd)
`endif
);
sky130_fd_sc_hd__conb_1 const_source (
`ifdef USE_POWER_PINS
.VPWR(vccd),
.VGND(vssd),
.VPB(vccd),
.VNB(vssd),
`endif
.HI(one_unbuf),
.LO(zero_unbuf)
);
assign zero = zero_unbuf;
assign one = one_unbuf;
endmodule
`default_nettype wire