diff --git a/caravel.py b/caravel.py index 6710855..eada1c0 100644 --- a/caravel.py +++ b/caravel.py @@ -11,97 +11,14 @@ class CaravelConfig(): self.num_projects = num_projects self.script_dir = os.path.dirname(os.path.realpath(__file__)) - # co-ords are bottom left corner - def place_macro(self, x, y, instance, rotate, fh): - c = self.config['layout'] - if not rotate: - # scanchain first - orientation = 'N' - macro_instance = self.projects[instance].get_scanchain_instance() - fh.write(f"{macro_instance} {x:<4} {y:<4} {orientation}\n") - - macro_instance = self.projects[instance].get_macro_instance() - x = x + c['scanchain_w'] + c['scanchain_spc'] - fh.write(f"{macro_instance} {x:<4} {y:<4} {orientation}\n") - else: - # macro first - orientation = 'S' - macro_instance = self.projects[instance].get_macro_instance() - fh.write(f"{macro_instance} {x:<4} {y:<4} {orientation}\n") - - macro_instance = self.projects[instance].get_scanchain_instance() - x = x + c['module_w'] + c['scanchain_spc'] - y = y + c['module_h'] - c['scanchain_h'] - fh.write(f"{macro_instance} {x:<4} {y:<4} {orientation}\n") - - # create macro file & positions, power hooks + # update caravel config def create_macro_config(self): - # array size - c = self.config['layout'] - - pos_x = 2 - pos_y = 0 - dir_x = 1 - dir_y = 1 - - # macro.cfg: where macros are placed - logging.info("creating macro.cfg") - with open("openlane/user_project_wrapper/macro.cfg", 'w') as fh: - fh.write(f"scan_controller {c['scan_control_x']} {c['scan_control_y']} N\n") - for i in range(self.num_projects): - logging.debug(f"Block {i:d} at ({pos_x:d}, {pos_y:d})") - # calculate 0, 0 point - macro_x = c['start_x'] + pos_x * (c['module_w'] + c['scanchain_w'] + c['scanchain_spc'] + c['space_x']) - macro_y = c['start_y'] + pos_y * (c['module_h'] + c['space_y']) - - # rotated? - if dir_x == 1: - rotate = False - else: - rotate = True - - self.place_macro(macro_x, macro_y, i, rotate, fh) - - # next position - pos_x += 2 * dir_x - - if pos_x >= c['cols']: - pos_x = c['cols'] - 1 - pos_y += dir_y - dir_x = - dir_x - - elif pos_x < 0: - pos_x = 0 - pos_y += dir_y - dir_x = - dir_x - - if pos_y >= c['rows']: - pos_y = c['rows'] - 1 - dir_y = - dir_y - - logging.info(f"total user macros placed: {self.num_projects}") - with open(os.path.join(self.script_dir, 'caravel_template', 'upw_config.json')) as fh: caravel_config = json.load(fh) - power_domains = "vccd1 vssd1 vccd1 vssd1" - power_config = [] - logging.info("creating macro hooks") - - for i in range(self.num_projects): - power_config.append(f"{self.projects[i].get_scanchain_instance()} {power_domains},") - power_config.append(f"{self.projects[i].get_macro_instance()} {power_domains},") - power_config.append(f"scan_controller {power_domains}") # no trailing comma - - caravel_config["FP_PDN_MACRO_HOOKS"] = power_config - logging.info("GDS and LEF") lef_prefix = "dir::../../lef/" gds_prefix = "dir::../../gds/" - caravel_config["EXTRA_LEFS"].append(lef_prefix + "scan_controller.lef") - caravel_config["EXTRA_LEFS"].append(lef_prefix + "scanchain.lef") - caravel_config["EXTRA_GDS_FILES"].append(gds_prefix + "scan_controller.gds") - caravel_config["EXTRA_GDS_FILES"].append(gds_prefix + "scanchain.gds") for project in self.projects: if not project.is_fill(): caravel_config["EXTRA_LEFS"].append(lef_prefix + project.get_macro_lef_filename()) @@ -112,115 +29,8 @@ class CaravelConfig(): # instantiate inside user_project_wrapper def instantiate(self): - logging.info("instantiating designs in user_project_wrapper.v") - - # NOTE: The user project wrapper initially used vectored signals for clk, - # scan, and latch signals. However, this leads to atrocious sim - # performance, as any change within the vectored signal is - # interpreted as a trigger condition for re-evaluating logic (at - # least this is the case under Icarus and Verilator). Therefore - # single bit signals are used between stages to limit the impact - # of any wire changing. - - # Instance the scan controller - body = [ - "", - "wire sc_clk_out, sc_data_out, sc_latch_out, sc_scan_out;", - "wire sc_clk_in, sc_data_in;", - "", - f"scan_controller #(.NUM_DESIGNS({self.num_projects})) scan_controller (", - " .clk (wb_clk_i),", - " .reset (wb_rst_i),", - " .active_select (io_in[20:12]),", - " .inputs (io_in[28:21]),", - " .outputs (io_out[36:29]),", - " .ready (io_out[37]),", - " .slow_clk (io_out[10]),", - " .set_clk_div (io_in[11]),", - "", - " .scan_clk_out (sc_clk_out),", - " .scan_clk_in (sc_clk_in),", - " .scan_data_out (sc_data_out),", - " .scan_data_in (sc_data_in),", - " .scan_select (sc_scan_out),", - " .scan_latch_en (sc_latch_out),", - "", - " .la_scan_clk_in (la_data_in[0]),", - " .la_scan_data_in (la_data_in[1]),", - " .la_scan_data_out (la_data_out[0]),", - " .la_scan_select (la_data_in[2]),", - " .la_scan_latch_en (la_data_in[3]),", - "", - " .driver_sel (io_in[9:8]),", - "", - " .oeb (io_oeb)", - ");", - ] - - # Instance every design on the scan chain - for idx in range(self.num_projects): - # First design driven by scan controller, all others are chained - pfx = f"sw_{idx:03d}" - prev_pfx = f"sw_{idx-1:03d}" if idx > 0 else "sc" - # Pickup the Wokwi design ID and github URL for the project - giturl = self.projects[idx].get_git_url() - - # Append the instance to the body - body += [ - "", - f"// [{idx:03d}] {giturl}", - f"wire {pfx}_clk_out, {pfx}_data_out, {pfx}_scan_out, {pfx}_latch_out;", - f"wire [7:0] {pfx}_module_data_in;", - f"wire [7:0] {pfx}_module_data_out;", - f"scanchain #(.NUM_IOS(8)) {self.projects[idx].get_scanchain_instance()} (", - f" .clk_in ({prev_pfx}_clk_out),", - f" .data_in ({prev_pfx}_data_out),", - f" .scan_select_in ({prev_pfx}_scan_out),", - f" .latch_enable_in ({prev_pfx}_latch_out),", - f" .clk_out ({pfx}_clk_out),", - f" .data_out ({pfx}_data_out),", - f" .scan_select_out ({pfx}_scan_out),", - f" .latch_enable_out({pfx}_latch_out),", - f" .module_data_in ({pfx}_module_data_in),", - f" .module_data_out ({pfx}_module_data_out)", - ");" - ] - - # Append the user module to the body - body += [ - "", - f"{self.projects[idx].get_macro_name()} {self.projects[idx].get_macro_instance()} (", - f" .io_in ({pfx}_module_data_in),", - f" .io_out ({pfx}_module_data_out)", - ");" - - ] - - # Link the final design back to the scan controller - body += [ - "", - "// Connect final signals back to the scan controller", - f"assign sc_clk_in = sw_{idx:03d}_clk_out;", - f"assign sc_data_in = sw_{idx:03d}_data_out;", - "", - "" - ] - - # Write to file - with open('verilog/rtl/user_project_wrapper.v', 'w') as fh: - # Insert the Caravel preamble - with open(os.path.join(self.script_dir, 'caravel_template', 'upw_pre.v')) as fh_pre: - fh.write(fh_pre.read()) - # Indent, join, and insert the module instances - fh.write("\n".join([(" " + x).rstrip() for x in body])) - # Insert the Caravel postamble - with open(os.path.join(self.script_dir, 'caravel_template', 'upw_post.v')) as fh_post: - fh.write(fh_post.read()) - # build the blackbox_project_includes.v file - used for blackboxing when building the GDS with open('verilog/blackbox_project_includes.v', 'w') as fh: - fh.write('`include "rtl/scan_controller/scan_controller.v"\n') - fh.write('`include "rtl/scanchain/scanchain.v"\n') for project in self.projects: if not project.is_fill(): fh.write(f'`include "gl/{project.get_gl_verilog_filename()}"\n') diff --git a/caravel_template/upw_config.json b/caravel_template/upw_config.json index 6c2b1c6..284a5c7 100644 --- a/caravel_template/upw_config.json +++ b/caravel_template/upw_config.json @@ -2,23 +2,38 @@ "DESIGN_NAME": "user_project_wrapper", "VERILOG_FILES": [ "dir::../../verilog/rtl/defines.v", - "dir::../../verilog/rtl/user_project_wrapper.v" + "dir::../../verilog/rtl/user_project_wrapper.v", + "dir::../../tt-multiplexer/proto/tt_top.v", + "dir::../../tt-multiplexer/proto/tt_user_module.v" + ], + "VERILOG_FILES_BLACKBOX": [ + "dir::../../tt-multiplexer/proto/tt_mux.v", + "dir::../../tt-multiplexer/proto/tt_ctrl.v", + "dir::../../tt-multiplexer/proto/tt_um_test.v", + "dir::../../verilog/blackbox_project_includes.v" ], "ROUTING_CORES": 8, "CLOCK_PERIOD": 25, "CLOCK_PORT": "wb_clk_i", "CLOCK_NET": "mprj.clk", - "FP_PDN_MACRO_HOOKS": [], + "FP_PDN_MACRO_HOOKS": [ + "tt_top1\\.ctrl_I vccd1 vssd1 vccd1 vssd1,", + "tt_top1\\..*mux_I vccd1 vssd1 vccd1 vssd1,", + "tt_top1\\..*tt_um_I vccd1 vssd1 vccd1 vssd1" + ], "MACRO_PLACEMENT_CFG": "dir::macro.cfg", "PDN_CFG": "dir::pdn_cfg.tcl", "MAGIC_DEF_LABELS": 0, - "VERILOG_FILES_BLACKBOX": [ - "dir::../../verilog/rtl/defines.v", - "dir::../../verilog/blackbox_project_includes.v" + "EXTRA_LEFS": [ + "dir::../../lef/tt_ctrl.lef", + "dir::../../lef/tt_mux.lef", + "dir::../../lef/tt_um_test.lef" + ], + "EXTRA_GDS_FILES": [ + "dir::../../gds/tt_ctrl.gds", + "dir::../../gds/tt_mux.gds", + "dir::../../gds/tt_um_test.gds" ], - "EXTRA_LEFS": [], - "EXTRA_GDS_FILES": [], - "BASE_SDC_FILE": "dir::base_user_project_wrapper.sdc", "RUN_KLAYOUT_XOR": 0, "RUN_KLAYOUT_DRC": 0, "RUN_MAGIC_DRC": 0, @@ -31,15 +46,17 @@ "PL_RESIZER_DESIGN_OPTIMIZATIONS": 0, "PL_RESIZER_TIMING_OPTIMIZATIONS": 0, "PL_RESIZER_BUFFER_INPUT_PORTS": 0, + "SYNTH_FLAT_TOP": 1, "FP_PDN_ENABLE_RAILS": 0, "DIODE_INSERTION_STRATEGY": 0, "RUN_FILL_INSERTION": 0, "RUN_TAP_DECAP_INSERTION": 0, "FP_PDN_VPITCH": 140, "FP_PDN_VOFFSET": 186, - "FP_PDN_HPITCH": 185, + "FP_PDN_HPITCH": 50, "FP_PDN_HOFFSET": 160, "CLOCK_TREE_SYNTH": 0, + "QUIT_ON_LVS_ERROR": "0", "MAGIC_ZEROIZE_ORIGIN": 0, "FP_SIZING": "absolute", "RUN_CVC": 0, @@ -62,48 +79,48 @@ "FP_PDN_VSPACING": "expr::(5 * $FP_PDN_CORE_RING_VWIDTH)", "FP_PDN_HSPACING": "expr::(5 * $FP_PDN_CORE_RING_HWIDTH)", "VDD_NETS": [ - "vccd1", - "vccd2", - "vdda1", - "vdda2" + "vccd1", + "vccd2", + "vdda1", + "vdda2" ], "GND_NETS": [ - "vssd1", - "vssd2", - "vssa1", - "vssa2" + "vssd1", + "vssd2", + "vssa1", + "vssa2" ], "SYNTH_USE_PG_PINS_DEFINES": "USE_POWER_PINS", "pdk::sky130*": { - "RT_MAX_LAYER": "met4", - "DIE_AREA": "0 0 2920 3520", - "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def", - "scl::sky130_fd_sc_hd": { - "CLOCK_PERIOD": 25 - }, - "scl::sky130_fd_sc_hdll": { - "CLOCK_PERIOD": 10 - }, - "scl::sky130_fd_sc_hs": { - "CLOCK_PERIOD": 8 - }, - "scl::sky130_fd_sc_ls": { - "CLOCK_PERIOD": 10, - "SYNTH_MAX_FANOUT": 5 - }, - "scl::sky130_fd_sc_ms": { - "CLOCK_PERIOD": 10 - } + "RT_MAX_LAYER": "met4", + "DIE_AREA": "0 0 2920 3520", + "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def", + "scl::sky130_fd_sc_hd": { + "CLOCK_PERIOD": 25 + }, + "scl::sky130_fd_sc_hdll": { + "CLOCK_PERIOD": 10 + }, + "scl::sky130_fd_sc_hs": { + "CLOCK_PERIOD": 8 + }, + "scl::sky130_fd_sc_ls": { + "CLOCK_PERIOD": 10, + "SYNTH_MAX_FANOUT": 5 + }, + "scl::sky130_fd_sc_ms": { + "CLOCK_PERIOD": 10 + } }, "pdk::gf180mcuC": { - "STD_CELL_LIBRARY": "gf180mcu_fd_sc_mcu7t5v0", - "FP_PDN_CHECK_NODES": 0, - "FP_PDN_ENABLE_RAILS": 0, - "RT_MAX_LAYER": "Metal4", - "DIE_AREA": "0 0 3000 3000", - "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper_gf180mcu.def", - "PL_OPENPHYSYN_OPTIMIZATIONS": 0, - "DIODE_INSERTION_STRATEGY": 0, - "MAGIC_WRITE_FULL_LEF": 0 + "STD_CELL_LIBRARY": "gf180mcu_fd_sc_mcu7t5v0", + "FP_PDN_CHECK_NODES": 0, + "FP_PDN_ENABLE_RAILS": 0, + "RT_MAX_LAYER": "Metal4", + "DIE_AREA": "0 0 3000 3000", + "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper_gf180mcu.def", + "PL_OPENPHYSYN_OPTIMIZATIONS": 0, + "DIODE_INSERTION_STRATEGY": 0, + "MAGIC_WRITE_FULL_LEF": 0 } -} + } \ No newline at end of file diff --git a/caravel_template/upw_post.v b/caravel_template/upw_post.v deleted file mode 100644 index cffc17f..0000000 --- a/caravel_template/upw_post.v +++ /dev/null @@ -1,4 +0,0 @@ - // end of module instantiation - -endmodule // user_project_wrapper -`default_nettype wire diff --git a/caravel_template/upw_pre.v b/caravel_template/upw_pre.v deleted file mode 100644 index 91a0446..0000000 --- a/caravel_template/upw_pre.v +++ /dev/null @@ -1,84 +0,0 @@ -// 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 -/* - *------------------------------------------------------------- - * - * user_project_wrapper - * - * This wrapper enumerates all of the pins available to the - * user for the user project. - * - * An example user project is provided in this wrapper. The - * example should be removed and replaced with the actual - * user project. - * - * THIS FILE HAS BEEN GENERATED USING multi_tools_project CODEGEN - * IF YOU NEED TO MAKE EDITS TO IT, EDIT codegen/caravel_iface_header.txt - * - *------------------------------------------------------------- - */ - -module user_project_wrapper #( - parameter BITS = 32 -)( -`ifdef USE_POWER_PINS - inout vdda1, // User area 1 3.3V supply - inout vdda2, // User area 2 3.3V supply - inout vssa1, // User area 1 analog ground - inout vssa2, // User area 2 analog ground - inout vccd1, // User area 1 1.8V supply - inout vccd2, // User area 2 1.8v supply - inout vssd1, // User area 1 digital ground - inout vssd2, // User area 2 digital ground -`endif - - // Wishbone Slave ports (WB MI A) - input wb_clk_i, - input wb_rst_i, - input wbs_stb_i, - input wbs_cyc_i, - input wbs_we_i, - input [3:0] wbs_sel_i, - input [31:0] wbs_dat_i, - input [31:0] wbs_adr_i, - output wbs_ack_o, - output [31:0] wbs_dat_o, - - // Logic Analyzer Signals - input [127:0] la_data_in, - output [127:0] la_data_out, - input [127:0] la_oenb, - - // IOs - input [`MPRJ_IO_PADS-1:0] io_in, - output [`MPRJ_IO_PADS-1:0] io_out, - output [`MPRJ_IO_PADS-1:0] io_oeb, - - // Analog (direct connection to GPIO pad---use with caution) - // Note that analog I/O is not available on the 7 lowest-numbered - // GPIO pads, and so the analog_io indexing is offset from the - // GPIO indexing by 7 (also upper 2 GPIOs do not have analog_io). - inout [`MPRJ_IO_PADS-10:0] analog_io, - - // Independent clock (on independent integer divider) - input user_clock2, - - // User maskable interrupt signals - output [2:0] user_irq -); - - // start of module instantiation diff --git a/configure.py b/configure.py index 9d21285..2a0a408 100755 --- a/configure.py +++ b/configure.py @@ -228,9 +228,7 @@ if __name__ == '__main__': with open('config.yaml') as fh: config = yaml.safe_load(fh) - # default max projects is row * col - 2 (scan controller takes first 2 slots) - config['num_projects'] = config['layout']['rows'] * config['layout']['cols'] - 2 - + parser.add_argument('--update-orders', help="update the order config file", action="store_const", const=True) parser.add_argument('--list', help="list projects", action='store_const', const=True) parser.add_argument('--clone-all', help="clone all projects", action="store_const", const=True) diff --git a/project.py b/project.py index aa94e97..979e275 100644 --- a/project.py +++ b/project.py @@ -54,12 +54,22 @@ class Project(): os.unlink(json_file) module_ports = ports['modules'][top]['ports'] - for port in ['io_in', 'io_out']: + required_ports = [ + ['clk', 1], + ['ena', 1], + ['rst_n', 1], + ['ui_in', 8], + ['uio_in', 8], + ['uio_oe', 8], + ['uio_out', 8], + ['uo_out', 8], + ] + for port, bits in required_ports: if port not in module_ports: logging.error(f"{self} {port} not found in top") exit(1) - if len(module_ports[port]['bits']) != 8: - logging.error(f"{self} {port} doesn't have 8 bits") + if len(module_ports[port]['bits']) != bits: + logging.error(f"{self} {port} doesn't have {bits} bits") exit(1) def check_num_cells(self):