[pad_wrapper] Extend the generic and Xilinx pad wrapper models

Signed-off-by: Michael Schaffner <msf@google.com>
diff --git a/hw/ip/prim/rtl/prim_pad_wrapper_pkg.sv b/hw/ip/prim/rtl/prim_pad_wrapper_pkg.sv
index 7b16642..08057ac 100644
--- a/hw/ip/prim/rtl/prim_pad_wrapper_pkg.sv
+++ b/hw/ip/prim/rtl/prim_pad_wrapper_pkg.sv
@@ -4,11 +4,12 @@
 
 package prim_pad_wrapper_pkg;
 
-  typedef enum logic [1:0] {
-    BidirStd = 2'h0, // Standard bidirectional pad
-    BidirTol = 2'h1, // Voltage tolerant pad
-    BidirOd = 2'h2,  // Open-drain capable pad
-    InputStd = 2'h3 // Input-only pad
+  typedef enum logic [2:0] {
+    BidirStd = 3'h0, // Standard bidirectional pad
+    BidirTol = 3'h1, // Voltage tolerant pad
+    BidirOd = 3'h2,  // Open-drain capable pad
+    InputStd = 3'h3, // Input-only pad
+    AnalogIn0 = 3'h4 // Analog input pad
   } pad_type_e;
 
   typedef enum logic [1:0] {
diff --git a/hw/ip/prim_generic/rtl/prim_generic_pad_attr.sv b/hw/ip/prim_generic/rtl/prim_generic_pad_attr.sv
index 23ef717..3d14b75 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_pad_attr.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_pad_attr.sv
@@ -15,6 +15,13 @@
 
   // Currently supported pad attributes of the generic pad library:
   //
+  // Input-only:
+  //
+  // - inversion
+  // - pullup / pulldown
+  //
+  // Bidirectional:
+  //
   // - inversion
   // - virtual open drain
   // - keeper
@@ -22,18 +29,42 @@
   // - 1 driving strength bit
   //
   // Note that the last three attributes are not supported on Verilator.
-  always_comb begin : p_attr
-    attr_warl_o = '0;
-    attr_warl_o.invert = 1'b1;
-    attr_warl_o.virt_od_en = 1'b1;
-    attr_warl_o.keep_en = 1'b1;
-// Driving strength and pulls are not supported by Verilator
-`ifndef VERILATOR
-    attr_warl_o.pull_en = 1'b1;
-    attr_warl_o.pull_select = 1'b1;
-    // Only one driving strength bit is supported.
-    attr_warl_o.drive_strength[0] = 1'b1;
-`endif
+  if (PadType == InputStd) begin : gen_input_only_warl
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+      attr_warl_o.invert = 1'b1;
+  // Driving strength and pulls are not supported by Verilator
+  `ifndef VERILATOR
+      attr_warl_o.pull_en = 1'b1;
+      attr_warl_o.pull_select = 1'b1;
+  `endif
+    end
+  end else if (PadType == BidirStd ||
+               PadType == BidirTol ||
+               PadType == BidirOd) begin : gen_bidir_warl
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+      attr_warl_o.invert = 1'b1;
+      attr_warl_o.virt_od_en = 1'b1;
+      attr_warl_o.keep_en = 1'b1;
+  // Driving strength and pulls are not supported by Verilator
+  `ifndef VERILATOR
+      attr_warl_o.pull_en = 1'b1;
+      attr_warl_o.pull_select = 1'b1;
+      // Only one driving strength bit is supported.
+      attr_warl_o.drive_strength[0] = 1'b1;
+  `endif
+    end
+  end else if (PadType == AnalogIn0) begin : gen_analog0_warl
+    // The analog pad type is basically just a feedthrough,
+    // and does hence not support any of the attributes.
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+    end
+  end else begin : gen_invalid_config
+    // this should throw link warnings in elaboration
+    assert_static_in_generate_config_not_available
+        assert_static_in_generate_config_not_available();
   end
 
 endmodule : prim_generic_pad_attr
diff --git a/hw/ip/prim_generic/rtl/prim_generic_pad_wrapper.sv b/hw/ip/prim_generic/rtl/prim_generic_pad_wrapper.sv
index 11d3151..59a402d 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_pad_wrapper.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_pad_wrapper.sv
@@ -29,6 +29,9 @@
   input pad_attr_t   attr_i    // additional pad attributes
 );
 
+  // analog pads cannot have a scan role.
+  `ASSERT_INIT(AnalogNoScan_A, PadType != AnalogIn0 || ScanRole == NoScan)
+
   // not all signals are used here.
   logic unused_sigs;
   assign unused_sigs = ^{attr_i.slew_rate,
@@ -38,26 +41,59 @@
                          scanmode_i,
                          pok_i};
 
-  assign in_raw_o = (ie_i) ? inout_io  : 1'bz;
-  // input inversion
-  assign in_o = attr_i.invert ^ in_raw_o;
+  if (PadType == InputStd) begin : gen_input_only
+    logic unused_sigs;
+    assign unused_sigs = ^{out_i,
+                           oe_i,
+                           attr_i.virt_od_en,
+                           attr_i.drive_strength};
 
-  // virtual open drain emulation
-  logic oe, out;
-  assign out = out_i ^ attr_i.invert;
-  assign oe  = oe_i & ((attr_i.virt_od_en & ~out) | ~attr_i.virt_od_en);
+    assign in_raw_o = (ie_i) ? inout_io  : 1'bz;
+    // input inversion
+    assign in_o = attr_i.invert ^ in_raw_o;
 
-// drive strength attributes are not supported by verilator
-`ifdef VERILATOR
-  assign inout_io = (oe)   ? out : 1'bz;
-`else
-  // different driver types
-  assign (strong0, strong1) inout_io = (oe && attr_i.drive_strength[0]) ? out : 1'bz;
-  assign (pull0, pull1)     inout_io = (oe && !attr_i.drive_strength[0]) ? out : 1'bz;
-  // pullup / pulldown termination
-  assign (weak0, weak1)     inout_io = attr_i.pull_en ? attr_i.pull_select : 1'bz;
-  // fake trireg emulation
-  assign (weak0, weak1)     inout_io = (attr_i.keep_en) ? inout_io : 1'bz;
-`endif
+  // pulls are not supported by verilator
+  `ifndef VERILATOR
+    // pullup / pulldown termination
+    assign (weak0, weak1) inout_io = attr_i.pull_en ? attr_i.pull_select : 1'bz;
+  `endif
+  end else if (PadType == BidirTol ||
+               PadType == BidirOd ||
+               PadType == BidirStd) begin : gen_bidir
+
+    assign in_raw_o = (ie_i) ? inout_io  : 1'bz;
+    // input inversion
+    assign in_o = attr_i.invert ^ in_raw_o;
+
+    // virtual open drain emulation
+    logic oe, out;
+    assign out = out_i ^ attr_i.invert;
+    assign oe  = oe_i & ((attr_i.virt_od_en & ~out) | ~attr_i.virt_od_en);
+
+  // drive strength attributes are not supported by verilator
+  `ifdef VERILATOR
+    assign inout_io = (oe)   ? out : 1'bz;
+  `else
+    // different driver types
+    assign (strong0, strong1) inout_io = (oe && attr_i.drive_strength[0]) ? out : 1'bz;
+    assign (pull0, pull1)     inout_io = (oe && !attr_i.drive_strength[0]) ? out : 1'bz;
+    // pullup / pulldown termination
+    assign (weak0, weak1)     inout_io = attr_i.pull_en ? attr_i.pull_select : 1'bz;
+    // fake trireg emulation
+    assign (weak0, weak1)     inout_io = (attr_i.keep_en) ? inout_io : 1'bz;
+  `endif
+  end else if (PadType == AnalogIn0) begin : gen_analog0
+
+    logic unused_sigs;
+    assign unused_sigs = ^{attr_i, out_i, oe_i, ie_i};
+
+    assign in_o = inout_io;
+    assign in_raw_o = inout_io;
+
+  end else begin : gen_invalid_config
+    // this should throw link warnings in elaboration
+    assert_static_in_generate_config_not_available
+        assert_static_in_generate_config_not_available();
+  end
 
 endmodule : prim_generic_pad_wrapper
diff --git a/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_attr.sv b/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_attr.sv
index 1f1187e..3e10f42 100644
--- a/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_attr.sv
+++ b/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_attr.sv
@@ -14,18 +14,41 @@
   output pad_attr_t attr_warl_o
 );
 
-  // Currently supporte pad attributes of the Xilinx pad library.
+  // Currently supported pad attributes of the Xilinx pad library.
+  //
+  // Input-only:
+  //
+  // - inversion
+  //
+  // Bidirectional:
   //
   // - inversion
   // - virtual open drain
   //
-  // TODO: add support for dynamic pull-up/down using the PULLUP/PULLDOWN primitives
-  // from the vivado-7series library (https://www.xilinx.com/support/documentation/
-  // sw_manuals/xilinx2020_2/ug953-vivado-7series-libraries.pdf)
-  always_comb begin : p_attr
-    attr_warl_o = '0;
-    attr_warl_o.invert = 1'b1;
-    attr_warl_o.virt_od_en = 1'b1;
+  if (PadType == InputStd) begin : gen_input_only_warl
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+      attr_warl_o.invert = 1'b1;
+    end
+  end else if (PadType == BidirStd ||
+               PadType == BidirTol ||
+               PadType == BidirOd) begin : gen_bidir_warl
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+      attr_warl_o.invert = 1'b1;
+      attr_warl_o.virt_od_en = 1'b1;
+    end
+  end else if (PadType == Analog0) begin : gen_analog0_warl
+    // The analog pad type is basically just a feedthrough,
+    // and does hence not support any of the attributes.
+    always_comb begin : p_attr
+      attr_warl_o = '0;
+    end
+  end else begin : gen_invalid_config
+    // this should throw link warnings in elaboration
+    assert_static_in_generate_config_not_available
+        assert_static_in_generate_config_not_available();
   end
 
+
 endmodule : prim_xilinx_pad_attr
diff --git a/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv b/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv
index 58608ff..db0d60b 100644
--- a/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv
+++ b/hw/ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv
@@ -28,6 +28,9 @@
   input pad_attr_t   attr_i    // additional pad attributes
 );
 
+  // analog pads cannot have a scan role.
+  `ASSERT_INIT(AnalogNoScan_A, PadType != AnalogIn0 || ScanRole == NoScan)
+
   // not all signals are used here.
   logic unused_sigs;
   assign unused_sigs = ^{attr_i.slew_rate,
@@ -40,24 +43,63 @@
                          scanmode_i,
                          pok_i};
 
-  // input inversion
-  logic in;
-  assign in_raw_o = (ie_i) ? in  : 1'bz;
-  assign in_o = attr_i.invert ^ in_raw_o;
 
-  // virtual open drain emulation
-  logic oe_n, out;
-  assign out      = out_i ^ attr_i.invert;
-  // oe_n = 0: enable driver
-  // oe_n = 1: disable driver
-  assign oe_n     = ~oe_i | (out & attr_i.virt_od_en);
+  if (PadType == InputStd) begin : gen_input_only
+    logic unused_sigs;
+    assign unused_sigs = ^{out_i,
+                           oe_i,
+                           attr_i.virt_od_en};
 
-  // driver
-  IOBUF u_iobuf (
-    .T  ( oe_n     ),
-    .I  ( out      ),
-    .O  ( in       ),
-    .IO ( inout_io )
-  );
+    // input inversion
+    logic in;
+    assign in_raw_o = (ie_i) ? in  : 1'bz;
+    assign in_o = attr_i.invert ^ in_raw_o;
+
+    IBUF u_ibuf (
+      .I ( inout_io ),
+      .O ( in       )
+    );
+  end else if (PadType == BidirTol ||
+               PadType == BidirOd ||
+               PadType == BidirStd) begin : gen_bidir
+
+    // input inversion
+    logic in;
+    assign in_raw_o = (ie_i) ? in  : 1'bz;
+    assign in_o = attr_i.invert ^ in_raw_o;
+
+    // virtual open drain emulation
+    logic oe_n, out;
+    assign out      = out_i ^ attr_i.invert;
+    // oe_n = 0: enable driver
+    // oe_n = 1: disable driver
+    assign oe_n     = ~oe_i | (out & attr_i.virt_od_en);
+
+    IOBUF u_iobuf (
+      .T  ( oe_n     ),
+      .I  ( out      ),
+      .O  ( in       ),
+      .IO ( inout_io )
+    );
+  end else if (PadType == AnalogIn0) begin : gen_analog0
+
+    logic unused_sigs;
+    assign unused_sigs = ^{out_i,
+                           oe_i,
+                           attr_i.invert,
+                           attr_i.virt_od_en};
+    // this is currently modeled as a logic feed through.
+    IBUF u_ibuf (
+      .I ( inout_io ),
+      .O ( in_raw_o )
+    );
+
+    assign in_raw_o = inout_io;
+
+  end else begin : gen_invalid_config
+    // this should throw link warnings in elaboration
+    assert_static_in_generate_config_not_available
+        assert_static_in_generate_config_not_available();
+  end
 
 endmodule : prim_xilinx_pad_wrapper