diff --git a/fpga/tt_fpga_top.pcf b/fpga/tt_fpga_top.pcf new file mode 100644 index 0000000..3a52fd4 --- /dev/null +++ b/fpga/tt_fpga_top.pcf @@ -0,0 +1,68 @@ +# +# Pinmap for the TT ASIC SIMULATOR version 1.0 +# + + +# Clock +set_io -nowarn clk_fpga 37 # IOT_45a_G1 + + +# SPI +set_io -nowarn spi_mosi 14 +set_io -nowarn spi_miso 17 +set_io -nowarn spi_clk 15 +set_io -nowarn spi_cs_n 16 + +# USB +set_io -nowarn usb_dp 32 # IOT_43A +set_io -nowarn usb_dn 31 # IOT_42B +set_io -nowarn usb_pu 34 # IOT_44B + + +# Button +set_io -nowarn -pullup yes btn 13 # IOB_24A / boot button + + +# Leds +set_io -nowarn rgb[0] 39 +set_io -nowarn rgb[1] 40 +set_io -nowarn rgb[2] 41 + + +# "project clock" +set_io -nowarn clk 35 # IOT_46B_G0 +set_io -nowarn rst_n 38 # IOT_50B +set_io -nowarn gpio_led 36 # IOT_48B + + +# I/O +# pmod buttons +set_io -nowarn ui_in[0] 2 +set_io -nowarn ui_in[1] 3 +set_io -nowarn ui_in[2] 4 +set_io -nowarn ui_in[3] 6 +set_io -nowarn ui_in[4] 9 +set_io -nowarn ui_in[5] 10 +set_io -nowarn ui_in[6] 11 +set_io -nowarn ui_in[7] 12 + + +set_io -nowarn uo_out[0] 18 +set_io -nowarn uo_out[1] 20 +set_io -nowarn uo_out[2] 19 +set_io -nowarn uo_out[3] 21 +set_io -nowarn uo_out[4] 23 +set_io -nowarn uo_out[5] 26 +set_io -nowarn uo_out[6] 25 +set_io -nowarn uo_out[7] 28 + + + +set_io -nowarn uio[0] 27 +set_io -nowarn uio[1] 42 +set_io -nowarn uio[2] 44 +set_io -nowarn uio[3] 43 +set_io -nowarn uio[4] 46 +set_io -nowarn uio[5] 45 +set_io -nowarn uio[6] 48 +set_io -nowarn uio[7] 47 diff --git a/fpga/tt_fpga_top.v b/fpga/tt_fpga_top.v new file mode 100644 index 0000000..2b1c775 --- /dev/null +++ b/fpga/tt_fpga_top.v @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache 2.0 +// Copyright (C) 2024, Tiny Tapeout LTD + +`default_nettype none + +module tt_fpga_top ( + input wire [7:0] ui_in, + output wire [7:0] uo_out, + inout wire [7:0] uio, + input wire clk, + input wire rst_n +); + wire [7:0] uio_in; + wire [7:0] uio_out; + wire [7:0] uio_oe; + + SB_IO #( + .PIN_TYPE(6'b1010_01) + ) uio_pin[7:0] ( + .PACKAGE_PIN(uio), + .OUTPUT_ENABLE(uio_oe), + .D_OUT_0(uio_out), + .D_IN_0(uio_in), + ); + + __tt_um_placeholder user_project ( + .ui_in(ui_in), + .uo_out(uo_out), + .uio_in(uio_in), + .uio_out(uio_out), + .uio_oe(uio_oe), + .ena(1'b1), + .clk(clk), + .rst_n(rst_n) + ); + +endmodule diff --git a/project.py b/project.py index e62d3cd..9557f46 100644 --- a/project.py +++ b/project.py @@ -48,6 +48,8 @@ PINOUT_KEYS = [ "uio[7]", ] +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + with open(os.path.join(os.path.dirname(__file__), "tile_sizes.yaml"), "r") as stream: tile_sizes = yaml.safe_load(stream) @@ -504,6 +506,46 @@ class Project: os.chdir(cwd) + def create_fpga_bitstream(self): + logging.info(f"Creating FPGA bitstream for {self}") + + with open(os.path.join(SCRIPT_DIR, "fpga/tt_fpga_top.v"), "r") as f: + top_module_template = f.read() + + with open(os.path.join(self.src_dir, "_tt_fpga_top.v"), "w") as f: + f.write( + top_module_template.replace("__tt_um_placeholder", self.info.top_module) + ) + + build_dir = os.path.join(self.local_dir, "build") + os.makedirs(build_dir, exist_ok=True) + + sources = [os.path.join(self.src_dir, src) for src in self.sources] + source_list = " ".join(sources) + + yosys_cmd = f"yosys -l {build_dir}/01-synth.log -DSYNTH -p 'synth_ice40 -top tt_fpga_top -json {build_dir}/tt_fpga.json' src/_tt_fpga_top.v {source_list}" + logging.debug(yosys_cmd) + p = subprocess.run(yosys_cmd, shell=True) + if p.returncode != 0: + logging.error("synthesis failed") + exit(1) + + nextpnr_cmd = f"nextpnr-ice40 -l {build_dir}/02-nextpnr.log --pcf-allow-unconstrained --seed 10 --freq 48 --package sg48 --up5k --asc {build_dir}/tt_fpga.asc --pcf {SCRIPT_DIR}/fpga/tt_fpga_top.pcf --json {build_dir}/tt_fpga.json" + logging.debug(nextpnr_cmd) + p = subprocess.run(nextpnr_cmd, shell=True) + if p.returncode != 0: + logging.error("placement failed") + exit(1) + + icepack_cmd = f"icepack {build_dir}/tt_fpga.asc {build_dir}/tt_fpga.bin" + logging.debug(icepack_cmd) + p = subprocess.run(icepack_cmd, shell=True) + if p.returncode != 0: + logging.error("bitstream creation failed failed") + exit(1) + + logging.info(f"Bitstream created successfully: {build_dir}/tt_fpga.bin") + # doc check # makes sure that the basic info is present def check_docs(self): @@ -531,10 +573,9 @@ class Project: template_args["uio"] = self.info.pinout.uio logging.info("Creating PDF") - script_dir = os.path.dirname(os.path.realpath(__file__)) - with open(os.path.join(script_dir, "docs/project_header.md")) as fh: + with open(os.path.join(SCRIPT_DIR, "docs/project_header.md")) as fh: doc_header = fh.read() - with open(os.path.join(script_dir, "docs/project_preview.md")) as fh: + with open(os.path.join(SCRIPT_DIR, "docs/project_preview.md")) as fh: doc_template = fh.read() info_md = os.path.join(self.local_dir, "docs/info.md") with open(info_md) as fh: @@ -743,12 +784,11 @@ class Project: # Print the summaries def summarize(self): cell_count = self.get_cell_counts_from_gl() - script_dir = os.path.dirname(os.path.realpath(__file__)) Categories = typing.TypedDict( "Categories", {"categories": typing.List[str], "map": typing.Dict[str, int]} ) - with open(os.path.join(script_dir, "categories.json")) as fh: + with open(os.path.join(SCRIPT_DIR, "categories.json")) as fh: categories: Categories = json.load(fh) defs = load_cells() diff --git a/tt_tool.py b/tt_tool.py index 3f2f4e5..2475d90 100755 --- a/tt_tool.py +++ b/tt_tool.py @@ -104,6 +104,14 @@ if __name__ == "__main__": const=True, ) + # FPGA + parser.add_argument( + "--create-fpga-bitstream", + help="create the FPGA bitstream", + action="store_const", + const=True, + ) + args = parser.parse_args() # setup log @@ -157,3 +165,6 @@ if __name__ == "__main__": if args.create_svg: project.create_svg() + + if args.create_fpga_bitstream: + project.create_fpga_bitstream()