Serial chain loading of the I/O configurations is now working.
diff --git a/verilog/rtl/caravel.v b/verilog/rtl/caravel.v
index 492712e..cdefe74 100644
--- a/verilog/rtl/caravel.v
+++ b/verilog/rtl/caravel.v
@@ -47,7 +47,6 @@
`include "chip_io.v"
`include "user_id_programming.v"
`include "gpio_control_block.v"
-`include "gpio_control_block2.v"
`ifdef USE_OPENRAM
`include "sram_1rw1r_32_8192_8_sky130.v"
@@ -368,16 +367,15 @@
wire [`MPRJ_IO_PADS-1:0] gpio_serial_link_shifted;
- assign gpio_serial_link_shifted = {mprj_io_loader_data, gpio_serial_link[`MPRJ_IO_PADS-1:1]};
+ assign gpio_serial_link_shifted = {gpio_serial_link[`MPRJ_IO_PADS-2:0], mprj_io_loader_data};
- // NOTE: The intention is to replace most of gpio_control_block2
- // (3 management wires per pad) with gpio_control_block (1 management
- // wire per pad). However, the inout line on gpio_control_block is
- // troublesome and so I am starting with the simpler interface. Ultimately
- // the JTAG and SDO lines will keep the 3-pin interface and these pads will
- // be located closest to the management area.
+ // Each control block sits next to an I/O pad in the user area.
+ // It gets input through a serial chain from the previous control
+ // block and passes it to the next control block. Due to the nature
+ // of the shift register, bits are presented in reverse, as the first
+ // bit in ends up as the last bit of the last I/O pad control block.
- gpio_control_block2 #(
+ gpio_control_block #(
.DM_INIT(3'b010), // Test: All pads set to pull-up
.OENB_INIT(1'b0) // Test: All pads set to pull-up
) gpio_control_inst [`MPRJ_IO_PADS-1:0] (
@@ -387,7 +385,7 @@
.resetn(mprj_io_loader_resetn),
.serial_clock(mprj_io_loader_clock),
- .mgmt_gpio_in(mgmt_io_in), // For gpio_control_block2 only
+ .mgmt_gpio_in(mgmt_io_in),
.mgmt_gpio_out({mgmt_io_in[(`MPRJ_IO_PADS-1):2], sdo_out, jtag_out}),
.mgmt_gpio_oeb({{(`MPRJ_IO_PADS-2){1'b1}}, sdo_outenb, jtag_outenb}),
diff --git a/verilog/rtl/gpio_control_block.v b/verilog/rtl/gpio_control_block.v
index 2325b39..5d3adb1 100644
--- a/verilog/rtl/gpio_control_block.v
+++ b/verilog/rtl/gpio_control_block.v
@@ -1,17 +1,13 @@
/*
*---------------------------------------------------------------------
- * 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.
+ * 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.
*
*---------------------------------------------------------------------
*/
@@ -51,7 +47,9 @@
input resetn, // Global reset
input serial_clock,
- inout mgmt_gpio_io, // Management to/from pad
+ 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,
@@ -72,7 +70,7 @@
output pad_gpio_ana_sel,
output pad_gpio_ana_pol,
output [2:0] pad_gpio_dm,
- output pad_gpio_oeb,
+ output pad_gpio_outenb,
output pad_gpio_out,
input pad_gpio_in
);
@@ -81,7 +79,7 @@
localparam MGMT_EN = 0;
localparam OEB = 1;
localparam HLDH = 2;
- localparam INP_DIS = 2;
+ localparam INP_DIS = 3;
localparam MOD_SEL = 4;
localparam AN_EN = 5;
localparam AN_SEL = 6;
@@ -148,7 +146,7 @@
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_outenb <= OENB_INIT; // (unused placeholder)
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
@@ -185,34 +183,10 @@
/* 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) ? mgmt_gpio_out : user_gpio_out;
+ assign pad_gpio_outenb = (mgmt_ena) ? mgmt_gpio_oeb : user_gpio_oeb;
- 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;
+ assign user_gpio_in = (mgmt_ena) ? 1'b0 : pad_gpio_in;
+ assign mgmt_gpio_in = (mgmt_ena) ? pad_gpio_in : 1'b0;
endmodule
diff --git a/verilog/rtl/gpio_control_block2.v b/verilog/rtl/gpio_control_block2.v
deleted file mode 100644
index b28a02e..0000000
--- a/verilog/rtl/gpio_control_block2.v
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- *---------------------------------------------------------------------
- * 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.
- *
- *---------------------------------------------------------------------
- */
-
-module gpio_control_block2 #(
- 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,
-
- 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
-);
-
- /* Parameters defining the bit offset of each function in the chain */
- localparam MGMT_EN = 0;
- localparam OEB = 1;
- localparam HLDH = 2;
- localparam ENH = 3;
- localparam INP_DIS = 4;
- localparam MOD_SEL = 5;
- localparam AN_EN = 6;
- localparam AN_SEL = 7;
- localparam AN_POL = 8;
- localparam SLOW = 9;
- localparam TRIP = 10;
- localparam DM = 11;
-
- /* 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; // (unused placeholder)
- 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 */
-
- assign pad_gpio_out = (mgmt_ena) ? mgmt_gpio_out : user_gpio_out;
- assign pad_gpio_outenb = (mgmt_ena) ? mgmt_gpio_oeb : user_gpio_oeb;
-
- assign user_gpio_in = (mgmt_ena) ? 1'b0 : pad_gpio_in;
- assign mgmt_gpio_in = (mgmt_ena) ? pad_gpio_in : 1'b0;
-
-endmodule
diff --git a/verilog/rtl/mprj_ctrl.v b/verilog/rtl/mprj_ctrl.v
index cb67ac4..c002610 100644
--- a/verilog/rtl/mprj_ctrl.v
+++ b/verilog/rtl/mprj_ctrl.v
@@ -93,7 +93,6 @@
output serial_clock,
output serial_resetn,
output serial_data_out,
- // inout [IO_PADS-1:0] mgmt_gpio_io
input [IO_PADS-1:0] mgmt_gpio_in,
output [IO_PADS-1:0] mgmt_gpio_out,
output [IO_PADS-1:0] mgmt_gpio_outz,
@@ -170,9 +169,11 @@
end
end else if (xfer_sel) begin
- iomem_rdata <= {31'd0, xfer_ctrl};
- if (iomem_wstrb[0]) xfer_ctrl <= iomem_wdata[1:0];
+ iomem_rdata <= {31'd0, busy};
+ if (iomem_wstrb[0]) xfer_ctrl <= iomem_wdata[0];
end
+ end else begin
+ xfer_ctrl <= 1'b0; // Immediately self-resetting
end
end
end
@@ -181,12 +182,14 @@
for (i=0; i<IO_PADS; i=i+1) begin
always @(posedge clk) begin
if (!resetn) begin
- // NOTE: This needs to be set to the specific bit sequence
- // that initializes every I/O pad to the appropriate state on
- // startup, to match the startup state at each pad. Otherwise
- // surprises happen if not all pad configurations are applied
- // from the program code, and a transfer is initiated.
- io_ctrl[i] <= 'd0;
+ // NOTE: This initialization must match the defaults passed
+ // to the control blocks. Specifically, 0x1801 is for a
+ // bidirectional pad, and 0x0403 is for a simple input pad
+ if (i < 2) begin
+ io_ctrl[i] <= 'h1801;
+ end else begin
+ io_ctrl[i] <= 'h0403;
+ end
end else begin
if (iomem_valid && !iomem_ready && iomem_addr[31:8] == BASE_ADR[31:8]) begin
if (io_ctrl_sel[i]) begin
@@ -224,7 +227,6 @@
reg [1:0] xfer_state;
reg serial_clock;
reg serial_resetn;
- reg serial_data_out;
// NOTE: Ignoring power control bits for now. . . need to revisit.
// Depends on how the power pads are arranged among the GPIO, and
@@ -233,20 +235,26 @@
reg [IO_CTRL_BITS-1:0] serial_data_staging;
+ wire serial_data_out;
+ wire busy;
+
+ assign serial_data_out = serial_data_staging[IO_CTRL_BITS-1];
+ assign busy = (xfer_state != `IDLE);
+
always @(posedge clk or negedge resetn) begin
if (resetn == 1'b0) begin
xfer_state <= `IDLE;
xfer_count <= 4'd0;
- pad_count <= 6'd0;
+ pad_count <= IO_PADS;
serial_resetn <= 1'b0;
serial_clock <= 1'b0;
- serial_data_out <= 1'b0;
end else begin
if (xfer_state == `IDLE) begin
- serial_resetn <= 1'b0;
+ pad_count <= IO_PADS;
+ serial_resetn <= 1'b1;
serial_clock <= 1'b0;
if (xfer_ctrl == 1'b1) begin
xfer_state <= `START;
@@ -255,25 +263,24 @@
serial_resetn <= 1'b1;
serial_clock <= 1'b0;
xfer_count <= 6'd0;
- if (pad_count == IO_PADS) begin
- xfer_state <= `LOAD;
- end else begin
- pad_count <= pad_count + 1;
- xfer_state <= `XBYTE;
- serial_data_staging <= io_ctrl[pad_count];
- end
+ pad_count <= pad_count - 1;
+ xfer_state <= `XBYTE;
+ serial_data_staging <= io_ctrl[pad_count - 1];
end else if (xfer_state == `XBYTE) begin
serial_resetn <= 1'b1;
serial_clock <= ~serial_clock;
if (serial_clock == 1'b0) begin
- if (xfer_count == IO_CTRL_BITS) begin
- xfer_state <= `START;
+ if (xfer_count == IO_CTRL_BITS - 1) begin
+ if (pad_count == 0) begin
+ xfer_state <= `LOAD;
+ end else begin
+ xfer_state <= `START;
+ end
end else begin
xfer_count <= xfer_count + 1;
end
end else begin
serial_data_staging <= {serial_data_staging[IO_CTRL_BITS-2:0], 1'b0};
- serial_data_out <= serial_data_staging[IO_CTRL_BITS-1];
end
end else if (xfer_state == `LOAD) begin
xfer_count <= xfer_count + 1;