// SPDX-License-Identifier: Apache-2.0 ; specs/widgets/resource-bars.t27 -- what a placed design uses of the XC7A200T, as bars ; Source of truth for public/widgets/resource-bars/ (gHashTag/trinity, apps/website). The page is ; written by scripts/widget-pages-from-spec.mjs from this file and hands every constant below to ; tool.js as window.T27_WIDGET. tool.js holds no English of its own: every word on screen is a ; SAY_ constant here, every number the tool relies on is a K_ constant. ; ASCII only (L3), English only (LANG-EN). ; WHAT IT DOES: one bar per resource -- six-input LUTs, flip-flops, CARRY4, slices, BRAM36, ; BRAM18, DSP48, bonded IOB, BUFGCTRL -- the used count against the part's total, with the ; percentage. Drop a routed nextpnr-xilinx JSON (the file --write writes) and it counts that ; design instead, in the browser with FileReader; nothing is sent anywhere. The IOB total depends ; on the package, which a routed JSON does not record, so the reader picks it. ; HOW IT COUNTS: LUT is the number of occupied LUT6 positions (a slice letter A-D whose 6LUT or ; 5LUT is used), because that is the unit AMD counts in DS180; nextpnr's own line counts LUT BELs ; (6LUT and 5LUT separately) and is shown beside it. FF counts SLICE_FFX cells, CARRY4 and the ; rest count their cells, slices count distinct SLICE sites, IOB counts PAD cells. ; DATA PROVENANCE: resources.json is written by scripts/widget-data/fpga-pnr.py from one real run ; made on 2026-10-04 (trinity_v3_jtaguart.v from gHashTag/trinity-fpga de4ebda367, yosys ; 0.67+post b8e7da6f40, nextpnr-xilinx 0.9.2-107-g7037c948 on the xc7a200tfbg676-1 chipdb, --freq ; 50 --seed 1); the exact commands ship in the file. That design uses 155 LUT6 positions (239 LUT ; BELs in nextpnr's count), 98 FF, 23 CARRY4, 60 slices, 5 IOB, 1 BUFGCTRL, no BRAM and no DSP. ; Totals are counted, not typed: the chipdb's BEL counts (nextpnr --report "available": ; SLICE_LUTX 269200, SLICE_FFX 269200, CARRY4 33650, RAMB36E1 365, RAMB18E1 730, DSP48E1 740, ; BUFGCTRL 32) and prjxray-db 517d66a383 (tilegrid.json site counts; package_pins.csv rows whose ; site is an IOB: 400 for fbg676, 285 for fbg484). fpga-pnr.py stops if the two sources disagree. ; All of them match AMD DS180 (7 Series Product Selection Guide) for the XC7A200T: 33,650 ; slices, 134,600 LUTs, 269,200 flip-flops, 365 36Kb block RAMs, 740 DSP slices, 400 and 285 ; user IO in the FBG676 and FBG484 packages. ; WHAT IT DOES NOT CLAIM: that a design which fits also meets timing or works; this counts ; placed cells only. Totals are the XC7A200T's: a dropped file from another part is counted ; against the 200T. MMCM, PLL, IDELAY and the other primitives are not drawn. ; phi^2 + 1/phi^2 = 3 | TRINITY module widget_resource_bars; pub const KIND : str = "widget-tool"; pub const ID : str = "resource-bars"; pub const TITLE : str = "Resource bars: LUT, FF, BRAM and DSP used on the XC7A200T"; pub const DESCRIPTION : str = "A real nextpnr-xilinx run counted against the XC7A200T's own totals, read from the chipdb and prjxray-db. Drop your routed JSON and it counts yours."; pub const IMAGE_ALT : str = "Eight bars on black, each a used count against the XC7A200T total: LUT 155 of 134,600, FF 98 of 269,200, CARRY4 23 of 33,650, slices 60 of 33,650, BRAM36 0 of 365, DSP48 0 of 740, IOB 5 of 400, BUFG 1 of 32. Headline: 155 of 134,600 LUTs, 0.12%."; pub const HOOK : str = "How much of a 200T does a UART-command blinker take? 0.12% of the LUTs. Drop your routed JSON and see your own bars."; pub const CATEGORY : str = "fpga"; pub const DATA_SOURCES : [5]str = ["gHashTag/trinity-fpga de4ebda367: fpga/openxc7-synth/trinity_v3_jtaguart.v and jtaguart.xdc", "nextpnr-xilinx 0.9.2-107-g7037c948 --chipdb xc7a200tfbg676-1.bin --report report.json: used and available BEL counts", "prjxray-db 517d66a383 artix7: xc7a200t/tilegrid.json site counts, xc7a200tfbg676-1 and xc7a200tfbg484-1 package_pins.csv", "AMD DS180 7 Series FPGAs Product Selection Guide: XC7A200T totals, cross-checked", "A routed nextpnr-xilinx JSON the reader drops or picks: read in the browser with FileReader, never uploaded"]; pub const READS_LOCAL_FILES : bool = true; pub const SENDS_NOTHING : bool = true; ; --- Numbers the tool relies on ------------------------------------------------------------ pub const K_DATA_FILE : str = "resources.json"; ; Rows in order: the key in resources.json "used" and "totals", and the XC7A200T total. pub const K_ROWS : [9]str = ["LUT", "FF", "CARRY4", "SLICE", "BRAM36", "BRAM18", "DSP48", "IOB", "BUFG"]; pub const K_TOTALS : [9]u32 = [134600, 269200, 33650, 33650, 365, 730, 740, 400, 32]; ; Index of IOB in K_ROWS: its total follows the package the reader picks. pub const K_IOB_ROW : u8 = 7; pub const K_PACKAGES : [2]str = ["fbg676", "fbg484"]; pub const K_PACKAGE_IOB : [2]u32 = [400, 285]; ; Chipdb BEL counts behind the totals. pub const K_SLICES : u32 = 33650; pub const K_LUT_BELS : u32 = 269200; pub const K_FF_BELS : u32 = 269200; pub const K_RAMB36 : u32 = 365; pub const K_RAMB18 : u32 = 730; ; Cell type prefixes per row, for a dropped file (the counting rule is in the header). pub const K_BRAM36_PREFIX : [3]str = ["RAMB36", "RAMBFIFO36", "FIFO36"]; pub const K_BRAM18_PREFIX : [2]str = ["RAMB18", "FIFO18"]; pub const K_DSP_PREFIX : str = "DSP48"; pub const K_BUFG_TYPES : [2]str = ["BUFGCTRL", "BUFG"]; ; A used count above zero gets at least this many pixels of bar, so 1 of 269200 still shows. pub const K_MIN_BAR_PX : u8 = 3; pub const K_MAX_FILE_MB : u16 = 512; ; --- Words on screen ------------------------------------------------------------------------ pub const SAY_ROW_NAMES : [9]str = ["LUT6", "Flip-flops", "CARRY4", "Slices", "BRAM36", "BRAM18", "DSP48", "Bonded IOB", "BUFGCTRL"]; pub const SAY_ROW_NOTES : [9]str = ["occupied LUT6 positions (A-D per slice)", "SLICE_FFX", "carry chain blocks", "distinct SLICE sites", "36Kb block RAM", "18Kb block RAM halves", "DSP48E1", "PAD cells, against the package", "global clock buffers"]; pub const SAY_USED_OF : str = "{0} / {1}"; pub const SAY_PCT : str = "{0}%"; pub const SAY_PACKAGE : str = "Package"; pub const SAY_DEMO : str = "Demo design"; pub const SAY_SUMMARY : str = "{0}: {1} cells, {2} of the LUTs"; pub const SAY_NEXTPNR_LINE : str = "nextpnr's own count: {0}"; pub const SAY_SOURCE_DEMO : str = "{0} on {1}, placed by {2}"; pub const SAY_SOURCE_FILE : str = "{0} ({1} bytes), {2}"; pub const SAY_TOTALS_NOTE : str = "Totals: chipdb BEL counts and prjxray-db, equal to AMD DS180 for the XC7A200T."; pub const SAY_DROP : str = "Drop a routed nextpnr-xilinx JSON here, or tap to pick one. It is read in this browser and sent nowhere."; pub const SAY_LOADING : str = "Loading the counts..."; pub const SAY_READING : str = "Reading {0}..."; pub const SAY_ERR_JSON : str = "{0} is not JSON."; pub const SAY_ERR_NO_BELS : str = "{0} has no placed cells: no NEXTPNR_BEL attributes. Use the JSON nextpnr writes with --write after routing."; pub const SAY_ERR_SIZE : str = "{0} is larger than {1} MB."; pub const SAY_ERR_DEMO : str = "The demo counts did not load."; pub const SAY_COMMANDS : str = "How this was made"; ; --- Tests ---------------------------------------------------------------------------------- test the_totals_follow_from_the_chipdb_counts { assert K_TOTALS[0] == K_LUT_BELS / 2; assert K_TOTALS[0] == 4 * K_SLICES; assert K_TOTALS[1] == K_FF_BELS; assert K_TOTALS[2] == K_SLICES; assert K_TOTALS[3] == K_SLICES; } test the_block_ram_halves_are_two_per_36kb { assert K_TOTALS[4] == K_RAMB36; assert K_TOTALS[5] == K_RAMB18; assert K_RAMB18 == 2 * K_RAMB36; } test the_iob_row_starts_on_the_first_package { assert K_ROWS[K_IOB_ROW] == "IOB"; assert K_TOTALS[K_IOB_ROW] == K_PACKAGE_IOB[0]; assert K_PACKAGE_IOB[1] < K_PACKAGE_IOB[0]; } test the_page_reads_files_and_sends_nothing { assert READS_LOCAL_FILES == true; assert SENDS_NOTHING == true; assert CATEGORY == "fpga"; }