Merge pull request #7 from proppy/configurable-io

make i/o configurable
diff --git a/.github/workflows/user_project_ci.yml b/.github/workflows/user_project_ci.yml
index 12d29e9..4f4f52a 100644
--- a/.github/workflows/user_project_ci.yml
+++ b/.github/workflows/user_project_ci.yml
@@ -60,7 +60,7 @@
 
     - name: fetch verilog and build config
       run: |
-        python -m pip install requests PyYAML
+        python -m pip install requests PyYAML Jinja2
         python configure.py --create-user-config
         
     - name: harden tiny_user_project
diff --git a/configure.py b/configure.py
index ff5d954..9730dd4 100755
--- a/configure.py
+++ b/configure.py
@@ -9,36 +9,31 @@
 import sys
 import csv
 import re
+import jinja2
 
+GPIO_VALID_RANGE = [8, 36]
 
 def load_yaml(yaml_file):
     with open(yaml_file, "r") as stream:
         return (yaml.safe_load(stream))
 
-tiny_user_project_v = '''// generated by configure.py
-`default_nettype none
-
-module tiny_user_project(
-  input [`MPRJ_IO_PADS-1:0] io_in,
-  output [`MPRJ_IO_PADS-1:0] io_out,
-  output [`MPRJ_IO_PADS-1:0] io_oeb
-);
-
-// pass input and output pins defined in user_defines.v
-{module_name} mod (
-    io_in[19:12],
-    io_out[27:20]
-);
-// all output enabled
-assign io_oeb[27:20] = 8'b0;
-
-endmodule	// tiny_user_project
-
-`default_nettype wire'''
-    
-def write_user_config(module_name, sources):
+def write_user_config(module_name, sources, io_ranges):
+    env = jinja2.Environment(
+        loader = jinja2.FileSystemLoader('verilog/rtl')
+    )
+    top_module_template = env.get_template('tiny_user_project.v.jinja2')
     with open('verilog/rtl/tiny_user_project.v', 'w') as fh:
-        fh.write(tiny_user_project_v.format(module_name=module_name))
+        fh.write(top_module_template.render(
+            module_name=module_name,
+            io_in_range=io_ranges[0],
+            io_out_range=io_ranges[1]
+        ))
+    user_defines_template = env.get_template('user_defines.v.jinja2')
+    with open('verilog/rtl/user_defines.v', 'w') as fh:
+        fh.write(user_defines_template.render(
+            io_in_range=io_ranges[0],
+            io_out_range=io_ranges[1]
+        ))
     with open('openlane/tiny_user_project/config.json', 'r') as fh:
         config_json = json.load(fh)
     sources.append('verilog/rtl/defines.v')
@@ -64,7 +59,7 @@
             logging.warning("couldn't download {}".format(url))
             exit(1)
 
-        filename = "user_module_{}.v".format(wokwi_id)
+        filename = "user_module.v"
         with open(os.path.join('verilog/rtl', filename), 'wb') as fh:
             fh.write(r.content)
 
@@ -128,6 +123,13 @@
     else:
         return yaml['project']['top_module']
 
+def get_io_ranges(yaml):
+    input_range = (GPIO_VALID_RANGE[0], GPIO_VALID_RANGE[0]+len(yaml['documentation']['inputs']))
+    output_range = (input_range[1], input_range[1]+len(yaml['documentation']['outputs']))
+    gpio_end = output_range[1]
+    if gpio_end > GPIO_VALID_RANGE[1]:
+        raise Exception('ETOOMANY IOs')
+    return (input_range, output_range)
 
 def get_stats():
     with open('runs/wokwi/reports/metrics.csv') as f:
@@ -177,4 +179,5 @@
         source_files = get_project_source(config)
         top_module = get_top_module(config)
         assert top_module != 'top'
