diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 00000000..cc8f14bf --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,28 @@ +on: push +name: Document +jobs: + document: + runs-on: ubuntu-latest + steps: + - name: Check out source code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install dependencies + run: | + python -m pip install --upgrade setuptools wheel + pip install -r docs/requirements.txt + - name: Build Sphinx documentation + run: | + sphinx-build docs/ docs/_build/ + touch docs/_build/.nojekyll + - name: Publish documentation + if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: docs/_build/ + CLEAN: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 96ef6c0b..614ac109 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/docs/_build /target Cargo.lock diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..11d96cc2 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,15 @@ +project = 'Project Combine' +copyright = '2023, Wanda' +author = 'Wanda' + +extensions = [ + 'sphinx.ext.todo', +] + +todo_include_todos = True + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..ebca11ba --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,17 @@ +Project Combine +############### + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + xc9500/index + + + +.. Indices and tables +.. ================== + +.. * :ref:`genindex` +.. * :ref:`modindex` +.. * :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..32bb2452 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..f0d97493 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx~=7.2 +sphinx-rtd-theme~=1.3 \ No newline at end of file diff --git a/docs/xc9500/bitstream-xc9500.rst b/docs/xc9500/bitstream-xc9500.rst new file mode 100644 index 00000000..7f71b1cb --- /dev/null +++ b/docs/xc9500/bitstream-xc9500.rst @@ -0,0 +1,415 @@ +Bitstream structure - XC9500 +############################ + +On a high level, the whole bitstream is split into "areas". Each FB +of the device corresponds two areas, one of which contains the UIM wire-AND +configuration, and the other (main area) contains everything else. + +The main area of a FB is made of 72 "rows". Each row is made of 15 "columns". +Each column is made of 6 or 8 bits: columns 0-8 are made of 8 bits, while +columns 9-14 are made of 6 bits. + +The low 6 bits of every column are used to store product term masks, and +the high 2 bits of columns 0-8 are used to store everything else. + +The UIM wire-AND area of a FB is, in turn, made of "subareas", one for each +FB of the device. Each subarea is in turn made of 18 rows. Each row +is made of 5 columns. Column 0 is made of 8 bits, while columns 1-4 are made +of 7 bits, making for 36 total bits per row. + +When programmed or read via JTAG, the bitstream is transmitted as bytes, +which are 6-8 bits long. Each byte of the bitstream has its address. +Not all addresses are valid, and valid addresses are not contiguous. +Address is 17 bits long, and is split to several fields: + +- bits 13-16: FB index +- bit 12: area kind + + - 0: main FB config + - 1: UIM wire-AND config + +- for main FB config area: + + - bits 5-11: row + - bits 3-4: column / 5 + - bits 0-2: column % 5 + +- for UIM wire-AND config area: + + - bits 8-11: subarea (source FB index) + - bits 3-7: row + - bits 0-2: column + +The unprogrammed state of a bit on XC9500 is ``1``. +The programmed state is ``0``. Thus, whenever a boolean fuse is mentioned +in the documentation, the "true" value is actually represented as ``0`` +in the bitstream. This includes the USERCODE bits. + + +JED format mapping +================== + +In the JED format, all fuses of the device are simply concatenated in order, +skipping over invalid addresses. The bytes are *not* padded to 8 bits, but +have their native size. Thus, converting from JED fuse index to device +address involves some complex calculations:: + + main_row_bits = 8 * 9 + 6 * 6 + uim_row_bits = 8 + 7 * 4 + main_area_bits = main_row_bits * 72 + uim_subarea_bits = uim_row_bits * 18 + uim_area_bits = uim_subarea_bits * device.num_fbs + fb_bits = main_area_bits + uim_area_bits + total_bits = fb_bits * device.num_fbs + + def jed_to_jtag(fuse): + fb = fuse // fb_bits + fuse %= fb_bits + if fuse < main_area_bits: + row = fuse // main_row_bits + fuse %= main_row_bits + if fuse < 8 * 9: + column = fuse // 8 + bit = fuse % 8 + else: + fuse -= 8 * 9 + column = 9 + fuse // 6 + bit = fuse % 6 + return ( + fb << 13 | + 0 << 12 | + row << 5 | + (column // 5) << 3 | + (column % 5) + ), bit + else: + fuse -= main_area_bits + subarea = fuse // uim_subarea_bits + fuse %= uim_subarea_bits + row = fuse // uim_row_bits + fuse %= uim_row_bits + if fuse < 8: + column = 0 + bit = fuse + else: + fuse -= 8 + column = 1 + fuse // 7 + bit = fuse % 7 + return ( + fb << 13 | + 1 << 12 | + subarea << 8 | + row << 3 | + column + ), bit + + def jtag_to_jed(addr, bit): + fb = addr >> 13 & 0xf + assert fb < device.num_fbs + if addr & (1 << 12): + row = addr >> 5 & 0x7f + assert row < 72 + col_hi = addr >> 3 & 3 + assert col_hi < 3 + col_lo = addr & 7 + assert col_lo < 5 + column = col_hi * 5 + col_lo + if column < 9: + cfuse = column * 8 + bit + else: + cfuse = 8 * 8 + (column - 9) * 6 + bit + return fb * fb_bits + row * main_row_bits + cfuse + else: + subarea = addr >> 8 & 0xf + assert subarea < device.num_fbs + row = addr >> 3 & 0x1f + assert row < 18 + column = addr & 7 + assert column < 5 + if column == 0: + cfuse = bit + else: + cfuse = 8 + (column - 1) * 7 + bit + return fb * fb_bits + main_area_bits + subarea * uim_subarea_bits + row * uim_row_bits + cfuse + + +Fuses — product terms +===================== + +The product term masks are stored in bits 0-5 of every column and every row of the main area. +The formulas are as follows: + +1. ``FB[i].MC[j].PT[k].IM[l].P`` is stored at: + + - row: ``l * 2`` + - column: ``k + (j % 3) * 5`` + - bit: ``j // 3`` + +2. ``FB[i].MC[j].PT[k].IM[l].N`` is stored at: + + - row: ``l * 2`` + - column: ``k + (j % 3) * 5`` + - bit: ``j // 3`` + + +Fuses — macrocells +================== + +Per-MC config fuses (that are not product term masks) are stored in bits 6-7 of +columns 0-8 of rows 12-54 of the main area. The formulas are as follows: + +- row: corresponds to fuse function +- column: ``mc_idx % 9`` +- bit: ``6 + mc_idx // 9`` + +The row is: + +- 12: ``PT[0].ALLOC`` bit 0 +- 13: ``PT[0].ALLOC`` bit 1 +- 14: ``PT[1].ALLOC`` bit 0 +- 15: ``PT[1].ALLOC`` bit 1 +- 16: ``PT[2].ALLOC`` bit 0 +- 17: ``PT[2].ALLOC`` bit 1 +- 18: ``PT[3].ALLOC`` bit 0 +- 19: ``PT[3].ALLOC`` bit 1 +- 20: ``PT[4].ALLOC`` bit 0 +- 21: ``PT[5].ALLOC`` bit 1 +- 22: ``INV`` +- 23: ``IMPORT_UP_ALLOC`` +- 24: ``IMPORT_DOWN_ALLOC`` +- 25: ``EXPORT_DIR`` +- 26: ``SUM_HP`` +- 27: ``IOB_OE_MUX`` bit 0 +- 28: ``IOB_OE_MUX`` bit 1 +- 29: ``OE_MUX`` bit 0 +- 30: ``OE_MUX`` bit 1 +- 31: ``OE_MUX`` bit 2 +- 32-34: unused? +- 35: ``OUT_MUX`` +- 36: ``CLK_MUX`` bit 0 +- 37: ``CLK_MUX`` bit 1 +- 38-39: unused +- 40: ``REG_MODE`` +- 41: ``RST_MUX`` +- 42: ``SET_MUX`` +- 43: ``INIT`` +- 44: ``UIM_OE_MUX`` bit 0 +- 45: ``UIM_OE_MUX`` bit 1 +- 46: ``UIM_OUT_INV`` +- 47: unused +- 48: ``GND`` +- 49: ``SLEW`` +- 50: ``PT[0].HP`` +- 51: ``PT[1].HP`` +- 52: ``PT[2].HP`` +- 53: ``PT[3].HP`` +- 54: ``PT[4].HP`` + +The fuse combination assignments are: + +- ``PT[*].ALLOC``: + + - ``11``: ``NONE`` + - ``10``: ``OR_MAIN`` + - ``01``: ``OR_EXPORT`` + - ``00``: ``SPECIAL`` + +- ``IMPORT_*_ALLOC``: + + - ``1``: ``OR_EXPORT`` + - ``0``: ``OR_MAIN`` + +- ``EXPORT_DIR``: + + - ``1``: ``UP`` + - ``0``: ``DOWN`` + +- ``IOB_OE_MUX``: + + - ``11``: ``GND`` + - ``10``: ``OE_MUX`` + - ``01``: ``VCC`` + +- ``OUT_MUX``: + + - ``1``: ``COMB`` + - ``0``: ``FF`` + +- ``OE_MUX``: + + - ``111``: ``PT`` + - ``110``: ``FOE0`` + - ``101``: ``FOE1`` + - ``100``: ``FOE2`` + - ``011``: ``FOE3`` + +- ``CLK_MUX``: + + - ``11``: ``FCLK1`` + - ``10``: ``FCLK2`` + - ``01``: ``FCLK0`` + - ``00``: ``PT`` + +- ``REG_MODE``: + + - ``1``: ``DFF`` + - ``0``: ``TFF`` + +- ``RST_MUX``, ``SET_MUX``: + + - ``1``: ``PT`` + - ``0``: ``FSR`` + +- ``UIM_OE_MUX``: + + - ``11``: ``OE_MUX`` + - ``10``: ``GND`` + - ``01``: ``VCC`` + +- ``SLEW``: + + - ``1``: ``SLOW`` + - ``0``: ``FAST`` + +- everything else: + + - ``1``: false + - ``0``: true + + +Fuses — FB inputs +================= + +The FB input mux configuraton is stored in rows 55-66, columns 0-8, bits 6-7. +The exact bit assignments are irregular and should be obtained from the database. + + +Fuses — per-FB bits and globals +=============================== + +Per-FB bits are stored in rows 67-68, columns 0-8, bits 6-7. The bits are (row, column, bit): + +- (67, 0, 6): ``ENABLE`` +- (67, 1, 6): ``EXPORT_ENABLE`` +- (68, 6, 6): ``PULLUP_DISABLE`` + +Global bits are stored in rows (0, 3, 4, 6, 7), columns 0-8, bits 6-7 of FB 0. The bits are (row, column, bit): + +- (0, 1, 6): ``FSR_INV`` +- (0, 2, 6): ``FCLK[0].INV`` +- (0, 3, 6): ``FCLK[1].INV`` +- (0, 4, 6): ``FCLK[2].INV`` +- (0, 5, 6): ``FOE[0].INV`` +- (0, 6, 6): ``FOE[1].INV`` +- (0, 7, 6): ``FOE[2].INV`` +- (0, 8, 6): ``FOE[3].INV`` +- (3, 2, 6): ``FCLK[0].MUX`` bit 0 +- (3, 3, 6): ``FCLK[1].MUX`` bit 0 +- (3, 4, 6): ``FCLK[2].MUX`` bit 0 +- (3, 5, 6): ``FOE[0].MUX`` bit 0 +- (3, 6, 6): ``FOE[1].MUX`` bit 0 +- (3, 7, 6): ``FOE[2].MUX`` bit 0 +- (3, 8, 6): ``FOE[3].MUX`` bit 0 +- (4, 2, 6): ``FCLK[0].MUX`` bit 1 +- (4, 3, 6): ``FCLK[1].MUX`` bit 1 +- (4, 4, 6): ``FCLK[2].MUX`` bit 1 +- (4, 5, 6): ``FOE[0].MUX`` bit 1 +- (4, 6, 6): ``FOE[1].MUX`` bit 1 +- (4, 7, 6): ``FOE[2].MUX`` bit 1 +- (4, 8, 6): ``FOE[3].MUX`` bit 1 +- (6, i, j) for i < 8, j in (6, 7): ``USERCODE`` bit ``16 + (7 - i) * 2 + (j - 6)`` +- (7, i, j) for i < 8, j in (6, 7): ``USERCODE`` bit ``(7 - i) * 2 + (j - 6)`` + +The fuse combination assignments are: + +- ``FCLK[0].MUX``: + + - ``11``: ``NONE`` + - ``10``: ``GCLK[1]`` + - ``01``: ``GCLK[0]`` + +- ``FCLK[1].MUX``: + + - ``11``: ``NONE`` + - ``10``: ``GCLK[2]`` + - ``01``: ``GCLK[1]`` + +- ``FCLK[2].MUX``: + + - ``11``: ``NONE`` + - ``10``: ``GCLK[0]`` + - ``01``: ``GCLK[2]`` + +- ``FOE[0].MUX`` (small device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[1]`` + - ``01``: ``GOE[0]`` + +- ``FOE[1].MUX`` (small device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[0]`` + - ``01``: ``GOE[1]`` + +- ``FOE[0].MUX`` (large device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[1]`` + - ``01``: ``GOE[0]`` + +- ``FOE[1].MUX`` (large device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[2]`` + - ``01``: ``GOE[1]`` + +- ``FOE[2].MUX`` (large device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[3]`` + - ``01``: ``GOE[2]`` + +- ``FOE[3].MUX`` (large device): + + - ``11``: ``NONE`` + - ``10``: ``GOE[0]`` + - ``01``: ``GOE[3]`` + +- ``USERCODE``: each bit stored inverted + +- everything else: + + - ``1``: false + - ``0``: true + + +Fuses — input buffer enable +=========================== + +On XC95288, the ``IBUF_ENABLE`` fuses are stored in rows (1, 2, 5, 8, 9), +columns 0-8, bits 6-7 of FBs (0, 1, 14, 15) in an irregular manner. Each +fuse is duplicated twice: once in FBs (0, 1) and once in FBs (14, 15). +The purpose of this duplication is unknown. Consult the database for exact +bit assignments. + + +Fuses — protection bits +======================= + +The protection bits are stored as follows (row, column, bit) in every FB: + +- (11, 3, 6): ``READ_PROT_A`` +- (68, 3, 6): ``READ_PROT_B`` +- (68, 0, 6): ``WRITE_PROT`` + + +Fuses — UIM wire-AND +==================== + +The ``FB[i].IM[j].UIM.FB[k].MC[l]`` fuse is stored at: + + - subarea: ``k`` + - row: ``l`` + - column: ``j % 5`` + - bit: ``j // 5`` diff --git a/docs/xc9500/bitstream-xc9500xl.rst b/docs/xc9500/bitstream-xc9500xl.rst new file mode 100644 index 00000000..fb8d18d4 --- /dev/null +++ b/docs/xc9500/bitstream-xc9500xl.rst @@ -0,0 +1,272 @@ +Bitstream structure — XC9500XL/XV +################################# + +The main differences from XC9500 are: + +1. The UIM wire-AND area is completely gone, only the main areas exist. +2. The main area has 108 rows per FB instead of 72. +3. Unprogrammed fuse state is ``0``, programmed fuse state is ``1``. + Thus, the sense of every bitstream bit is inverted from the XC9500 version. +4. While in XC9500 all areas are loaded sequentially, in XC9500XL/XV the areas + are loaded in parallel. Thus, the JTAG unit is not a byte, but a word of + size ``8 * num_fbs``. Likewise, the bytes for each FB are interleaved + in the JED format. + +On a high level, the whole bitstream is split into "areas". Each FB +of the device corresponds to one area. + +Each area is made of 108 "rows". Each row is made of 15 "columns". +Each column is made of 6 or 8 bits: columns 0-8 are made of 8 bits, while +columns 9-14 are made of 6 bits. + +The low 6 bits of every column are used to store product term masks, and +the high 2 bits of columns 0-8 are used to store everything else. + +When programmed or read via JTAG, the bitstream is transmitted as words. +Each word is 8 bits per FB. Each word of the bitstream has its address. +Not all addresses are valid, and valid addresses are not contiguous. +Address is 12 bits long, and is split to several fields: + +- bits 5-11: row +- bits 3-4: column / 5 +- bits 0-2: column % 5 + +The unprogrammed state of a bit on XC9500XL/XV is ``0``. +The programmed state is ``1``. Thus, whenever a boolean fuse is mentioned +in the documentation, the "true" value is actually represented as ``1`` +in the bitstream. + + +JED format mapping +================== + +In the JED format, all fuses of the device are simply concatenated in order, +skipping over invalid addresses. The bytes are *not* padded to 8 bits, but +have their native size. Thus, converting from JED fuse index to device +address involves some complex calculations:: + + row_bits = (8 * 9 + 6 * 6) * device.num_fbs + total_bits = row_bits * 108 + + def jed_to_jtag(fuse): + row = fuse // row_bits + fuse %= row_bits + if fuse < 8 * 9 * device.num_fbs: + column = fuse // (8 * device.num_fbs) + fuse %= (8 * device.num_fbs) + fb = fuse // 8 + bit = fuse % 8 + else: + fuse -= 8 * 9 * device.num_fbs + column = 9 + fuse // (6 * device.num_fbs) + fuse %= (6 * device.num_fbs) + fb = fuse // 6 + bit = fuse % 6 + return ( + row << 5 | + (column // 5) << 3 | + (column % 5) + ), (fb * 8 + bit) + + def jtag_to_jed(addr, bit): + fb = bit // 8 + bit %= 8 + row = addr >> 5 & 0x7f + assert row < 108 + col_hi = addr >> 3 & 3 + assert col_hi < 3 + col_lo = addr & 7 + assert col_lo < 5 + column = col_hi * 5 + col_lo + if column < 9: + cfuse = column * 8 * device.num_fbs + fb * 8 + bit + else: + cfuse = 8 * 8 + (column - 9) * 6 * device.num_fbs + fb * 6 + bit + return row * row_bits + cfuse + + +Fuses — product terms +===================== + +The product term masks are stored in bits 0-5 of every column and every row of the main area. +The formulas are as follows (unchanged from XC9500, but now with more rows): + +1. ``FB[i].MC[j].PT[k].IM[l].P`` is stored at: + + - row: ``l * 2`` + - column: ``k + (j % 3) * 5`` + - bit: ``j // 3`` + +2. ``FB[i].MC[j].PT[k].IM[l].N`` is stored at: + + - row: ``l * 2`` + - column: ``k + (j % 3) * 5`` + - bit: ``j // 3`` + + +Fuses — macrocells +================== + +Per-MC config fuses (that are not product term masks) are stored in bits 6-7 of +columns 0-8 of rows 12-49 of the main area. The formulas are as follows: + +- row: corresponds to fuse function +- column: ``mc_idx % 9`` +- bit: ``6 + mc_idx // 9`` + +The row is: + +- 12: ``PT[0].ALLOC`` bit 0 +- 13: ``PT[0].ALLOC`` bit 1 +- 14: ``PT[1].ALLOC`` bit 0 +- 15: ``PT[1].ALLOC`` bit 1 +- 16: ``PT[2].ALLOC`` bit 0 +- 17: ``PT[2].ALLOC`` bit 1 +- 18: ``PT[3].ALLOC`` bit 0 +- 19: ``PT[3].ALLOC`` bit 1 +- 20: ``PT[4].ALLOC`` bit 0 +- 21: ``PT[5].ALLOC`` bit 1 +- 22: ``INV`` +- 23: ``IMPORT_UP_ALLOC`` +- 24: ``IMPORT_DOWN_ALLOC`` +- 25: ``EXPORT_DIR`` +- 26: ``SUM_HP`` +- 27: ``OE_MUX`` bit 0 +- 28: ``OE_MUX`` bit 1 +- 29: ``OE_MUX`` bit 2 +- 30: ``OE_INV`` +- 31: unused +- 32: ``OUT_MUX`` +- 33: ``CLK_MUX`` bit 0 +- 34: ``CLK_MUX`` bit 1 +- 35: ``CLK_INV`` +- 36: ``CE_MUX`` bit 0 +- 37: ``CE_MUX`` bit 1 +- 38: unused +- 39: ``REG_MODE`` +- 40: ``RST_MUX`` +- 41: ``SET_MUX`` +- 42: ``INIT`` +- 43: ``GND`` +- 44: ``SLEW`` +- 45: ``PT[0].HP`` +- 46: ``PT[1].HP`` +- 47: ``PT[2].HP`` +- 48: ``PT[3].HP`` +- 49: ``PT[4].HP`` + +The fuse combination assignments are: + +- ``PT[*].ALLOC``: + + - ``00``: ``NONE`` + - ``01``: ``OR_MAIN`` + - ``10``: ``OR_EXPORT`` + - ``11``: ``SPECIAL`` + +- ``IMPORT_*_ALLOC``: + + - ``0``: ``OR_EXPORT`` + - ``1``: ``OR_MAIN`` + +- ``EXPORT_DIR``: + + - ``0``: ``UP`` + - ``1``: ``DOWN`` + +- ``OUT_MUX``: + + - ``0``: ``COMB`` + - ``1``: ``FF`` + +- ``CLK_MUX``: + + - ``00``: ``FCLK1`` + - ``01``: ``FCLK0`` + - ``10``: ``FCLK2`` + - ``11``: ``PT`` + +- ``CE_MUX``: + + - ``00``: ``NONE`` + - ``01``: ``PT2`` + - ``10``: ``PT3`` + +- ``REG_MODE``: + + - ``0``: ``DFF`` + - ``1``: ``TFF`` + +- ``RST_MUX``, ``SET_MUX``: + + - ``0``: ``PT`` + - ``1``: ``FSR`` + +- ``SLEW``: + + - ``0``: ``SLOW`` + - ``1``: ``FAST`` + +- everything else: + + - ``0``: false + - ``1``: true + + +Fuses — FB inputs +================= + +The FB input mux configuraton is stored in rows 50-76, columns 0-8, bits 6-7. +``FB[i].IM[j].MUX`` has 9 bits and is stored at the following coordinates: + +- row: ``50 + j % 27`` +- column: mux fuse index (0-8) +- bit: 6 if ``j < 27``, 7 otherwise + +The exact bit assignments are irregular and should be obtained from the database. + + +Fuses — per-FB bits and globals +=============================== + +Per-FB bits are stored in row 78, columns 0-8, bits 6-7. The bits are (row, column, bit): + +- (78, 0, 6): ``ENABLE`` +- (78, 1, 6): ``EXPORT_ENABLE`` +- (78, 6, 6): ``PULLUP_DISABLE`` + +Global bits are stored in rows (2, 6, 7), columns 0-8, bits 6-7 of FB 0. The bits are (row, column, bit): + +- (2, 0, 6): ``FSR_INV`` +- (2, 1, 6): ``FCLK[0].EN`` +- (2, 2, 6): ``FCLK[1].EN`` +- (2, 3, 6): ``FCLK[2].EN`` +- (2, 4, 6): ``FOE[0].EN`` +- (2, 5, 6): ``FOE[1].EN`` +- (2, 6, 6): ``FOE[2].EN`` +- (2, 7, 6): ``FOE[3].EN`` +- (2, 8, 6): ``KEEPER`` +- (6, i, j) for i < 8, j in (6, 7): ``USERCODE`` bit ``16 + (7 - i) * 2 + (j - 6)`` +- (7, i, j) for i < 8, j in (6, 7): ``USERCODE`` bit ``(7 - i) * 2 + (j - 6)`` + +The fuse combination assignments are: + +- ``USERCODE``: each bit stored directly + +- everything else: + + - ``0``: false + - ``1``: true + + +Fuses — protection bits, DONE +============================= + +The protection bits are stored as follows (row, column, bit) in every FB: + +- (11, 0, 6): ``WRITE_PROT`` +- (11, 3, 6): ``READ_PROT`` + +The ``DONE`` bit is stored only once, in FB 0: + +- (11, 6, 6): ``DONE`` diff --git a/docs/xc9500/index.rst b/docs/xc9500/index.rst new file mode 100644 index 00000000..87f80ec7 --- /dev/null +++ b/docs/xc9500/index.rst @@ -0,0 +1,12 @@ +XC9500, XC9500XL, XC9500XV +########################## + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + intro + structure + bitstream-xc9500 + bitstream-xc9500xl + jtag \ No newline at end of file diff --git a/docs/xc9500/intro.rst b/docs/xc9500/intro.rst new file mode 100644 index 00000000..b48d096c --- /dev/null +++ b/docs/xc9500/intro.rst @@ -0,0 +1,126 @@ +Introduction +############ + +XC9500 is a family of flash-based CPLDs manufactured by Xilinx. It is a derivative of the earlier XC7200 and XC7300 +EPLD families. It comes in three variants: + +1. XC9500: 5V core logic, 3.3V or 5V I/O, original version. +2. XC9500XL: 3.3V core logic, 2.5V or 3.3V I/O (5V tolerant), with some functional changes from XC9500: + + - UIM no longer has wired-AND functionality + - FFs have clock enable function + - 54 (instead of 36) UIM inputs per function block + - FF has configurable clock polarity + - FOE/FCLK multiplexers and inversion have been removed + - optional weak bus keeper can be configured for all pins + +3. XC9500XV: 2.5V core logic, 1.8V, 2.5V, or 3.3V I/O, with minor functional changes from XC9500XL: + + - larger devices have two I/O banks with separate VCCIO + - the bitstream now contains a ``DONE`` bit tied to ``ISC_DONE``, preventing problems with partially configured devices + + +Devices +======= + +The following devices exist: + +========= ======== =============== +Device Variant Function Blocks +========= ======== =============== +XC9536 XC9500 2 +XC9572 XC9500 4 +XC95108 XC9500 6 +XC95144 XC9500 8 +XC95216 XC9500 12 +XC95288 XC9500 16 +XC9536XL XC9500XL 2 +XC9572XL XC9500XL 4 +XC95144XL XC9500XL 8 +XC95288XL XC9500XL 16 +XA9536XL XC9500XL 2 +XA9572XL XC9500XL 4 +XA95144XL XC9500XL 8 +XC9536XV XC9500XV 2 +XC9572XV XC9500XV 4 +XC95144XV XC9500XV 8 +XC95288XV XC9500XV 16 +========= ======== =============== + +The parts starting with XA are automotive versions. They are functionally completely identical to corresponding XC versions. + + +Packages +======== + +The devices come in the following packages: + +- PLCC: + + - PC44 (JEDEC MO-047) + - PC84 (JEDEC MO-047) + +- QFP: + + - very thin QFP (1mm thickness): + + - VQ44 (0.8mm pitch; JEDEC MS-026-ACB) + - VQ64 (0.5mm pitch; JEDEC MS-026-ACD) + + - thin QFP (1.4mm thickness): + + - TQ100 (0.5mm pitch; JEDEC MS-026-BED) + - TQ144 (0.5mm pitch; JEDEC MS-026-BFB) + + - plastic QFP: + + - PQ100 (2.7mm thickness, non-square, 30×20 pins, 0.65mm pitch; JEDEC MS-022-GC1) + - PQ160 (3.4mm thickness, 0.65mm pitch; JEDEC MS-022-DD1) + - PQ208 (3.4mm thickness, 0.5mm pitch; JEDEC MS-029-FA-1) + + - platic QFP with heat sink: + + - HQ208 (same footprint as PQ208) + +- BGA: + + - standard BGA (1.27mm pitch): + + - BG256 (JEDEC MS-034-BAL-2) + - BG352 + + - fine-pitch BGA (1mm pitch): + + - FG256 + + - chip-scale BGA (0.8mm pitch): + + - CS48 + - CS144 (JEDEC MO-216-BAG-2) + - CS280 (JEDEC MO-216-BAL-1) + + +========= ==== ==== ==== ==== ===== ===== ===== ===== ===== ===== ===== ===== ===== ==== ===== ===== +Device PC44 PC84 VQ44 VQ64 TQ100 TQ144 PQ100 PQ160 PQ208 HQ208 BG256 BG352 FG256 CS48 CS144 CS280 +========= ==== ==== ==== ==== ===== ===== ===== ===== ===== ===== ===== ===== ===== ==== ===== ===== +XC9536 X X X +XC9572 X X X X +XC95108 X X X X +XC95144 X X X +XC95216 X X X +XC95288 X X +XC9536XL X X X X +XC9572XL X X X X X +XC95144XL X X X +XC95288XL X X X X X +XA9536XL X +XA9572XL X X X +XA95144XL X +XC9536XV X X X +XC9572XV X X X X +XC95144XV X X X +XC95288XV X X X X +========= ==== ==== ==== ==== ===== ===== ===== ===== ===== ===== ===== ===== ===== ==== ===== ===== + +Pin compatibility is maintained across all 3 variants of the XC9500 family within a single package. +Additionally, devices in PQ208 and HQ208 packages are pin compatible with each other. \ No newline at end of file diff --git a/docs/xc9500/jtag.rst b/docs/xc9500/jtag.rst new file mode 100644 index 00000000..f925a078 --- /dev/null +++ b/docs/xc9500/jtag.rst @@ -0,0 +1,171 @@ +JTAG interface +############## + +IR +== + +The IR is 8 bits long. The following instructions exist: + +============ ============ ==================== ======= +IR Instruction Register Notes +============ ============ ==================== ======= +``00000000`` ``EXTEST`` ``BOUNDARY`` +``00000001`` ``SAMPLE`` ``BOUNDARY`` +``00000010`` ``INTEST`` ``BOUNDARY`` +``11100101`` ``FBLANK`` ``ISPADDRESS`` XC9500XL/XV only +``11101000`` ``ISPEN`` ``ISPENABLE`` +``11101001`` ``ISPENC`` ``ISPENABLE`` XC9500XL/XV only +``11101010`` ``FPGM`` ``ISPCONFIGURATION`` +``11101011`` ``FPGMI`` ``ISPDATA`` +``11101100`` ``FERASE`` ``ISPCONFIGURATION`` XC9500 only +``11101100`` ``FERASE`` ``ISPADDRESS`` XC9500XL/XV only +``11101101`` ``FBULK`` ``ISPCONFIGURATION`` XC9500 only +``11101101`` ``FBULK`` ``ISPADDRESS`` XC9500XL/XV only +``11101110`` ``FVFY`` ``ISPCONFIGURATION`` +``11101111`` ``FVFYI`` ``ISPDATA`` +``11110000`` ``ISPEX`` ``BYPASS`` +``11111010`` ``CLAMP`` ``BYPASS`` XC9500XL/XV only +``11111100`` ``HIGHZ`` ``BYPASS`` +``11111101`` ``USERCODE`` ``USERCODE`` +``11111110`` ``IDCODE`` ``IDCODE`` +``11111111`` ``BYPASS`` ``BYPASS`` +============ ============ ==================== ======= + +The IR status is: + +- bit 0: const 1 +- bit 1: const 0 +- bit 2: ``WRITE_PROT`` status +- bit 3: ``READ_PROT`` status +- bit 4: ISP mode enabled +- bit 5: ``DONE`` status (XC9500XV only, const 0 on other devices) +- bits 6-7: const 0 + +.. todo:: verify + + +Boundary scan register +====================== + +The boundary scan register is ``3 * 18 * num_fbs`` bits long, and consists of 3 bits for every MC +in the device: input, output, and output enable. Such bits are included even for MCs that do not +have a corresponding IOB. + +The boundary register bit indices for ``FB[i].MC[j]`` are: + +- input: ``(num_fbs - 1 - i) * 18 * 3 + (17 - j) * 3 + 2`` +- output: ``(num_fbs - 1 - i) * 18 * 3 + (17 - j) * 3 + 1`` +- output enable: ``(num_fbs - 1 - i) * 18 * 3 + (17 - j) * 3 + 0`` + +All bits of the register are ``BC_1`` type cells. + +.. todo:: details on the cell connection, EXTEST, INTEST semantics + + +ISP instructions +================ + +ISP DR registers — XC9500 +------------------------- + +The following DR registers exist on XC9500: + +1. ``ISPENABLE`` (16 bits): function and contents unclear +2. ``ISPCONFIGURATION`` (27 bits): used to load and store bitstream bytes + + - bit 0: valid bit + - bit 1: strobe bit + - bits 2-9: data byte + - bits 10-26: address + +3. ``ISPDATA`` (10 bits): a subset of ``ISPCONFIGURATION`` used by instructions with autoincrementing address + + - bit 0: valid bit + - bit 1: strobe bit + - bits 2-9: data byte + +.. todo:: ISPENABLE, describe valid & strobe + + +ISP DR registers — XC9500XL/XV +------------------------------ + +The following DR registers exist on XC9500XL/XV: + +1. ``ISPENABLE`` (6 bits): function and contents unclear +2. ``ISPCONFIGURATION`` (``18 + num_fbs * 8`` bits): used to load and store bitstream words + + - bit 0: valid bit + - bit 1: strobe bit + - bits 2-``(num_sbs * 8 + 1)``: data word + - bits ``(num_fbs * 8 + 2)``-``(num_fbs * 8 + 17)``: address + +3. ``ISPDATA`` (``2 + num_fbs * 8`` bits): a subset of ``ISPCONFIGURATION`` used by instructions with autoincrementing address + + - bit 0: valid bit + - bit 1: strobe bit + - bits 2-``(num_sbs * 8 + 1)``: data word + +4. ``ISPADDRESS`` (18 bits): a subset of ``ISPCONFIGURATION`` used by some instructions: + + - bit 0: valid bit + - bit 1: strobe bit + - bits 2-17: address + + +Entering and exiting ISP mode +----------------------------- + +Before any programming or readout can be done, the device needs to be put into ISP mode. +For that purpose, the ``ISPEN`` or ``ISPENC`` (XC9500XL/XV only) instructions can be used. +Both instructions use the ``ISPENABLE`` register, which is 16 bits on XC9500 +and 6 bits on XC9500XL/XV. Its meaning, if any, is unknown. + +To enter ISP mode: + +- shift ``ISPEN`` or ``ISPENC`` into IR +- shift 0s into DR +- go to Run-Test/Idle state for at least 1 clock + +If the ``ISPEN`` instruction is used, all outputs will be put in high-Z with weak pull-ups while ISP mode is active. +If the ``ISPENC`` ("clamp" mode) instruction is used, all output and output enable signals will be snapshotted +and outputs will continue driving the last value while ISP mode is active. + +To exit ISP mode: + +- shift ``ISPEX`` into IR +- go to Run-Test/Idle state for at least 1 clock + +When ISP mode is exitted, the device will initialize itself and start normal operation. + +.. todo:: verify, see if anything can be figured out about the DR + + +Erasing fuses +------------- + +.. todo:: write me + + +Programming fuses +----------------- + +.. todo:: write me + + +Reading fuses +------------- + +.. todo:: write me + + +Blank check +----------- + +.. todo:: write me + + +Programming sequence +==================== + +.. todo:: write me \ No newline at end of file diff --git a/docs/xc9500/structure.rst b/docs/xc9500/structure.rst new file mode 100644 index 00000000..133e2bb3 --- /dev/null +++ b/docs/xc9500/structure.rst @@ -0,0 +1,622 @@ +Device structure +################ + +Overview +======== + +An XC9500 family device is made of: + +- the UIM (universal interconnect matrix), which routes MC and IOB outputs to FB inputs +- 2-16 FBs (function blocks), each of which has: + + - 36 (XC9500) or 54 (XC9500XL/XV) routable inputs from UIM + - 18 MCs (macrocells), each of which has: + + - configurable low power / high performance mode + - 5 PTs (product terms) + - PT router, which can route PTs to: + + - the sum term for this MC + - the sum term for export into neighbouring MCs + - a special function (OE, RST, SET, CLK, CE, XOR depending on the PT) + + - PT export/import logic for borrowing PTs between neighbouring MCs + - a sum term + - a dedicated XOR gate + - optional inverter + - a flip-flop, with: + + - configurable DFF or TFF function + - configurable initial value + - clock (freely invertible on XC9500XL/XV), routable from FCLK or PT + - async reset, routable from FSR or PT + - async set, routable from FSR or PT + - (XC9500XL/XV only) clock enable, routable from PT + + - a single output (selectable from combinatorial or FF output), routed to IOB and UIM + - (XC9500 only) UIM output enable and inversion + - IOB (input/output buffer) (on larger devices, not all macrocells have an IOB), with: + + - input buffer (routed to UIM) + - output enable (freely invertible on XC9500XL/XV), routable from FOE or PT + - configurable slew rate (fast or slow) + - programmable ground + +- global signals + + - 3 FCLK (fast clock) signals + + - (XC9500) freely invertible and routable from GCLK pins + - (XC9500XL/XV) hardwired 1-1 to GCLK pins + + - 1 FSR (fast set/reset) signal + + - freely invertible + - always routed from GSR pin + + - 2-4 FOE (fast output enable) signals + + - (XC9500) freely invertible and routable from GOE pins + - (XC9500XL/XV) hardwired 1-1 to GOE pins + +- global pull-up enable (only meant to be used in unconfigured devices) +- (XC9500XL/XV) global bus keeper enable +- special global configuration bits + + - 32-bit standard JTAG USERCODE + - read protection enable + - write protection enable + - (XC9500XV only) DONE bit + + +UIM and FB inputs — XC9500 +========================== + +The core interconnect structure in XC9500 devices is the UIM, Universal Interconnect Matrix. +The name is quite appropriate, at least for internal signals: any FB input can be routed +to any MC output. More than that, any FB input can be routed to a *wire-AND* of an arbitrary +subset of MC outputs from the entire device. Together with the UIM OE functionality within +the MC, this can be used for emulated internal tri-state buses. + +The name is, however, less appropriate when it comes to external signals: a given FB input +can only be routed to some subset of input signals from IOBs. The set of routable IOBs +depends on the FB input index and the device. + +Additionally, on devices other than XC9536, some FB inputs can be routed to "fast feedback" +paths, which come straight from MC outputs within the same FB. This is functionally redundant +with the wire-AND path, but much faster. + +Each FB has 36 inputs, which we call ``FB[i].IM[j]``. Each FB input is controlled by two sets of fuses: + +- mux fuses (``FB[i].IM[j].MUX``) select what is routed to the input. The combinations include: + + - ``EMPTY``: the input is a constant ??? (for unused inputs) + - ``UIM``: the input is a wire-AND of MC outputs (and the second set of fuses is relevant) + - ``FBK_MC{k}``: the input is routed through fast feedback path from ``FB[i].MC[k].OUT`` + - ``PAD_FB{k}_MC{l}``: the input is routed from an input buffer ``FB[k].MC[l].IOB.I`` + + The allowable combinations differ between inputs within a single FB, but don't differ across + FBs within a single device. In other words, the set of allowed values for these fuses + depends only on the ``j`` coordinate, but not on ``i``. + +- wire-AND fuses (``FB[i].IM[j].UIM.FB[k].MC[l]``) select which MC outputs participate in the + wire-AND. If a given fuse is programmed, it means that ``FB[k].MC[l].OUT_UIM`` is included + in the product. These fuses are only relevant when the mux fuse set is set to ``UIM``. + +.. todo:: check ``EMPTY`` semantics + +.. todo:: verify ``FBK`` doesn't go through OE and inversion + + +UIM and FB inputs — XC9500XL/XV +=============================== + +The core interconnect structure in XC9500XL/XV devices is the UIM 2. This version of the UIM +is not really universal, and is much more of a classic CPLD design. + +Each FB has 54 inputs, which we call ``FB[i].IM[j]``. Each FB input is controlled by a set of fuses: + +- mux fuses (``FB[i].IM[j].MUX``) select what is routed to the input. The combinations include: + + - ``EMPTY``: the input is a constant ??? (for unused inputs) + - ``FB{k}_MC{l}``: the input is routed from the macrocell output ``FB[k].MC[l].OUT`` + - ``PAD_FB{k}_MC{l}``: the input is routed from the input buffer ``FB[k].MC[l].IOB.I`` + + The allowable combinations differ between inputs within a single FB, but don't differ across + FBs within a single device. In other words, the set of allowed values for these fuses + depends only on the ``j`` coordinate, but not on ``i``. + +.. todo:: check ``EMPTY`` semantics + + +FB global fuses +=============== + +Each function block has two fuses controlling the entire FB: + +- ``FB[i].ENABLE``: function block enable; needs to be programmed to use this function block +- ``FB[i].EXPORT_ENABLE``: function block PT export enable; needs to be programmed to use PT + import/export within this function block + +.. todo:: determine what these do exactly + + +Product terms +============= + +Each function block has 90 product terms, 5 per macrocell. We call them ``FB[i].MC[j].PT[k]``. +Each of the 5 PTs has a dedicated function, as follows: + +- ``*.PT[0]``: clock +- ``*.PT[1]``: output enable +- ``*.PT[2]``: async reset (or clock enable on XC9500XL/XV) +- ``*.PT[3]``: async set (or clock enable on XC9500XL/XV) +- ``*.PT[4]``: second XOR input + +The inputs to all product terms within a FB are the same, and consist of all FB inputs, in both +true and inverted forms. + +Each product term can be individually configured as low power or high performance. This affects propagation time. + +Each product term can be routed to at most one of three destinations: + +- ``OR_MAIN``: input to the MC's sum term +- ``OR_EXPORT``: input to the export OR gate +- ``SPECIAL``: used for the dedicated function + +The fuses controlling a product term are: + +- ``FB[i].MC[j].PT[k].IM[l].P``: if programmed, ``FB[i].IM[l]`` is included in the product term (true polarity) +- ``FB[i].MC[j].PT[k].IM[l].N``: if programmed, ``~FB[i].IM[l]`` is included in the product term (inverted polarity) +- ``FB[i].MC[j].PT[k].HP``: if programmed, the product term is in high performance mode; otherwise, it is in low power mode +- ``FB[i].MC[j].PT[k].ALLOC``: has one of four values: + + - ``EMPTY``: product term is unused + - ``OR_MAIN``: product term is used for MC's sum term; dedicated function wired to 0 + - ``OR_EXPORT`` product term is used for export sum term; dedicated function wired to 0 + - ``SPECIAL``: product term is used for the dedicated function + +The product term's corresponding dedicated function is called ``FB[i].MC[j].PT[k].SPECIAL``. +It is equal to ``FB[i].MC[j].PT[k]`` if the dedicated function is enabled in ``ALLOC``, 0 otherwise. + +PT import/export +---------------- + +Product terms can be borrowed between neighbouring MCs within a FB. To this end, each MC has two outputs, +``FB[i].MC[j].EXPORT_{UP|DOWN}``, and two inputs, ``FB[i].MC[j].IMPORT_{UP|DOWN}``. The "down" direction +corresponds to exporting PTs toward lower-numbered MCs (with a wraparound from 0 to 17), and the "up" +direction corresponds to exporting PTs towards higher-numbered MCs (with a wraparound from 17 to 0). +Accordingly, we have:: + + FB[i].MC[j].IMPORT_DOWN = FB[i].MC[(j + 1) % 18].EXPORT_DOWN; + FB[i].MC[j].IMPORT_UP = FB[i].MC[(j - 1) % 18].EXPORT_UP; + +A MC can only export product terms in one direction (up or down). +Product terms can be imported from both directions at once. +Imported terms from a given direction can be used for either the main sum term, or for further export, but not both at once. + +PT import/export is controlled by the following per-MC fuses: + +- ``FB[i].MC[j].EXPORT_DIR``: one of: + + - ``DOWN``: exports PTs downwards + - ``UP``: exports PTs upwards + +- ``FB[i].MC[j].IMPORT_UP_ALLOC``: one of: + + - ``OR_MAIN``: includes PTs imported upwards in the main sum term + - ``OR_EXPORT``: includes PTs imported upwards in the export sum term + +- ``FB[i].MC[j].IMPORT_DOWN_ALLOC``: one of: + + - ``OR_MAIN``: includes PTs imported downwards in the main sum term + - ``OR_EXPORT``: includes PTs imported downwards in the export sum term + +Additionally, the per-FB ``FB[i].EXPORT_ENABLE`` fuse needs to be set if any term within a FB is exported or imported. + +Export works as follows:: + + FB[i].MC[j].EXPORT = + (FB[i].MC[j].IMPORT_UP_ALLOC == OR_EXPORT ? FB[i].MC[j].IMPORT_UP : 0) | + (FB[i].MC[j].IMPORT_DOWN_ALLOC == OR_EXPORT ? FB[i].MC[j].IMPORT_DOWN : 0) | + (FB[i].MC[j].PT[0].ALLOC == OR_EXPORT ? FB[i].MC[j].PT[0] : 0) | + (FB[i].MC[j].PT[1].ALLOC == OR_EXPORT ? FB[i].MC[j].PT[1] : 0) | + (FB[i].MC[j].PT[2].ALLOC == OR_EXPORT ? FB[i].MC[j].PT[2] : 0) | + (FB[i].MC[j].PT[3].ALLOC == OR_EXPORT ? FB[i].MC[j].PT[3] : 0) | + (FB[i].MC[j].PT[4].ALLOC == OR_EXPORT ? FB[i].MC[j].PT[4] : 0); + + FB[i].MC[j].EXPORT_UP = (FB[i].MC[j].EXPORT_DIR == UP ? FB[i].MC[j].EXPORT : 0); + FB[i].MC[j].EXPORT_DOWN = (FB[i].MC[j].EXPORT_DIR == DOWN ? FB[i].MC[j].EXPORT : 0); + +.. todo:: verify all of the above + + +Sum term, XOR gate +================== + +Each macrocell has a main sum term, which includes all product terms and imports routed towards it:: + + FB[i].MC[j].SUM = + (FB[i].MC[j].IMPORT_UP_ALLOC == OR_MAIN ? FB[i].MC[j].IMPORT_UP : 0) | + (FB[i].MC[j].IMPORT_DOWN_ALLOC == OR_MAIN ? FB[i].MC[j].IMPORT_DOWN : 0) | + (FB[i].MC[j].PT[0].ALLOC == OR_MAIN ? FB[i].MC[j].PT[0] : 0) | + (FB[i].MC[j].PT[1].ALLOC == OR_MAIN ? FB[i].MC[j].PT[1] : 0) | + (FB[i].MC[j].PT[2].ALLOC == OR_MAIN ? FB[i].MC[j].PT[2] : 0) | + (FB[i].MC[j].PT[3].ALLOC == OR_MAIN ? FB[i].MC[j].PT[3] : 0) | + (FB[i].MC[j].PT[4].ALLOC == OR_MAIN ? FB[i].MC[j].PT[4] : 0); + +The sum term then goes through a XOR gate (whose other input is either 0 or a dedicated PT) and a programmable inverter:: + + FB[i].MC[j].XOR = FB[i].MC[j].SUM ^ FB[i].MC[j].PT[4].SPECIAL ^ FB[i].MC[j].INV; + +The fuses involved are: + +- ``FB[i].MC[j].SUM_HP``: if programmed, the sum term is in high performance mode; otherwise, it's in low power mode +- ``FB[i].MC[j].INV``: if programmed, the output of the XOR gate is further inverted + +Flip-flop +========= + +Each macrocell includes a flip-flop. It has: + +- configurable DFF or TFF function +- D or T input connected to the (potentially inverted) XOR gate output +- configurable initial value +- clock (freely invertible on XC9500XL/XV), routable from FCLK or ``PT[0]`` +- async reset, routable from FSR or ``PT[2]`` +- async set, routable from FSR or ``PT[3]`` +- (XC9500XL/XV only) clock enable, routable from ``PT[2]`` or ``PT[3]`` + +The fuses involved are: + +- ``FB[i].MC[j].CLK_MUX``: selects CLK input + + - ``PT``: product term 0 dedicated function + - ``FCLK[0-2]``: global ``FCLK[0-2]`` network + +- ``FB[i].MC[j].CLK_INV``: if programmed, the CLK input is inverted (ie. clock is negedge) (XC9500XL/XV only) + +- ``FB[i].MC[j].RST_MUX``: selects RST input + + - ``PT``: product term 2 dedicated function or 0; if PT 2 is used for clock enable, it will not be routed to RST (0 will be substituted) + - ``FSR``: global ``FSR`` network + +- ``FB[i].MC[j].SET_MUX``: selects SET input + + - ``PT``: product term 3 dedicated function or 0; if PT 3 is used for clock enable, it will not be routed to SET (0 will be substituted) + - ``FSR``: global ``FSR`` network + +- ``FB[i].MC[j].CE_MUX``: selects CE input (XC9500XL/XV only) + + - ``NONE``: const 1 + - ``PT2``: product term 2 dedicated function + - ``PT3``: product term 3 dedicated function + +- ``FB[i].MC[j].INIT``: if programmed, the initial value of the FF is 1; otherwise, it is 0 + +- ``FB[i].MC[j].REG_MODE``: selects FF mode + + - ``DFF`` + - ``TFF`` + +On XC9500, the FF works as follows:: + + case(FB[i].MC[j].CLK_MUX) + PT: FB[i].MC[j].CLK = FB[i].MC[j].PT[0].SPECIAL; + FCLK0: FB[i].MC[j].CLK = FCLK[0]; + FCLK1: FB[i].MC[j].CLK = FCLK[1]; + FCLK2: FB[i].MC[j].CLK = FCLK[2]; + endcase + + case(FB[i].MC[j].RST_MUX) + PT: FB[i].MC[j].RST = FB[i].MC[j].PT[2].SPECIAL; + FSR: FB[i].MC[j].RST = FSR; + endcase + + case(FB[i].MC[j].SET_MUX) + PT: FB[i].MC[j].SET = FB[i].MC[j].PT[3].SPECIAL; + FSR: FB[i].MC[j].SET = FSR; + endcase + + initial FB[i].MC[j].FF = FB[i].MC[j].INIT; + + // Pretend the usual synth/sim mismatch doesn't happen. + always @(posedge FB[i].MC[j].CLK, posedge FB[i].MC[j].RST_MUX, posedge FB[i].MC[j].SET_MUX) + if (FB[i].MC[j].RST_MUX) + FB[i].MC[j].FF = 0; + else if (FB[i].MC[j].SET_MUX) + FB[i].MC[j].FF = 1; + else + FB[i].MC[j].FF = FB[i].MC[j].XOR; + +On XC9500XL/XV, the FF works as follows:: + + case(FB[i].MC[j].CLK_MUX) + PT: FB[i].MC[j].CLK = FB[i].MC[j].PT[0].SPECIAL ^ FB[i].MC[j].CLK_INV; + FCLK0: FB[i].MC[j].CLK = FCLK[0] ^ FB[i].MC[j].CLK_INV; + FCLK1: FB[i].MC[j].CLK = FCLK[1] ^ FB[i].MC[j].CLK_INV; + FCLK2: FB[i].MC[j].CLK = FCLK[2] ^ FB[i].MC[j].CLK_INV; + endcase + + case(FB[i].MC[j].RST_MUX) + PT: FB[i].MC[j].RST = (FB[i].MC[j].CE_MUX == PT2 ? 0 : FB[i].MC[j].PT[2].SPECIAL); + FSR: FB[i].MC[j].RST = FSR; + endcase + + case(FB[i].MC[j].SET_MUX) + PT: FB[i].MC[j].SET = (FB[i].MC[j].CE_MUX == PT3 ? 0 : FB[i].MC[j].PT[3].SPECIAL); + FSR: FB[i].MC[j].SET = FSR; + endcase + + case(FB[i].MC[j].CE_MUX) + PT2: FB[i].MC[j].CE = FB[i].MC[j].PT[2].SPECIAL; + PT3: FB[i].MC[j].CE = FB[i].MC[j].PT[3].SPECIAL; + NONE: FB[i].MC[j].CE = 1; + endcase + + initial FB[i].MC[j].FF = FB[i].MC[j].INIT; + + // Pretend the usual synth/sim mismatch doesn't happen. + always @(posedge FB[i].MC[j].CLK, posedge FB[i].MC[j].RST_MUX, posedge FB[i].MC[j].SET_MUX) + if (FB[i].MC[j].RST_MUX) + FB[i].MC[j].FF = 0; + else if (FB[i].MC[j].SET_MUX) + FB[i].MC[j].FF = 1; + else if (FB[i].MC[j].CE) + FB[i].MC[j].FF = FB[i].MC[j].XOR; + +.. todo:: verify (in particular, CE vs RST/SET shenanigans) + + +Macrocell output — XC9500 +========================= + +The output of the macrocell can be selected from combinatorial (XOR gate output) or registered (FF output):: + + FB[i].MC[j].OUT = (FB[i].MC[j].OUT_MUX == COMB ? FB[i].MC[j].XOR : FB[i].MC[j].FF); + +The macrocell also has an output enable, which can be from a product term, or a global network. +It can be used for the IOB output buffer, for UIM output, or both:: + + case(FB[i].MC[j].OE_MUX) + PT: FB[i].MC[j].OE = FB[i].MC[j].PT[1].SPECIAL; + FOE0: FB[i].MC[j].OE = FOE[0]; + FOE1: FB[i].MC[j].OE = FOE[1]; + FOE2: FB[i].MC[j].OE = FOE[2]; + FOE3: FB[i].MC[j].OE = FOE[3]; + endcase + + case(FB[i].MC[j].UIM_OE_MUX) + GND: FB[i].MC[j].UIM_OE = 0; + VCC: FB[i].MC[j].UIM_OE = 1; + OE_MUX: FB[i].MC[j].UIM_OE = FB[i].MC[j].OE; + endcase + + case(FB[i].MC[j].IOB_OE_MUX) + GND: FB[i].MC[j].IOB_OE = 0; + VCC: FB[i].MC[j].IOB_OE = 1; + OE_MUX: FB[i].MC[j].IOB_OE = FB[i].MC[j].OE; + endcase + +The output is routed to up to three places: + +- this MC's IOB (``FB[i].MC[j].OUT``) +- this FB's inputs via the feedback path (``FB[i].MC[j].OUT``) +- the UIM (``FB[i].MC[j].OUT_UIM``) + +The output to UIM additionally goes through (emulated) output enable logic, which can be used +to implement emulated on-chip tristate buses in conjunction with the UIM wire-AND logic:: + + FB[i].MC[j].OUT_UIM = FB[i].MC[j].UIM_OE ? FB[i].MC[j].OUT ^ FB[i].MC[j].UIM_OUT_INV : 1; + +The fuses involved are: + +- ``FB[i].MC[j].OUT_MUX``: selects output mode + + - ``COMB``: combinatorial, the output is connected to XOR gate output + - ``FF``: registered, the output is connected to FF output + +- ``FB[i].MC[j].OE_MUX``: selects output enable source + + - ``PT``: product term 1 dedicated function or 0 + - ``FOE[0-3]``: global ``FOE[0-3]`` network + +- ``FB[i].MC[j].UIM_OE_MUX``: selects output enable for UIM output + + - ``GND``: const 0 + - ``VCC``: const 1 + - ``OE_MUX``: the input selected by the ``OE_MUX`` fuses + +- ``FB[i].MC[j].IOB_OE_MUX``: selects output enable for IOB output + + - ``GND``: const 0 + - ``VCC``: const 1 + - ``OE_MUX``: the input selected by the ``OE_MUX`` fuses + +- ``FB[i].MC[j].UIM_OUT_INV``: if programmed, the output to UIM is inverted + + +.. todo:: verify all of the above (in particular, feedback path vs OE and UIM out) + + +Macrocell output — XC9500XL/XV +============================== + +The output of the macrocell can be selected from combinatorial (XOR gate output) or registered (FF output):: + + FB[i].MC[j].OUT = (FB[i].MC[j].OUT_MUX == COMB ? FB[i].MC[j].XOR : FB[i].MC[j].FF); + +The output is routed to the UIM and this MC's IOB. + +The macrocell also has an output enable, which can be from a product term, or a global network, and can +be freely inverted. It is used for the IOB output buffer:: + + case(FB[i].MC[j].OE_MUX) + PT: FB[i].MC[j].IOB_OE = FB[i].MC[j].PT[1].SPECIAL ^ FB[i].MC[j].OE_INV; + FOE0: FB[i].MC[j].IOB_OE = FOE[0] ^ FB[i].MC[j].OE_INV; + FOE1: FB[i].MC[j].IOB_OE = FOE[1] ^ FB[i].MC[j].OE_INV; + FOE2: FB[i].MC[j].IOB_OE = FOE[2] ^ FB[i].MC[j].OE_INV; + FOE3: FB[i].MC[j].IOB_OE = FOE[3] ^ FB[i].MC[j].OE_INV; + endcase + +The fuses involved are: + +- ``FB[i].MC[j].OUT_MUX``: selects output mode + + - ``COMB``: combinatorial, the output is connected to XOR gate output + - ``FF``: registered, the output is connected to FF output + +- ``FB[i].MC[j].OE_MUX``: selects output enable source + + - ``PT``: product term 1 dedicated function or 0 + - ``FOE[0-3]``: global ``FOE[0-3]`` network + +- ``FB[i].MC[j].OE_INV``: if programmed, the output enable is inverted + + +Input/output buffer +=================== + +All I/O buffers (except dedicated JTAG pins) are associated with a macrocell. Not all MCs +have associated IOBs. + +The output buffer is controlled by the ``FB[i].MC[j].OUT`` and ``FB[i].MC[j].IOB_OE`` signals of the macrocell. +If the pad is supposed to be input only, the OE signal should be programmed to be always 0: + +- on XC9500, ``IOB_OE_MUX`` should be set to ``GND`` +- on XC9500XL/XV, ``OE_MUX`` should be set to ``PT``, ``OE_INV`` should be unset, and ``PT[1]`` should not be allocated to its dedicated function + +Likewise, if the pad is supposed to be always-on output, the OE signal should be programmed to be always 1: + +- on XC9500, ``IOB_OE_MUX`` should be set to ``VCC`` +- on XC9500XL/XV, ``OE_MUX`` should be set to ``PT``, ``OE_INV`` should be set, and ``PT[1]`` should not be allocated to its dedicated function + +The output slew rate is programmable between two settings, "fast" and "slow". + +Instead of being connected to MC output, an IOB can also be a "programmed ground", ie be set to always output a const 0 +regardless of the ``IOB_OE`` and ``OUT`` signals. In this case, ``IOB_OE`` should be 0. + +The input buffer is connected to the ``FB[i].MC[j].IOB.I`` network. On all devices other than XC95288 (non-XL/XV), +it is directly connected to the UIM. On XC95288, there are additional enable fuses for this connection. + +Each I/O buffer has the following fuses: + +- ``FB[i].MC[j].GND``: if programmed, this pin is "programmed ground" and will always output a const 0 +- ``FB[i].MC[j].SLEW``: selects slew rate, one of: + + - ``SLOW`` + - ``FAST`` + +- ``FB[i].MC[j].IBUF_ENABLE`` (XC95288 only): when programmed, the input buffer is active and connected to UIM + +.. todo:: figure out the IBUF_ENABLE thing + + +Configuration pull-ups +====================== + +Before the device is configured, all IOBs are configured with a very weak pull-up +resistor (XC9500) or a bus keeper (XC9500XL/XV). To disable this pull-up, a per-FB fuse +is used which is set in the bitstream: + +- ``FB[i].PULLUP_DISABLE``: if programmed, disables the pre-configuration pull-ups / bus keepers for all IOBs in this FB + + +XC9500XL/XV bus keeper +====================== + +The XC9500XL/XV have a weak bus keeper in each IOB. The keeper functionality can only be enabled +globally for all pads on the device, or not at all, via a global fuse: + +- ``KEEPER``: if programmed, the bus keeper is enabled on all IOBs + + +Global networks — XC9500 +======================== + +The device has several global networks for fast control signals. The networks are always driven by special pads +on the device (which can also be used as normal I/O). The networks are: + +- ``FCLK[0-2]``: clock +- ``FSR``: async set or reset +- ``FOE[0-1]`` (XC9536, XC9572, XC95108) or ``FOE[0-3]`` (XC95144, XC95216, XC95288): output enable + +The special pads are: + +- ``GCLK[0-2]``: clock +- ``GSR``: async set or reset +- ``GOE[0-1]`` (XC9536, XC9572, XC95108) or ``GOE[0-3]`` (XC95144, XC95216, XC95288): output enable + +The mapping of ``G*`` special pads to MCs depends on the device and, in at least one case, the package (!). + +The allowed mappings are: + +- ``FCLK0``: ``GCLK0``, ``GCLK1`` +- ``FCLK1``: ``GCLK1``, ``GCLK2`` +- ``FCLK2``: ``GCLK2``, ``GCLK0`` +- ``FSR``: ``GSR`` +- ``FOE0`` (XC9536, XC9572, XC95108): ``GOE0``, ``GOE1`` +- ``FOE1`` (XC9536, XC9572, XC95108): ``GOE0``, ``GOE1`` +- ``FOE0`` (XC95144, XC95216, XC95288): ``GOE0``, ``GOE1`` +- ``FOE1`` (XC95144, XC95216, XC95288): ``GOE1``, ``GOE2`` +- ``FOE2`` (XC95144, XC95216, XC95288): ``GOE2``, ``GOE3`` +- ``FOE3`` (XC95144, XC95216, XC95288): ``GOE3``, ``GOE0`` + +Additionally, all networks can be inverted from their source pins. + +The fuses involved are: + +- ``FCLK[i].MUX``: selects the input routed to ``FCLK[i]`` + + - ``NONE``: const ??? + - ``GCLK[i]``: the corresponding ``GCLK`` pad + +- ``FOE[i].MUX``: selects the input routed to ``FOE[i]`` + + - ``NONE``: const ??? + - ``FOE[i]``: the corresponding ``FOE`` pad + +- ``FCLK[i].INV``: if programmed, the ``GCLK`` pad is inverted before driving ``FCLK[i]`` network +- ``FSR.INV``: if programmed, the ``GSR`` pad is inverted before driving ``FSR`` network +- ``FOE[i].INV``: if programmed, the ``GOE`` pad is inverted before driving ``FOE[i]`` network + +.. todo:: consts + +.. todo:: no, really, what is it with the XC9572 GOE mapping varying between packages + + +Global networks — XC9500XL/XV +============================= + +Global networks on XC9500XL/XV work similarly, except there are no muxes and no inversion (except for ``FSR``). +Thus the ``GCLK[i]`` and ``GOE[i]`` pads are mapped 1-1 directly to ``FCLK[i]`` and ``FOE[i]`` networks, with only +an enable fuse. + +The fuses involved are: + +- ``FCLK[i].EN``: if programmed, the given ``FCLK`` network is active and connected to the ``GCLK[i]`` pad +- ``FOE[i].EN``: if programmed, the given ``FOE`` network is active and connected to the ``GOE[i]`` pad +- ``FSR.INV``: if programmed, the ``GSR`` pad is inverted before driving ``FSR`` network + +The ``FSR`` network is always enabled. + +.. todo:: what does disabled network do + + +Misc configuration +================== + +The devices also include the following misc fuses: + +- ``USERCODE``: 32-bit fuse set, readable via the JTAG USERCODE instruction + +- ``FB[i].READ_PROT_{A|B}`` (XC9500): if programmed, the device is read protected +- ``FB[i].READ_PROT`` (XC9500XL/XV): if programmed, the device is read protected +- ``FB[i].WRITE_PROT``: if programmed, the device is write protected (needs a special JTAG instruction sequence + to program/erase) +- ``DONE`` (XC9500XV only): used to mark a fully programmed device; if programmed, the device will complete its + boot sequence and activate I/O buffers; otherwise, all output buffers will be disabled + +Due to their special semantics, the protection fuses and the ``DONE`` fuse should be programmed last, after all other fuses in the bitstream. + +.. todo:: exact semantics of protection fuses \ No newline at end of file