diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 00000000..2e68ce94 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 117c0c5490c7a38a33b72ed05e1d75dd +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle new file mode 100644 index 00000000..b13b0673 Binary files /dev/null and b/.doctrees/environment.pickle differ diff --git a/.doctrees/index.doctree b/.doctrees/index.doctree new file mode 100644 index 00000000..b38d1fc1 Binary files /dev/null and b/.doctrees/index.doctree differ diff --git a/.doctrees/xc9500/bitstream-xc9500.doctree b/.doctrees/xc9500/bitstream-xc9500.doctree new file mode 100644 index 00000000..1a0aefd7 Binary files /dev/null and b/.doctrees/xc9500/bitstream-xc9500.doctree differ diff --git a/.doctrees/xc9500/bitstream-xc9500xl.doctree b/.doctrees/xc9500/bitstream-xc9500xl.doctree new file mode 100644 index 00000000..0b639d5c Binary files /dev/null and b/.doctrees/xc9500/bitstream-xc9500xl.doctree differ diff --git a/.doctrees/xc9500/index.doctree b/.doctrees/xc9500/index.doctree new file mode 100644 index 00000000..e912e814 Binary files /dev/null and b/.doctrees/xc9500/index.doctree differ diff --git a/.doctrees/xc9500/intro.doctree b/.doctrees/xc9500/intro.doctree new file mode 100644 index 00000000..a06020dd Binary files /dev/null and b/.doctrees/xc9500/intro.doctree differ diff --git a/.doctrees/xc9500/jtag.doctree b/.doctrees/xc9500/jtag.doctree new file mode 100644 index 00000000..75fb0240 Binary files /dev/null and b/.doctrees/xc9500/jtag.doctree differ diff --git a/.doctrees/xc9500/structure.doctree b/.doctrees/xc9500/structure.doctree new file mode 100644 index 00000000..8063ded9 Binary files /dev/null and b/.doctrees/xc9500/structure.doctree differ diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 00000000..ebca11ba --- /dev/null +++ b/_sources/index.rst.txt @@ -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/_sources/xc9500/bitstream-xc9500.rst.txt b/_sources/xc9500/bitstream-xc9500.rst.txt new file mode 100644 index 00000000..7f71b1cb --- /dev/null +++ b/_sources/xc9500/bitstream-xc9500.rst.txt @@ -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/_sources/xc9500/bitstream-xc9500xl.rst.txt b/_sources/xc9500/bitstream-xc9500xl.rst.txt new file mode 100644 index 00000000..fb8d18d4 --- /dev/null +++ b/_sources/xc9500/bitstream-xc9500xl.rst.txt @@ -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/_sources/xc9500/index.rst.txt b/_sources/xc9500/index.rst.txt new file mode 100644 index 00000000..87f80ec7 --- /dev/null +++ b/_sources/xc9500/index.rst.txt @@ -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/_sources/xc9500/intro.rst.txt b/_sources/xc9500/intro.rst.txt new file mode 100644 index 00000000..b48d096c --- /dev/null +++ b/_sources/xc9500/intro.rst.txt @@ -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/_sources/xc9500/jtag.rst.txt b/_sources/xc9500/jtag.rst.txt new file mode 100644 index 00000000..f925a078 --- /dev/null +++ b/_sources/xc9500/jtag.rst.txt @@ -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/_sources/xc9500/structure.rst.txt b/_sources/xc9500/structure.rst.txt new file mode 100644 index 00000000..133e2bb3 --- /dev/null +++ b/_sources/xc9500/structure.rst.txt @@ -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 diff --git a/_static/alabaster.css b/_static/alabaster.css new file mode 100644 index 00000000..517d0b29 --- /dev/null +++ b/_static/alabaster.css @@ -0,0 +1,703 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 00000000..30fee9d0 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 00000000..d06a71d7 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 00000000..7e4c114f --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 00000000..250f5665 --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 00000000..d96755fd Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 00000000..7107cec9 Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 00000000..57c7df37 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000 } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 00000000..7918c3fa --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 00000000..8a96c69a --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '
' + + '' + + _("Hide Search Matches") + + "
" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/genindex.html b/genindex.html new file mode 100644 index 00000000..c9c857f9 --- /dev/null +++ b/genindex.html @@ -0,0 +1,102 @@ + + + + + + ++ Searching for multiple words only shows matches that contain + all words. +
+ + + + + + +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.
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
+
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:
+FB[i].MC[j].PT[k].IM[l].P
is stored at:
row: l * 2
column: k + (j % 3) * 5
bit: j // 3
FB[i].MC[j].PT[k].IM[l].N
is stored at:
row: l * 2
column: k + (j % 3) * 5
bit: j // 3
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
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.
+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
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.
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
The FB[i].IM[j].UIM.FB[k].MC[l]
fuse is stored at:
+++
+- +
subarea:
k
- +
row:
l
- +
column:
j % 5
- +
bit:
j // 5
The main differences from XC9500 are:
+The UIM wire-AND area is completely gone, only the main areas exist.
The main area has 108 rows per FB instead of 72.
Unprogrammed fuse state is 0
, programmed fuse state is 1
.
+Thus, the sense of every bitstream bit is inverted from the XC9500 version.
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.
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
+
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):
+FB[i].MC[j].PT[k].IM[l].P
is stored at:
row: l * 2
column: k + (j % 3) * 5
bit: j // 3
FB[i].MC[j].PT[k].IM[l].N
is stored at:
row: l * 2
column: k + (j % 3) * 5
bit: j // 3
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
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.
+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
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
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:
+XC9500: 5V core logic, 3.3V or 5V I/O, original version.
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
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
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.
+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.
+The IR is 8 bits long. The following instructions exist:
+IR |
+Instruction |
+Register |
+Notes |
+
---|---|---|---|
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
+XC9500XL/XV only |
+
|
+
|
+
|
++ |
|
+
|
+
|
+XC9500XL/XV only |
+
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
+XC9500 only |
+
|
+
|
+
|
+XC9500XL/XV only |
+
|
+
|
+
|
+XC9500 only |
+
|
+
|
+
|
+XC9500XL/XV only |
+
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
+XC9500XL/XV only |
+
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
++ |
|
+
|
+
|
++ |
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
+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
+The following DR registers exist on XC9500:
+ISPENABLE
(16 bits): function and contents unclear
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
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
+The following DR registers exist on XC9500XL/XV:
+ISPENABLE
(6 bits): function and contents unclear
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
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
ISPADDRESS
(18 bits): a subset of ISPCONFIGURATION
used by some instructions:
+++
+- +
bit 0: valid bit
- +
bit 1: strobe bit
- +
bits 2-17: address
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
+Todo
+write me
+Todo
+write me
+Todo
+write me
+Todo
+write me
+Todo
+write me
+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
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
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
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
+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.
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
+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
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)
+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)
+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
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
+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
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
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 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
+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
+