-        write_user_config(top_module, source_files)
+        io_ranges = get_io_ranges(config)
+        write_user_config(top_module, source_files, io_ranges)
diff --git a/openlane/tiny_user_project/config.json b/openlane/tiny_user_project/config.json
index 897d83d..0379a03 100644
--- a/openlane/tiny_user_project/config.json
+++ b/openlane/tiny_user_project/config.json
@@ -2,10 +2,10 @@
     "DESIGN_NAME": "tiny_user_project",
     "DESIGN_IS_CORE": 0,
     "VERILOG_FILES": [
-	"dir::../../verilog/rtl/defines.v",
-	"dir::../../verilog/rtl/cells.v",
-	"dir::../../verilog/rtl/user_module.v",
-	"dir::../../verilog/rtl/tiny_user_project.v"	
+        "dir::../../verilog/rtl/user_module.v",
+        "dir::../../verilog/rtl/cells.v",
+        "dir::../../verilog/rtl/defines.v",
+        "dir::../../verilog/rtl/tiny_user_project.v"
     ],
     "CLOCK_PERIOD": 10,
     "CLOCK_PORT": "",
@@ -14,20 +14,24 @@
     "DIE_AREA": "0 0 250 250",
     "PL_BASIC_PLACEMENT": 1,
     "PL_TARGET_DENSITY": 0.55,
-    "VDD_NETS": ["vccd1"],
-    "GND_NETS": ["vssd1"],
+    "VDD_NETS": [
+        "vccd1"
+    ],
+    "GND_NETS": [
+        "vssd1"
+    ],
     "DIODE_INSERTION_STRATEGY": 4,
     "RUN_CVC": 1,
     "RUN_KLAYOUT_XOR": 0,
     "RUN_KLAYOUT_DRC": 0,
     "pdk::sky130*": {
-	"DECAP_CELL": [
-	    "sky130_fd_sc_hd__decap_3",
-	    "sky130_fd_sc_hd__decap_4",
-	    "sky130_fd_sc_hd__decap_6",
-	    "sky130_fd_sc_hd__decap_8",
-	    "sky130_ef_sc_hd__decap_12"
-	],
+        "DECAP_CELL": [
+            "sky130_fd_sc_hd__decap_3",
+            "sky130_fd_sc_hd__decap_4",
+            "sky130_fd_sc_hd__decap_6",
+            "sky130_fd_sc_hd__decap_8",
+            "sky130_ef_sc_hd__decap_12"
+        ],
         "RT_MAX_LAYER": "met4"
     },
     "pdk::gf180mcuC": {
@@ -35,4 +39,4 @@
         "RT_MAX_LAYER": "Metal4",
         "SYNTH_MAX_FANOUT": 4
     }
