blob: 2325b39ea1cc942c6a8f48ddb4cc30ebd299627c [file] [log] [blame]
/*
*---------------------------------------------------------------------
* This block interfaces between the sky130_fd_io GPIOv2 pad, the
* caravel management area, and the user project area.
*
* The management area has ultimate control over the setting of the
* pad, and can modify all core-voltage inputs to the pad and monitor
* the output.
*
* The user project will rely on the management SoC startup program
* to configure each I/O pad to a fixed configuration except for
* the output enable which remains under user control.
*
* This module is bit-sliced. Instantiate once for each GPIO pad.
*
*---------------------------------------------------------------------
*/
/*
*---------------------------------------------------------------------
*
* 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.
*
*---------------------------------------------------------------------
*/
module gpio_control_block #(
parameter PAD_CTRL_BITS = 13,
// Parameterized initial startup state of the pad.
// The default parameters if unspecified is for the pad to be
// an input with no pull-up or pull-down, so that it is disconnected
// from the outside world.
parameter HOLD_INIT = 1'b0,
parameter SLOW_INIT = 1'b0,
parameter TRIP_INIT = 1'b0,
parameter IB_INIT = 1'b0,
parameter IENB_INIT = 1'b0,
parameter OENB_INIT = 1'b1,
parameter DM_INIT = 3'b001,
parameter AENA_INIT = 1'b0,
parameter ASEL_INIT = 1'b0,
parameter APOL_INIT = 1'b0
) (
// Management Soc-facing signals
input resetn, // Global reset
input serial_clock,
inout mgmt_gpio_io, // Management to/from pad
// 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_oeb,
output pad_gpio_out,
input pad_gpio_in
);
/* Parameters defining the bit offset of each function in the chain */
localparam MGMT_EN = 0;
localparam OEB = 1;
localparam HLDH = 2;
localparam INP_DIS = 2;
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;
/* Serial shift for the above (latched) values */
reg [PAD_CTRL_BITS-1:0] shift_register;
/* Utilize reset and clock to encode a load operation */
wire load_data;
wire int_reset;
/* Create internal reset and load signals from input reset and clock */
assign serial_data_out = shift_register[PAD_CTRL_BITS-1];
assign int_reset = (~resetn) & (~serial_clock);
assign load_data = (~resetn) & serial_clock;
always @(posedge serial_clock or posedge int_reset) begin
if (int_reset == 1'b1) 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 load_data or posedge int_reset) begin
if (int_reset == 1'b1) begin
/* Initial state on reset: Pad set to management input */
mgmt_ena <= 1'b1; // Management SoC has control over all I/O
gpio_holdover <= HOLD_INIT; // All signals latched in hold mode
gpio_slow_sel <= SLOW_INIT; // Fast slew rate
gpio_vtrip_sel <= TRIP_INIT; // CMOS mode
gpio_ib_mode_sel <= IB_INIT; // CMOS mode
gpio_inenb <= IENB_INIT; // Input enabled
gpio_outenb <= OENB_INIT; // Output disabled
gpio_dm <= DM_INIT; // Configured as input only
gpio_ana_en <= AENA_INIT; // Digital enabled
gpio_ana_sel <= ASEL_INIT; // Don't-care when gpio_ana_en = 0
gpio_ana_pol <= APOL_INIT; // Don't-care when gpio_ana_en = 0
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 */
/* If pad is configured for input and dm[2:1] is 01, then pad is */
/* configured as pull-up or pull-down depending on dm[0], and to */
/* set the pullup or pulldown condition, the pad output bit must */
/* be set to the opposite state of dm[0]. */
/* dm[0] = 0 is pull-down; dm[0] = 1 is pull-up. */
/* */
/* This is done for management control only, since the management */
/* control has only 1 inout signal for both input and output, and */
/* cannot set the output and read the input simultaneously. The */
/* user has control over both lines and only needs to be told what */
/* to set the output line to, to enable a pullup or pulldown. */
assign pad_gpio_out = (mgmt_ena) ?
(((gpio_dm[2:1] == 2'b01) && (gpio_inenb == 1'b0)) ?
~gpio_dm[0] : mgmt_gpio_io) : user_gpio_out;
/* When under user control, gpio_outenb = 1 means that the pad is */
/* configured as input, and the user outenb is unused. Otherwise, */
/* the pad outenb signal is controlled by the user. */
assign pad_gpio_outenb = (mgmt_ena) ? gpio_outenb :
((gpio_outenb == 1) ? 1'b1 : user_gpio_oeb);
/* User gpio_in is grounded when the management controls the pad */
assign user_gpio_in = (mgmt_ena) ? 1'b0 : pad_gpio_in;
/* Management I/O line is set from the pad when the pad is */
/* configured for input. */
assign mgmt_gpio_io = (gpio_inenb == 0) ? pad_gpio_in : 1'bz;
endmodule