-}
+}
\ No newline at end of file
diff --git a/verilog/rtl/tiny_user_project.v b/verilog/rtl/tiny_user_project.v
index f8e3149..0b99eae 100644
--- a/verilog/rtl/tiny_user_project.v
+++ b/verilog/rtl/tiny_user_project.v
@@ -1,6 +1,9 @@
-// generated by configure.py
+// Copyright 2022 Google LLC.
+// SPDX-License-Identifier: Apache-2.0
 `default_nettype none
 
+// tiny_user_project top module instance
+// generated by configure.py
 module tiny_user_project(
   input [`MPRJ_IO_PADS-1:0] io_in,
   output [`MPRJ_IO_PADS-1:0] io_out,
@@ -9,11 +12,11 @@
 
 // pass input and output pins defined in user_defines.v
 user_module_334445762078310996 mod (
-    io_in[19:12],
-    io_out[27:20]
+    io_in[15:8],
+    io_out[23:16]
 );
 // all output enabled
-assign io_oeb[27:20] = 8'b0;
+assign io_oeb[23:16] = 8'b0;
 
 endmodule	// tiny_user_project
 
diff --git a/verilog/rtl/tiny_user_project.v.jinja2 b/verilog/rtl/tiny_user_project.v.jinja2
new file mode 100644
index 0000000..8dc53cc
--- /dev/null
+++ b/verilog/rtl/tiny_user_project.v.jinja2
@@ -0,0 +1,23 @@
+// Copyright 2022 Google LLC.
+// SPDX-License-Identifier: Apache-2.0
+`default_nettype none
+
+// tiny_user_project top module instance
+// generated by configure.py
+module tiny_user_project(
+  input [`MPRJ_IO_PADS-1:0] io_in,
+  output [`MPRJ_IO_PADS-1:0] io_out,
+  output [`MPRJ_IO_PADS-1:0] io_oeb
+);
+
+// pass input and output pins defined in user_defines.v
+{{ module_name }} mod (
+    io_in[{{ io_in_range[1] - 1 }}:{{ io_in_range[0] }}],
+    io_out[{{ io_out_range[1] - 1 }}:{{ io_out_range[0] }}]
+);
+// all output enabled
+assign io_oeb[{{ io_out_range[1] - 1 }}:{{ io_out_range[0] }}] = 8'b0;
+
+endmodule	// tiny_user_project
+
+`default_nettype wire
\ No newline at end of file
diff --git a/verilog/rtl/user_defines.v b/verilog/rtl/user_defines.v
index 7a38e03..f65f57b 100644
--- a/verilog/rtl/user_defines.v
+++ b/verilog/rtl/user_defines.v
@@ -52,34 +52,33 @@
 // up in a state that can be used immediately without depending on
 // the management SoC to run a startup program to configure the GPIOs.
 
-`define USER_CONFIG_GPIO_5_INIT  `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_6_INIT  `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_7_INIT  `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_8_INIT  `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_9_INIT  `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_10_INIT `GPIO_MODE_INVALID
-`define USER_CONFIG_GPIO_11_INIT `GPIO_MODE_INVALID
+// tiny_user_project i/o
+// generated by configure.py
 
-// tinytapeout project input
+
+`define USER_CONFIG_GPIO_5_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_6_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_7_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_8_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
+`define USER_CONFIG_GPIO_9_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
+`define USER_CONFIG_GPIO_10_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
+`define USER_CONFIG_GPIO_11_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
 `define USER_CONFIG_GPIO_12_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
 `define USER_CONFIG_GPIO_13_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
 `define USER_CONFIG_GPIO_14_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
 `define USER_CONFIG_GPIO_15_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
-`define USER_CONFIG_GPIO_16_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
-`define USER_CONFIG_GPIO_17_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
-`define USER_CONFIG_GPIO_18_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
-`define USER_CONFIG_GPIO_19_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
-
-// tinytapeout project output
+`define USER_CONFIG_GPIO_16_INIT `GPIO_MODE_USER_STD_OUTPUT
+`define USER_CONFIG_GPIO_17_INIT `GPIO_MODE_USER_STD_OUTPUT
+`define USER_CONFIG_GPIO_18_INIT `GPIO_MODE_USER_STD_OUTPUT
+`define USER_CONFIG_GPIO_19_INIT `GPIO_MODE_USER_STD_OUTPUT
 `define USER_CONFIG_GPIO_20_INIT `GPIO_MODE_USER_STD_OUTPUT
 `define USER_CONFIG_GPIO_21_INIT `GPIO_MODE_USER_STD_OUTPUT
 `define USER_CONFIG_GPIO_22_INIT `GPIO_MODE_USER_STD_OUTPUT
 `define USER_CONFIG_GPIO_23_INIT `GPIO_MODE_USER_STD_OUTPUT
-`define USER_CONFIG_GPIO_24_INIT `GPIO_MODE_USER_STD_OUTPUT
-`define USER_CONFIG_GPIO_25_INIT `GPIO_MODE_USER_STD_OUTPUT
-`define USER_CONFIG_GPIO_26_INIT `GPIO_MODE_USER_STD_OUTPUT
-`define USER_CONFIG_GPIO_27_INIT `GPIO_MODE_USER_STD_OUTPUT
-
+`define USER_CONFIG_GPIO_24_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_25_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_26_INIT `GPIO_MODE_INVALID
+`define USER_CONFIG_GPIO_27_INIT `GPIO_MODE_INVALID
 `define USER_CONFIG_GPIO_28_INIT `GPIO_MODE_INVALID
 `define USER_CONFIG_GPIO_29_INIT `GPIO_MODE_INVALID
 `define USER_CONFIG_GPIO_30_INIT `GPIO_MODE_INVALID
@@ -91,4 +90,4 @@
 `define USER_CONFIG_GPIO_36_INIT `GPIO_MODE_INVALID
 `define USER_CONFIG_GPIO_37_INIT `GPIO_MODE_INVALID
 
-`endif // __USER_DEFINES_H
+`endif // __USER_DEFINES_H
\ No newline at end of file
diff --git a/verilog/rtl/user_defines.v.jinja2 b/verilog/rtl/user_defines.v.jinja2
new file mode 100644
index 0000000..c935c87
--- /dev/null
+++ b/verilog/rtl/user_defines.v.jinja2
@@ -0,0 +1,68 @@
+// 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
+
+`ifndef __USER_DEFINES_H
+// User GPIO initial configuration parameters
+`define __USER_DEFINES_H
+
+// deliberately erroneous placeholder value; user required to config GPIO's to other
+`define GPIO_MODE_INVALID                  13'hXXXX
+
+// Authoritive source of these MODE defs is: caravel/verilog/rtl/user_defines.v
+// Useful GPIO mode values.  These match the names used in defs.h.
+//
+`define GPIO_MODE_MGMT_STD_INPUT_NOPULL    13'h0403
+`define GPIO_MODE_MGMT_STD_INPUT_PULLDOWN  13'h0c01
+`define GPIO_MODE_MGMT_STD_INPUT_PULLUP    13'h0801
+`define GPIO_MODE_MGMT_STD_OUTPUT          13'h1809
+`define GPIO_MODE_MGMT_STD_BIDIRECTIONAL   13'h1801
+`define GPIO_MODE_MGMT_STD_ANALOG          13'h000b
+
+`define GPIO_MODE_USER_STD_INPUT_NOPULL    13'h0402
+`define GPIO_MODE_USER_STD_INPUT_PULLDOWN  13'h0c00
+`define GPIO_MODE_USER_STD_INPUT_PULLUP    13'h0800
+`define GPIO_MODE_USER_STD_OUTPUT          13'h1808
+`define GPIO_MODE_USER_STD_BIDIRECTIONAL   13'h1800
+`define GPIO_MODE_USER_STD_OUT_MONITORED   13'h1802
+`define GPIO_MODE_USER_STD_ANALOG          13'h000a
+
+// The power-on configuration for GPIO 0 to 4 is fixed and cannot be
+// modified (allowing the SPI and debug to always be accessible unless
+// overridden by a flash program).
+
+// The values below can be any of the standard types defined above,
+// or they can be any 13-bit value if the user wants a non-standard
+// startup state for the GPIO.  By default, every GPIO from 5 to 37
+// is set to power up as an input controlled by the management SoC.
+// Users may want to redefine these so that the user project powers
+// up in a state that can be used immediately without depending on
+// the management SoC to run a startup program to configure the GPIOs.
+
+// tiny_user_project i/o
+// generated by configure.py
+
+{% for n in range(5, 38) -%}
+{%- if io_in_range[0] <= n < io_in_range[1] %}
+`define USER_CONFIG_GPIO_{{ n }}_INIT `GPIO_MODE_USER_STD_INPUT_NOPULL
+{%- elif io_out_range[0] <= n < io_out_range[1] %}
+`define USER_CONFIG_GPIO_{{ n }}_INIT `GPIO_MODE_USER_STD_OUTPUT
+{%- else %}
+`define USER_CONFIG_GPIO_{{ n }}_INIT `GPIO_MODE_INVALID
+{%- endif -%}
+{% endfor %}
+
+`endif // __USER_DEFINES_H
diff --git a/verilog/rtl/user_module_334445762078310996.v b/verilog/rtl/user_module.v
similarity index 100%
rename from verilog/rtl/user_module_334445762078310996.v
rename to verilog/rtl/user_module.v