Part 1: Background and Reverse Engineering the BLEs
Contents
- CLB Background
- Digging into the Datasheet
- Digging into the tool
- Digging into the backend files
- Decoding the BLE LUT Configuration
- Decoding the BLE LUT Input Configuration
- FLOPSEL
- Counter
- Inputs
- Outputs
- CLKDIV
- Final Config Table
CLB Background
The datasheet tells us that the CLB has 32 Basic Logic Elements (BLEs) that each have Look-Up Tables (LUTs). I generally use the terms interchangeably throughout this series of posts.
What is a LUT?
A LUT is the basic building block of an FPGA, it looks up a result based on the given input. The CLB uses a 4-LUT, a LUT with 4 inputs, as its base element.
A LUT does what it says on the box: based on the 4 inputs, it looks up an output value. It can be used to implement any 4-input boolean function, i.e., AND/OR/XOR/NOR, or even complex functions like an AND gate with the second input inverted and the third input overriding the 4th.
LUTs are typically also included with a latch, so they can be configured as various types of latches/flip-flops. When configured like this, it’s generally called a Basic Logic Element (BLE).
Digging into the Datasheet
The first step on our journey is to see what information Microchip does provide us about how the CLB works and what’s inside!.
Microchip’s Basic Logic Element
The first stop is there diagram of what’s inside a BLE:
Source: Microchip, PIC16F13145 Datasheet
Microchip’s BLE has a standard 4-input LUT with a D flip-flop. In the CLB, each BLE shares most of the inputs shown above. CLBCLK
is a global input to the CLB peripheral; CLBMD
/RESET
/EN
are all for the whole module.
Only the BLE Input A/B/C/D
and the BLE Flop select
are per BLE as well as the LUT config itself.
We can start our definition of what makes up the CLB (the Data Model) with this defining a python dataclass to hold our BLE config:
@dataclass
class BLE_CFG:
LUT_CONFIG: int # 16-bit
FLOPSEL: bool
LUT_I_A: LUT_IN_A
LUT_I_B: LUT_IN_B
LUT_I_C: LUT_IN_C
LUT_I_D: LUT_IN_D
BLE Input Selection
Next, they have an extremely helpful diagram showing what can be connected to every BLE input:
Source: Microchip, PIC16F13145 Datasheet
A couple of things to note:
- Each LUT input configuration is 5 bits for a total of 20 bits of configuration for the BLE inputs.
- The
CLB_BLE[n]
values are from the specified BLE, and not every BLE can go to every LUT input. This hints at the physical layout of the BLEs on the chip. - The
CLB_IN_SYNC[n]
inputs come from the 16 CLB module input signals (more on those later). - The
COUNTER[n]
inputs are from the built-in counter peripheral in the CLB (more on it later).
But luckily Microchip provides what the mappings from bits to inputs! This would have been a major pain to reverse engineer.
Let’s add it to our data model:
class LUT_IN_A(IntEnum):
"""BLE INPUT A[4:0]"""
CLB_BLE_0 = 0b00000
CLB_BLE_1 = 0b00001
CLB_BLE_2 = 0b00010
CLB_BLE_3 = 0b00011
# ...
CLBSWIN7 = 0b10011
COUNT_IS_A1 = 0b10100
COUNT_IS_A2 = 0b10101
# ...
Full BLE Input Data Model
For the eagle-eyed among you, you will notice I use IN0
below vs. the above CLB_IN_SYNC
. The reason will become apparent later.
class LUT_IN_A(IntEnum):
"""BLE INPUT A[4:0]"""
CLB_BLE_0 = 0b00000
CLB_BLE_1 = 0b00001
CLB_BLE_2 = 0b00010
CLB_BLE_3 = 0b00011
CLB_BLE_4 = 0b00100
CLB_BLE_5 = 0b00101
CLB_BLE_6 = 0b00110
CLB_BLE_7 = 0b00111
IN0 = 0b01000
IN1 = 0b01001
IN2 = 0b01010
IN3 = 0b01011
CLBSWIN0 = 0b01100
CLBSWIN1 = 0b01101
CLBSWIN2 = 0b01110
CLBSWIN3 = 0b01111
CLBSWIN4 = 0b10000
CLBSWIN5 = 0b10001
CLBSWIN6 = 0b10010
CLBSWIN7 = 0b10011
COUNT_IS_A1 = 0b10100
COUNT_IS_A2 = 0b10101
class LUT_IN_B(IntEnum):
"""BLE INPUT B[4:0]"""
CLB_BLE_8 = 0b00000
CLB_BLE_9 = 0b00001
CLB_BLE_10 = 0b00010
CLB_BLE_11 = 0b00011
CLB_BLE_12 = 0b00100
CLB_BLE_13 = 0b00101
CLB_BLE_14 = 0b00110
CLB_BLE_15 = 0b00111
IN4 = 0b01000
IN5 = 0b01001
IN6 = 0b01010
IN7 = 0b01011
CLBSWIN8 = 0b01100
CLBSWIN9 = 0b01101
CLBSWIN10 = 0b01110
CLBSWIN11 = 0b01111
CLBSWIN12 = 0b10000
CLBSWIN13 = 0b10001
CLBSWIN14 = 0b10010
CLBSWIN15 = 0b10011
COUNT_IS_B1 = 0b10100
COUNT_IS_B2 = 0b10101
class LUT_IN_C(IntEnum):
"""BLE INPUT C[4:0]"""
CLB_BLE_16 = 0b00000
CLB_BLE_17 = 0b00001
CLB_BLE_18 = 0b00010
CLB_BLE_19 = 0b00011
CLB_BLE_20 = 0b00100
CLB_BLE_21 = 0b00101
CLB_BLE_22 = 0b00110
CLB_BLE_23 = 0b00111
IN8 = 0b01000
IN9 = 0b01001
IN10 = 0b01010
IN11 = 0b01011
CLBSWIN16 = 0b01100
CLBSWIN17 = 0b01101
CLBSWIN18 = 0b01110
CLBSWIN19 = 0b01111
CLBSWIN20 = 0b10000
CLBSWIN21 = 0b10001
CLBSWIN22 = 0b10010
CLBSWIN23 = 0b10011
COUNT_IS_C1 = 0b10100
COUNT_IS_C2 = 0b10101
class LUT_IN_D(IntEnum):
"""BLE INPUT D[4:0]"""
CLB_BLE_24 = 0b00000
CLB_BLE_25 = 0b00001
CLB_BLE_26 = 0b00010
CLB_BLE_27 = 0b00011
CLB_BLE_28 = 0b00100
CLB_BLE_29 = 0b00101
CLB_BLE_30 = 0b00110
CLB_BLE_31 = 0b00111
IN12 = 0b01000
IN13 = 0b01001
IN14 = 0b01010
IN15 = 0b01011
CLBSWIN24 = 0b01100
CLBSWIN25 = 0b01101
CLBSWIN26 = 0b01110
CLBSWIN27 = 0b01111
CLBSWIN28 = 0b10000
CLBSWIN29 = 0b10001
CLBSWIN30 = 0b10010
CLBSWIN31 = 0b10011
COUNT_IS_D1 = 0b10100
COUNT_IS_D2 = 0b10101
Digging into the tool
The next step is to start poking around at the tool Microchip provides for configuring the CLB.
CLB Synthesizer

Source: Microchip, CLB Synthesizer
It’s called CLB Synthesizer. You can use their web version or the version that gets auto-installed by MPLAB X (their IDE); it turns out it’s just an NPM module. It is a drag and drop interface that lets you build logic designs and translate them into the corresponding CLB configuration.
How Does it Work?
CLB Synthesizer lets you build your circuit in its drag-and-drop interface (optionally also with a Verilog module), and the web frontend generates some Verilog and corresponding supporting files.
For example, the inverter example above generates the files shown below. Let’s dig in.
Verilog
The first file is the main.v
file that contains the body of the module you designed.
(* MUX0.CLBIN = 6'd0 *)
(* MUX0.INSYNC = 3'b100 *)
(* CLKDIV = 3'd1 *)
module main
(CLBIN0PPS_synchronized, PPS_OUT1);
input CLBIN0PPS_synchronized;
output PPS_OUT1;
logic net2;
not U1 (net2, CLBIN0PPS_synchronized);
assign PPS_OUT1 = net2;
endmodule
Constraints
The next file is main.xdc
. It’s a constraints file that, in the CLB synthesizer, primarily specifies the names of signals,
mapping them from internal names to more user-friendly ones.
set_property PACKAGE_PIN IN0 [get_ports CLBIN0PPS_synchronized]
set_property PACKAGE_PIN PPS_OUT1 [get_ports PPS_OUT1]
Here you can see we define the signal IN0
to be named CLBIN0PPS_synchronized
for use above.
Project files
The last few files are files that define the project; they map to the visual layout as well as some metadata about the design.
It does not appear to include any personal information beyond the software version used and the creation date.
design.clb
{
"backendversion": "25.3.1-4",
"clockDivider": 1,
"creationDate": "2023-09-26T12:59:39.897Z",
"device": "PIC16F13145",
"entryFile": "main",
"hdls": [
],
"mnmap": [
{
"id": "cd4ee2db-361f-4f1f-94dd-f6a50e67a83c",
"name": "main",
"type": 0
}
],
"name": "inverter",
"projectId": "6d4d6a22-1273-4bf4-a115-eed4f658bc43",
"schematics": [
{
"data": {
"class": "GraphLinksModel",
"linkDataArray": [
{
"from": 1,
"fromPort": "OUT",
"key": -1,
"points": [
-255,
-90,
-245,
-90,
-82,
-90,
-82,
-90,
-39,
-90,
-29,
-90
],
"to": 2,
"toPort": "IN1"
},
{
"from": 2,
"fromPort": "OUT",
"key": -2,
"points": [
51,
-90,
61,
-90,
153,
-90,
153,
-90,
285,
-90,
295,
-90
],
"to": 3,
"toPort": "IN"
}
],
"linkFromPortIdProperty": "fromPort",
"linkKeyProperty": "key",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{
"category": "inputport",
"inputmodifier": "synchronized",
"key": 1,
"loc": "-320 -90",
"portname": "CLBIN0PPS",
"size": "130 20"
},
{
"category": "not",
"key": 2,
"loc": "10 -90"
},
{
"category": "outputport",
"inputmodifier": "synchronized",
"key": 3,
"loc": "360 -90",
"portname": "PPS_OUT1",
"size": "130 20"
},
{
"category": "comment",
"key": "comment",
"loc": "-200 110",
"text": "Input Port"
}
]
},
"id": "cd4ee2db-361f-4f1f-94dd-f6a50e67a83c"
}
],
"type": "Microchip CLB Synthesizer Design",
"version": "1.0"
}
project.json
{
"creationDate": "2023-09-26T12:59:39.897Z",
"device": "PIC16F13145",
"interface": {
"inputs": [
"CLBIN0PPS"
],
"outputs": [
"PPS_OUT1"
]
},
"projectId": "6d4d6a22-1273-4bf4-a115-eed4f658bc43",
"topLevelFile": "main",
"type": "Microchip CLB Synthesizer Design",
"version": "0.2"
}
stats.json
{
"client": "web",
"clientVersion": "1.3.0-3",
"device": "PIC16F13145",
"projectId": "6d4d6a22-1273-4bf4-a115-eed4f658bc43",
"schematics": 1,
"symbols": {
"inputport": 2,
"not": 2,
"outputport": 2
},
"verilog": 0
}
Submit to Backend
The last step is to zip the files and submit them to the backend for synthesis.
Response
The backend responds with a JSON message containing a zip file that has a lot of interesting information.
{
"synthres": {
"formatversion": "0.1",
"clb": "pic16f131xx_v1",
"id": "1743199249251",
"bitstream": [
"0x3C00",
...
"0x0000"
],
"outputmappings": {},
"inputs": [
"CLBIN0PPS"
],
"outputs": [
"PPS_OUT1"
],
"resources": [
{
"type": "LUTs",
"count": 1,
"of": 32
}
],
"status": "success",
"errormessage": ""
},
"process": {
"returncode": 0,
"killed": false,
"stdout": "\nPerform synthesis:\n\n\n ...",
"stderr": ""
},
"zip": "UEsDBB..."
}
Zip content:
zip
│ bitstream.s
│ clb1.c
│ clb1.h
│ clb1_output_mappings.h
│ pre-routing.svg
│ readme.txt
│ routed.svg
│
├───build
│ bitstream.json
│ funfacts.tmp
│ out.fasm
│ out.net
│ out.net.post_routing
│ out.netlist
│ out.phys
│ out.place
│ out.route
│ packing_pin_util.rpt
│ pre-routing.dot
│ pre-routing.v
│ routed.dot
│ routed.v
│ stderr.txt
│ stdout.txt
│ synth.json
│ vpr_stdout.log
│ yosys.log
│
└───input
design.clb
main.v
main.xdc
project.json
stats.json
Lets dig in…
Digging into the backend files
We get quite a lot of information from the backend.
The first set of files we get are the outputs for you to load into the CLB on your microcontroller, including the raw bitstream:
│ bitstream.s
│ clb1.c
│ clb1.h
│ clb1_output_mappings.h
This bitstream has 102 words in it, 1428 bits to reverse engineer (this PIC has 14 bit words).
More interesting at the top level is routed.svg
:
routed.svg
This tells us quite a lot, like exactly what BLE is used, what inputs it uses, how it’s configured, etc.
Next, we can take a peek at the build
dir. stdout.txt
, it tells us that Microchip is using the
open source Yosys design chain with custom extensions for the CLB.
stdout.txt
Perform synthesis:
/----------------------------------------------------------------------------\
| |
| yosys -- Yosys Open SYnthesis Suite |
| |
| Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |
| |
| Permission to use, copy, modify, and/or distribute this software for any |
| purpose with or without fee is hereby granted, provided that the above |
| copyright notice and this permission notice appear in all copies. |
| |
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
\----------------------------------------------------------------------------/
Yosys 0.25 (git sha1 e02b7f64b, gcc 10.2.1-6 -fPIC -Os)
-- Running command ` read_verilog -sv -I../input ../input/main.v; synth_microchip -top main -rmports; scc; write_json synth.json' --
1. Executing Verilog-2005 frontend: ../input/main.v
Parsing SystemVerilog input from `../input/main.v' to AST representation.
Generating RTLIL representation for module `\main'.
Successfully finished Verilog frontend.
2. Executing SYNTH_MICROCHIP pass.
2.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/microchip/cells_sim.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/microchip/cells_sim.v' to AST representation.
Generating RTLIL representation for module `\DFF'.
Generating RTLIL representation for module `\LUT1'.
Generating RTLIL representation for module `\LUT2'.
Generating RTLIL representation for module `\LUT3'.
Generating RTLIL representation for module `\LUT4'.
Generating RTLIL representation for module `\CLB_COUNTER'.
Generating RTLIL representation for module `\IB'.
Generating RTLIL representation for module `\OB'.
Successfully finished Verilog frontend.
2.2. Executing HIERARCHY pass (managing design hierarchy).
2.2.1. Analyzing design hierarchy..
Top module: \main
2.2.2. Analyzing design hierarchy..
Top module: \main
Removed 0 unused modules.
2.3. Executing PROC pass (convert processes to netlists).
2.3.1. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
2.3.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees).
Removed a total of 0 dead cases.
2.3.3. Executing PROC_PRUNE pass (remove redundant assignments in processes).
Removed 0 redundant assignments.
Promoted 0 assignments to connections.
2.3.4. Executing PROC_INIT pass (extract init attributes).
2.3.5. Executing PROC_ARST pass (detect async resets in processes).
2.3.6. Executing PROC_ROM pass (convert switches to ROMs).
Converted 0 switches.
2.3.7. Executing PROC_MUX pass (convert decision trees to multiplexers).
2.3.8. Executing PROC_DLATCH pass (convert process syncs to latches).
2.3.9. Executing PROC_DFF pass (convert process syncs to FFs).
2.3.10. Executing PROC_MEMWR pass (convert process memory writes to cells).
2.3.11. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
2.3.12. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.4. Executing FLATTEN pass (flatten design).
2.5. Executing DEMINOUT pass (demote inout ports to input or output).
2.6. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.7. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
Removed 0 unused cells and 1 unused wires.
<suppressed ~1 debug messages>
2.8. Executing CHECK pass (checking for obvious problems).
Checking module main...
Found and reported 0 problems.
2.9. Executing OPT pass (performing simple optimizations).
2.9.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.9.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.9.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Running muxtree optimizer on module \main..
Creating internal representation of mux trees.
No muxes found in this module.
Removed 0 multiplexer ports.
2.9.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Optimizing cells in module \main.
Performed a total of 0 changes.
2.9.5. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.9.6. Executing OPT_DFF pass (perform DFF optimizations).
2.9.7. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.9.8. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.9.9. Finished OPT passes. (There is nothing left to do.)
2.10. Executing FSM pass (extract and optimize FSM).
2.10.1. Executing FSM_DETECT pass (finding FSMs in design).
2.10.2. Executing FSM_EXTRACT pass (extracting FSM from design).
2.10.3. Executing FSM_OPT pass (simple optimizations of FSMs).
2.10.4. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.10.5. Executing FSM_OPT pass (simple optimizations of FSMs).
2.10.6. Executing FSM_RECODE pass (re-assigning FSM state encoding).
2.10.7. Executing FSM_INFO pass (dumping all available information on FSM cells).
2.10.8. Executing FSM_MAP pass (mapping FSMs to basic logic).
2.11. Executing OPT pass (performing simple optimizations).
2.11.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.11.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.11.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Running muxtree optimizer on module \main..
Creating internal representation of mux trees.
No muxes found in this module.
Removed 0 multiplexer ports.
2.11.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Optimizing cells in module \main.
Performed a total of 0 changes.
2.11.5. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.11.6. Executing OPT_DFF pass (perform DFF optimizations).
2.11.7. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.11.8. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.11.9. Finished OPT passes. (There is nothing left to do.)
2.12. Executing WREDUCE pass (reducing word size of cells).
2.13. Executing PEEPOPT pass (run peephole optimizers).
2.14. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.15. Executing TECHMAP pass (map to technology primitives).
2.15.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/cmp2lut.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/cmp2lut.v' to AST representation.
Generating RTLIL representation for module `\_90_lut_cmp_'.
Successfully finished Verilog frontend.
2.15.2. Continuing TECHMAP pass.
No more expansions possible.
<suppressed ~6 debug messages>
2.16. Executing SHARE pass (SAT-based resource sharing).
2.17. Executing OPT pass (performing simple optimizations).
2.17.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.17.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.17.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Running muxtree optimizer on module \main..
Creating internal representation of mux trees.
No muxes found in this module.
Removed 0 multiplexer ports.
2.17.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Optimizing cells in module \main.
Performed a total of 0 changes.
2.17.5. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.17.6. Executing OPT_DFF pass (perform DFF optimizations).
2.17.7. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.17.8. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.17.9. Finished OPT passes. (There is nothing left to do.)
2.18. Executing MEMORY pass.
2.18.1. Executing OPT_MEM pass (optimize memories).
Performed a total of 0 transformations.
2.18.2. Executing OPT_MEM_PRIORITY pass (removing unnecessary memory write priority relations).
Performed a total of 0 transformations.
2.18.3. Executing OPT_MEM_FEEDBACK pass (finding memory read-to-write feedback paths).
2.18.4. Executing MEMORY_BMUX2ROM pass (converting muxes to ROMs).
2.18.5. Executing MEMORY_DFF pass (merging $dff cells to $memrd).
2.18.6. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.18.7. Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).
2.18.8. Executing OPT_MEM_WIDEN pass (optimize memories where all ports are wide).
Performed a total of 0 transformations.
2.18.9. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.18.10. Executing MEMORY_COLLECT pass (generating $mem cells).
2.19. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.20. Executing OPT pass (performing simple optimizations).
2.20.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.20.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.20.3. Executing OPT_DFF pass (perform DFF optimizations).
2.20.4. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.20.5. Finished fast OPT passes.
2.21. Executing MEMORY_MAP pass (converting memories to logic and flip-flops).
2.22. Executing OPT pass (performing simple optimizations).
2.22.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.22.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.22.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Running muxtree optimizer on module \main..
Creating internal representation of mux trees.
No muxes found in this module.
Removed 0 multiplexer ports.
2.22.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Optimizing cells in module \main.
Performed a total of 0 changes.
2.22.5. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.22.6. Executing OPT_SHARE pass.
2.22.7. Executing OPT_DFF pass (perform DFF optimizations).
2.22.8. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.22.9. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.22.10. Finished OPT passes. (There is nothing left to do.)
2.23. Executing TECHMAP pass (map to technology primitives).
2.23.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/techmap.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/techmap.v' to AST representation.
Generating RTLIL representation for module `\_90_simplemap_bool_ops'.
Generating RTLIL representation for module `\_90_simplemap_reduce_ops'.
Generating RTLIL representation for module `\_90_simplemap_logic_ops'.
Generating RTLIL representation for module `\_90_simplemap_compare_ops'.
Generating RTLIL representation for module `\_90_simplemap_various'.
Generating RTLIL representation for module `\_90_simplemap_registers'.
Generating RTLIL representation for module `\_90_shift_ops_shr_shl_sshl_sshr'.
Generating RTLIL representation for module `\_90_shift_shiftx'.
Generating RTLIL representation for module `\_90_fa'.
Generating RTLIL representation for module `\_90_lcu'.
Generating RTLIL representation for module `\_90_alu'.
Generating RTLIL representation for module `\_90_macc'.
Generating RTLIL representation for module `\_90_alumacc'.
Generating RTLIL representation for module `\$__div_mod_u'.
Generating RTLIL representation for module `\$__div_mod_trunc'.
Generating RTLIL representation for module `\_90_div'.
Generating RTLIL representation for module `\_90_mod'.
Generating RTLIL representation for module `\$__div_mod_floor'.
Generating RTLIL representation for module `\_90_divfloor'.
Generating RTLIL representation for module `\_90_modfloor'.
Generating RTLIL representation for module `\_90_pow'.
Generating RTLIL representation for module `\_90_pmux'.
Generating RTLIL representation for module `\_90_demux'.
Generating RTLIL representation for module `\_90_lut'.
Successfully finished Verilog frontend.
2.23.2. Continuing TECHMAP pass.
Using extmapper simplemap for cells of type $not.
No more expansions possible.
<suppressed ~72 debug messages>
2.24. Executing OPT pass (performing simple optimizations).
2.24.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.24.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.24.3. Executing OPT_DFF pass (perform DFF optimizations).
2.24.4. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.24.5. Finished fast OPT passes.
2.25. Executing DFFLEGALIZE pass (convert FFs to types supported by the target).
2.26. Executing OPT pass (performing simple optimizations).
2.26.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.26.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.26.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Running muxtree optimizer on module \main..
Creating internal representation of mux trees.
No muxes found in this module.
Removed 0 multiplexer ports.
2.26.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Optimizing cells in module \main.
Performed a total of 0 changes.
2.26.5. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.26.6. Executing OPT_DFF pass (perform DFF optimizations).
2.26.7. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.26.8. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.26.9. Finished OPT passes. (There is nothing left to do.)
2.27. Executing ABC9 pass.
2.27.1. Executing ABC9_OPS pass (helper functions for ABC9).
2.27.2. Executing ABC9_OPS pass (helper functions for ABC9).
2.27.3. Executing SCC pass (detecting logic loops).
Found 0 SCCs in module main.
Found 0 SCCs.
2.27.4. Executing ABC9_OPS pass (helper functions for ABC9).
2.27.5. Executing PROC pass (convert processes to netlists).
2.27.5.1. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
2.27.5.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees).
Removed a total of 0 dead cases.
2.27.5.3. Executing PROC_PRUNE pass (remove redundant assignments in processes).
Removed 0 redundant assignments.
Promoted 0 assignments to connections.
2.27.5.4. Executing PROC_INIT pass (extract init attributes).
2.27.5.5. Executing PROC_ARST pass (detect async resets in processes).
2.27.5.6. Executing PROC_ROM pass (convert switches to ROMs).
Converted 0 switches.
2.27.5.7. Executing PROC_MUX pass (convert decision trees to multiplexers).
2.27.5.8. Executing PROC_DLATCH pass (convert process syncs to latches).
2.27.5.9. Executing PROC_DFF pass (convert process syncs to FFs).
2.27.5.10. Executing PROC_MEMWR pass (convert process memory writes to cells).
2.27.5.11. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
2.27.5.12. Executing OPT_EXPR pass (perform const folding).
2.27.6. Executing TECHMAP pass (map to technology primitives).
2.27.6.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/techmap.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/techmap.v' to AST representation.
Generating RTLIL representation for module `\_90_simplemap_bool_ops'.
Generating RTLIL representation for module `\_90_simplemap_reduce_ops'.
Generating RTLIL representation for module `\_90_simplemap_logic_ops'.
Generating RTLIL representation for module `\_90_simplemap_compare_ops'.
Generating RTLIL representation for module `\_90_simplemap_various'.
Generating RTLIL representation for module `\_90_simplemap_registers'.
Generating RTLIL representation for module `\_90_shift_ops_shr_shl_sshl_sshr'.
Generating RTLIL representation for module `\_90_shift_shiftx'.
Generating RTLIL representation for module `\_90_fa'.
Generating RTLIL representation for module `\_90_lcu'.
Generating RTLIL representation for module `\_90_alu'.
Generating RTLIL representation for module `\_90_macc'.
Generating RTLIL representation for module `\_90_alumacc'.
Generating RTLIL representation for module `\$__div_mod_u'.
Generating RTLIL representation for module `\$__div_mod_trunc'.
Generating RTLIL representation for module `\_90_div'.
Generating RTLIL representation for module `\_90_mod'.
Generating RTLIL representation for module `\$__div_mod_floor'.
Generating RTLIL representation for module `\_90_divfloor'.
Generating RTLIL representation for module `\_90_modfloor'.
Generating RTLIL representation for module `\_90_pow'.
Generating RTLIL representation for module `\_90_pmux'.
Generating RTLIL representation for module `\_90_demux'.
Generating RTLIL representation for module `\_90_lut'.
Successfully finished Verilog frontend.
2.27.6.2. Continuing TECHMAP pass.
No more expansions possible.
<suppressed ~80 debug messages>
2.27.7. Executing OPT pass (performing simple optimizations).
2.27.7.1. Executing OPT_EXPR pass (perform const folding).
2.27.7.2. Executing OPT_MERGE pass (detect identical cells).
Removed a total of 0 cells.
2.27.7.3. Executing OPT_MUXTREE pass (detect dead branches in mux trees).
Removed 0 multiplexer ports.
2.27.7.4. Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).
Performed a total of 0 changes.
2.27.7.5. Executing OPT_MERGE pass (detect identical cells).
Removed a total of 0 cells.
2.27.7.6. Executing OPT_DFF pass (perform DFF optimizations).
2.27.7.7. Executing OPT_CLEAN pass (remove unused cells and wires).
2.27.7.8. Executing OPT_EXPR pass (perform const folding).
2.27.7.9. Finished OPT passes. (There is nothing left to do.)
2.27.8. Executing TECHMAP pass (map to technology primitives).
2.27.8.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/abc9_map.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/abc9_map.v' to AST representation.
Successfully finished Verilog frontend.
2.27.8.2. Continuing TECHMAP pass.
No more expansions possible.
<suppressed ~2 debug messages>
2.27.9. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/abc9_model.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/abc9_model.v' to AST representation.
Generating RTLIL representation for module `$__ABC9_DELAY'.
Generating RTLIL representation for module `$__ABC9_SCC_BREAKER'.
Generating RTLIL representation for module `$__DFF_N__$abc9_flop'.
Generating RTLIL representation for module `$__DFF_P__$abc9_flop'.
Successfully finished Verilog frontend.
2.27.10. Executing ABC9_OPS pass (helper functions for ABC9).
2.27.11. Executing ABC9_OPS pass (helper functions for ABC9).
<suppressed ~2 debug messages>
2.27.12. Executing AIGMAP pass (map logic to AIG).
Module main: replaced 0 cells with 0 new cells, skipped 1 cells.
not replaced 1 cell types:
1 $_NOT_
2.27.12.1. Executing ABC9_OPS pass (helper functions for ABC9).
2.27.12.2. Executing XAIGER backend.
<suppressed ~5 debug messages>
Extracted 0 AND gates and 5 wires from module `main' to a netlist network with 1 inputs and 1 outputs.
2.27.12.3. Executing ABC9_EXE pass (technology mapping using ABC9).
2.27.12.4. Executing ABC9.
Running ABC command: "<yosys-exe-dir>/yosys-abc" -s -f <abc-temp-dir>/abc.script 2>&1
ABC: ABC command line: "source <abc-temp-dir>/abc.script".
ABC:
ABC: + read_lut <abc-temp-dir>/lutdefs.txt
ABC: + read_box <abc-temp-dir>/input.box
ABC: + &read <abc-temp-dir>/input.xaig
ABC: + &ps
ABC: <abc-temp-dir>/input : i/o = 1/ 1 and = 0 lev = 0 (0.00) mem = 0.00 MB box = 0 bb = 0
ABC: + &if -v
ABC: K = 4. Memory (bytes): Truth = 0. Cut = 48. Obj = 128. Set = 528. CutMin = no
ABC: Node = 0. Ch = 0. Total mem = 0.00 MB. Peak cut mem = 0.00 MB.
ABC: P: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: P: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: P: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: E: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: F: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: E: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: A: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: E: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: A: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: E: Del = 0.00. Ar = 0.0. Edge = 0. Cut = 0. T = 0.00 sec
ABC: Total time = 0.00 sec
ABC: + &ps -l
ABC: <abc-temp-dir>/input : i/o = 1/ 1 and = 0 lev = 0 (0.00) mem = 0.00 MB box = 0 bb = 0
ABC: Mapping (K=0) : lut = 0 edge = 0 lev = 0 (0.00) mem = 0.00 MB
ABC: LUT = 0 : Ave = 0.00
ABC: + &write -n <abc-temp-dir>/output.aig
ABC: + time
ABC: elapse: 0.00 seconds, total: 0.00 seconds
2.27.12.5. Executing AIGER frontend.
<suppressed ~12 debug messages>
Removed 0 unused cells and 2 unused wires.
2.27.12.6. Executing ABC9_OPS pass (helper functions for ABC9).
ABC RESULTS: $lut cells: 1
ABC RESULTS: input signals: 1
ABC RESULTS: output signals: 1
Removing temp directory.
2.27.13. Executing TECHMAP pass (map to technology primitives).
2.27.13.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/abc9_unmap.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/abc9_unmap.v' to AST representation.
Generating RTLIL representation for module `\$__DFF_x__$abc9_flop'.
Generating RTLIL representation for module `\$__ABC9_SCC_BREAKER'.
Successfully finished Verilog frontend.
2.27.13.2. Continuing TECHMAP pass.
No more expansions possible.
<suppressed ~5 debug messages>
2.28. Executing TECHMAP pass (map to technology primitives).
2.28.1. Executing Verilog-2005 frontend: /usr/local/bin/../share/yosys/microchip/cells_map.v
Parsing Verilog input from `/usr/local/bin/../share/yosys/microchip/cells_map.v' to AST representation.
Generating RTLIL representation for module `\$_DFF_P_'.
Generating RTLIL representation for module `\$lut'.
Successfully finished Verilog frontend.
2.28.2. Continuing TECHMAP pass.
Using template $paramod\$lut\WIDTH=32'00000000000000000000000000000001\LUT=2'01 for cells of type $lut.
No more expansions possible.
<suppressed ~18 debug messages>
2.29. Executing OPT pass (performing simple optimizations).
2.29.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.29.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.29.3. Executing OPT_DFF pass (perform DFF optimizations).
2.29.4. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
Removed 0 unused cells and 4 unused wires.
<suppressed ~1 debug messages>
2.29.5. Finished fast OPT passes.
2.30. Executing SPLITNETS pass (splitting up multi-bit signals).
2.31. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
Removed 0 unused cells and 1 unused wires.
<suppressed ~1 debug messages>
2.32. Executing RMPORTS pass (remove ports with no connections).
Finding unconnected ports in module \main
Removed 0 unused ports.
Removing now-unused cell ports in module \main
2.33. Executing OPT pass (performing simple optimizations).
2.33.1. Executing OPT_EXPR pass (perform const folding).
Optimizing module main.
2.33.2. Executing OPT_MERGE pass (detect identical cells).
Finding identical cells in module `\main'.
Removed a total of 0 cells.
2.33.3. Executing OPT_DFF pass (perform DFF optimizations).
2.33.4. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \main..
2.33.5. Finished fast OPT passes.
2.34. Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).
Mapping port main.CLBIN0PPS_synchronized using IB.
Mapping port main.PPS_OUT1 using OB.
2.35. Executing SETUNDEF pass (replace undef values with defined constants).
2.36. Executing HIERARCHY pass (managing design hierarchy).
2.36.1. Analyzing design hierarchy..
Top module: \main
2.36.2. Analyzing design hierarchy..
Top module: \main
Removed 0 unused modules.
2.37. Printing statistics.
=== main ===
Number of wires: 4
Number of wire bits: 4
Number of public wires: 2
Number of public wire bits: 2
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 3
IB 1
LUT1 1
OB 1
2.38. Executing CHECK pass (checking for obvious problems).
Checking module main...
Found and reported 0 problems.
3. Executing SCC pass (detecting logic loops).
Found 0 SCCs in module main.
Found 0 SCCs.
4. Executing JSON backend.
End of script. Logfile hash: 602acaecb5, CPU: user 0.07s system 0.01s, MEM: 14.71 MB peak
Yosys 0.25 (git sha1 e02b7f64b, gcc 10.2.1-6 -fPIC -Os)
Time spent: 34% 1x abc9_exe (0 sec), 23% 12x read_verilog (0 sec), ...
Run place & route:
python -m fpga_interchange.yosys_json \
--schema_dir /usr/local/share/arch-defs/third_party/fpga-interchange-schema/interchange \
--device /usr/local/share/arch-defs/device/pic16f131xx_v1/fpga_interchange/chipdb.device \
synth.json out.netlist
python /usr/local/share/arch-defs/device/pic16f131xx_v1/fasm_generator.py --schema_dir /usr/local/share/arch-defs/third_party/fpga-interchange-schema/interchange /usr/local/share/arch-defs/device/pic16f131xx_v1/fpga_interchange/chipdb.device out.netlist out.phys out.fasm
build the output files:
python /usr/local/share/arch-defs/device/pic16f131xx_v1/bitstream/assembler.py -p ../input/project.json out.fasm bitstream.json
python /usr/local/share/arch-defs/device/pic16f131xx_v1/bitstream/format.py -t /usr/local/share/arch-defs/device/pic16f131xx_v1/templates/c_header_template.c bitstream.json ../bitstream.s
python /usr/local/share/arch-defs/result_generators/mapping_header.py -t /usr/local/share/arch-defs/device/pic16f131xx_v1/templates/output_mappings.h bitstream.json ../clb1_output_mappings.h
2. Executing SCC pass (detecting logic loops).
Found 0 SCCs in module main.
Found 0 SCCs.
3. Printing statistics.
=== main ===
Number of wires: 4
Number of wire bits: 4
Number of public wires: 2
Number of public wire bits: 2
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 3
IB 1
LUT1 1
OB 1
Draw the graphs:
Reading and parsing FASM file...
Reading XDC file...
Decoding...
Writing output file(s)...
But very interesting is the out.fasm
file. .fasm
stands for FPGA Assembly; it’s a low-level file that describes the
FPGA layout before it’s converted into a bitstream.
# Created by the FPGA Interchange FASM Generator (v0.0.18)
BLE_X3Y3.BLE0.FLOPSEL.DISABLE
BLE_X3Y3.BLE0.LUT.INIT[15:0] = 16'b0101010101010101
CLKDIV[2:0] = 3'b001
MUX0.CLBIN[5:0] = 6'b000000
MUX0.INSYNC[2:0] = 3'b100
BLE_X3Y3.BLE0_LI0.IN0
PPS_X5Y3.OPAD0_O.LO_Y_2
This is very exciting, it has an exact detailed description of the configurations,
including the exact BLE LUT config, the settings of the clock divider, and it shows the BLE input 0 is connected to IN0
from our constraints file.
The PPS_X5Y3.OPAD0_O.LO_Y_2
line is a bit confusing; let’s ignore that for now.
The rest of the files are not too useful for our work.
Decoding the BLE LUT Configuration
Based on observing the requests, I was able to make a script that submits a zip file and unpacks the response.
As I was playing around I figured out that I can specify LUTs directly int he verilog in a compact form like this, where
16'hAAAA
is the LUT config:
LUT4 #(.INIT(16'hAAAA)) name (
.I0(...),
.I1(...),
.I2(...),
.I3(...),
.O(...)
);
This makes reading and writing lots of LUT configurations really easy.
The Plan
My first thought was to make a single configuration where I filled up all the LUTs in the system, then I could just flip bits one at a time in the 16-bit LUT and find where the corresponding bit flipped in the bitstream, simple right?
The Reality
Note
If you want to actually run this yourself, you need to carefully manage the includes (main.xdc
) to match the Verilog.
If it does not, it will refuse to run.
It turns out that if you try to make a logic design that comes even close to filling all LUTs, the synthesis engine just fails.
Based on the datasheet configuration (recall our data model?) each LUT input can connect to a subset of the signals.
The most useful ones are the CLBSWIN
signals, a 32-bit wide input to the CLB block: CLBSWIN0-7
can connect to the A input,
CLBSWIN8-15
to B, CLBSWIN16-23
to C, and CLBSWIN24-31
to D. This means we can (try) to prevent the router from altering
our design by connecting the SW inputs to the corresponding LUT inputs in our design.
So we can make the following design:
LUT4 #(.INIT(16'hAAAA)) name (
.I0(CLBSWIN0),
.I1(CLBSWIN8),
.I2(CLBSWIN16),
.I3(CLBSWIN24),
.O(PPS_OUT1)
);
When we synthesize this it shows the expected results:
routed.svg
# Created by the FPGA Interchange FASM Generator (v0.0.18)
BLE_X3Y3.BLE0.FLOPSEL.DISABLE
BLE_X3Y3.BLE0.LUT.INIT[15:0] = 16'b1010101010101010
BLE_X3Y3.BLE0_LI0.CLBSWIN0
BLE_X3Y3.BLE0_LI1.CLBSWIN8
BLE_X3Y3.BLE0_LI2.CLBSWIN16
BLE_X3Y3.BLE0_LI3.CLBSWIN24
PPS_X5Y3.OPAD0_O.LO_Y_2
I tried to expand this by fanning out from the single output, layer by layer, with a random LUT configuration so it couldn’t be optimized out. (If a LUT configuration ignores an input signal, it will be optimized out).
Here is an example of a design that uses all 32 BLEs that can be implemented but fails to synthesize.
ble_fill_32.v
Note
Note: PPS 0 can actually be connected to BLE0
\nI also use random LUT configs so they can’t be optimized out.
module main(
CLBSWIN0, CLBSWIN1, CLBSWIN2, CLBSWIN3, CLBSWIN4, CLBSWIN5, CLBSWIN6, CLBSWIN7,
CLBSWIN8, CLBSWIN9, CLBSWIN10, CLBSWIN11, CLBSWIN12, CLBSWIN13, CLBSWIN14, CLBSWIN15,
CLBSWIN16, CLBSWIN17, CLBSWIN18, CLBSWIN19, CLBSWIN20, CLBSWIN21, CLBSWIN22, CLBSWIN23,
CLBSWIN24, CLBSWIN25, CLBSWIN26, CLBSWIN27, CLBSWIN28, CLBSWIN29, CLBSWIN30, CLBSWIN31,
PPS_OUT0
);
input CLBSWIN0, CLBSWIN1, CLBSWIN2, CLBSWIN3, CLBSWIN4, CLBSWIN5, CLBSWIN6, CLBSWIN7;
input CLBSWIN8, CLBSWIN9, CLBSWIN10, CLBSWIN11, CLBSWIN12, CLBSWIN13, CLBSWIN14, CLBSWIN15;
input CLBSWIN16, CLBSWIN17, CLBSWIN18, CLBSWIN19, CLBSWIN20, CLBSWIN21, CLBSWIN22, CLBSWIN23;
input CLBSWIN24, CLBSWIN25, CLBSWIN26, CLBSWIN27, CLBSWIN28, CLBSWIN29, CLBSWIN30, CLBSWIN31;
output PPS_OUT0;
wire LUT4_0, LUT4_1, LUT4_2, LUT4_3, LUT4_4, LUT4_5, LUT4_6, LUT4_7, LUT4_8, LUT4_9;
wire LUT4_10, LUT4_11, LUT4_12, LUT4_13, LUT4_14, LUT4_15, LUT4_16, LUT4_17;
wire LUT4_18, LUT4_19, LUT4_20, LUT4_21, LUT4_22, LUT4_23, LUT4_24, LUT4_25;
wire LUT4_26, LUT4_27, LUT4_28, LUT4_29, LUT4_30, LUT4_31;
// First stage LUT connections
LUT4 #(.INIT(16'h2087)) lut0 (.I0(LUT4_1), .I1(LUT4_9), .I2(LUT4_17), .I3(LUT4_25), .O(LUT4_0));
assign PPS_OUT0 = LUT4_0;
// Second stage LUT connections
LUT4 #(.INIT(16'hf939)) lut1 (.I0(LUT4_2), .I1(LUT4_10), .I2(LUT4_18), .I3(LUT4_26), .O(LUT4_1));
LUT4 #(.INIT(16'hf7c2)) lut9 (.I0(LUT4_3), .I1(LUT4_11), .I2(LUT4_19), .I3(LUT4_27), .O(LUT4_9));
LUT4 #(.INIT(16'h944f)) lut17 (.I0(LUT4_4), .I1(LUT4_12), .I2(LUT4_20), .I3(LUT4_28), .O(LUT4_17));
LUT4 #(.INIT(16'h6f52)) lut25 (.I0(LUT4_5), .I1(LUT4_13), .I2(LUT4_21), .I3(LUT4_29), .O(LUT4_25));
// Third stage LUT connections
LUT4 #(.INIT(16'h68a2)) lut2 (.I0(LUT4_6), .I1(LUT4_14), .I2(LUT4_22), .I3(LUT4_30), .O(LUT4_2));
LUT4 #(.INIT(16'h2e34)) lut10 (.I0(LUT4_7), .I1(LUT4_15), .I2(LUT4_23), .I3(LUT4_31), .O(LUT4_10));
LUT4 #(.INIT(16'he58a)) lut18 (.I0(CLBSWIN24), .I1(LUT4_8), .I2(LUT4_16), .I3(LUT4_24), .O(LUT4_18));
// Leaf LUT connections
LUT4 #(.INIT(16'bb22)) lut3 (.I0(CLBSWIN0), .I1(CLBSWIN8), .I2(CLBSWIN16), .I3(CLBSWIN24), .O(LUT4_3));
LUT4 #(.INIT(16'h2718)) lut4 (.I0(CLBSWIN0), .I1(CLBSWIN8), .I2(CLBSWIN16), .I3(CLBSWIN24), .O(LUT4_4));
LUT4 #(.INIT(16'hdcf2)) lut5 (.I0(CLBSWIN0), .I1(CLBSWIN8), .I2(CLBSWIN16), .I3(CLBSWIN24), .O(LUT4_5));
LUT4 #(.INIT(16'hf4d6)) lut6 (.I0(CLBSWIN1), .I1(CLBSWIN9), .I2(CLBSWIN17), .I3(CLBSWIN25), .O(LUT4_6));
LUT4 #(.INIT(16'h83ff)) lut7 (.I0(CLBSWIN2), .I1(CLBSWIN10), .I2(CLBSWIN18), .I3(CLBSWIN26), .O(LUT4_7));
LUT4 #(.INIT(16'hebf8)) lut8 (.I0(CLBSWIN3), .I1(CLBSWIN11), .I2(CLBSWIN19), .I3(CLBSWIN27), .O(LUT4_8));
LUT4 #(.INIT(16'h05fa)) lut11 (.I0(CLBSWIN4), .I1(CLBSWIN12), .I2(CLBSWIN20), .I3(CLBSWIN28), .O(LUT4_11));
LUT4 #(.INIT(16'h8d2c)) lut12 (.I0(CLBSWIN5), .I1(CLBSWIN13), .I2(CLBSWIN21), .I3(CLBSWIN29), .O(LUT4_12));
LUT4 #(.INIT(16'h8c2f)) lut13 (.I0(CLBSWIN6), .I1(CLBSWIN14), .I2(CLBSWIN22), .I3(CLBSWIN30), .O(LUT4_13));
LUT4 #(.INIT(16'hd720)) lut14 (.I0(CLBSWIN7), .I1(CLBSWIN15), .I2(CLBSWIN23), .I3(CLBSWIN31), .O(LUT4_14));
LUT4 #(.INIT(16'ha9cf)) lut15 (.I0(CLBSWIN0), .I1(CLBSWIN8), .I2(CLBSWIN16), .I3(CLBSWIN24), .O(LUT4_15));
LUT4 #(.INIT(16'h8837)) lut16 (.I0(CLBSWIN1), .I1(CLBSWIN9), .I2(CLBSWIN17), .I3(CLBSWIN25), .O(LUT4_16));
LUT4 #(.INIT(16'hf3f9)) lut19 (.I0(CLBSWIN2), .I1(CLBSWIN10), .I2(CLBSWIN18), .I3(CLBSWIN26), .O(LUT4_19));
LUT4 #(.INIT(16'h3b19)) lut20 (.I0(CLBSWIN3), .I1(CLBSWIN11), .I2(CLBSWIN19), .I3(CLBSWIN27), .O(LUT4_20));
LUT4 #(.INIT(16'hc980)) lut21 (.I0(CLBSWIN4), .I1(CLBSWIN12), .I2(CLBSWIN20), .I3(CLBSWIN28), .O(LUT4_21));
LUT4 #(.INIT(16'h68f9)) lut22 (.I0(CLBSWIN5), .I1(CLBSWIN13), .I2(CLBSWIN21), .I3(CLBSWIN29), .O(LUT4_22));
LUT4 #(.INIT(16'h7367)) lut23 (.I0(CLBSWIN6), .I1(CLBSWIN14), .I2(CLBSWIN22), .I3(CLBSWIN30), .O(LUT4_23));
LUT4 #(.INIT(16'hc07f)) lut24 (.I0(CLBSWIN7), .I1(CLBSWIN15), .I2(CLBSWIN23), .I3(CLBSWIN31), .O(LUT4_24));
LUT4 #(.INIT(16'hd4ec)) lut26 (.I0(CLBSWIN0), .I1(CLBSWIN8), .I2(CLBSWIN16), .I3(CLBSWIN24), .O(LUT4_26));
LUT4 #(.INIT(16'h9f5e)) lut27 (.I0(CLBSWIN1), .I1(CLBSWIN9), .I2(CLBSWIN17), .I3(CLBSWIN25), .O(LUT4_27));
LUT4 #(.INIT(16'hc319)) lut28 (.I0(CLBSWIN2), .I1(CLBSWIN10), .I2(CLBSWIN18), .I3(CLBSWIN26), .O(LUT4_28));
LUT4 #(.INIT(16'hab98)) lut29 (.I0(CLBSWIN3), .I1(CLBSWIN11), .I2(CLBSWIN19), .I3(CLBSWIN27), .O(LUT4_29));
LUT4 #(.INIT(16'hff75)) lut30 (.I0(CLBSWIN4), .I1(CLBSWIN12), .I2(CLBSWIN20), .I3(CLBSWIN28), .O(LUT4_30));
LUT4 #(.INIT(16'h2ac5)) lut31 (.I0(CLBSWIN5), .I1(CLBSWIN13), .I2(CLBSWIN21), .I3(CLBSWIN29), .O(LUT4_31));
endmodule
The best I could do was 23 LUTs in a single design:
ble_fill_23.v
(* CLKDIV = 3'd0 *)
module lut5group #(
// One 80-bit parameter: 5 * 16-bit LUT tables (5*4 nibbles = 20 nibbles)
parameter [79:0] LUTS_INIT = 80'h00000000000000000000
)(
// Single 16-bit input, where bits are grouped as:
// in[3:0] -> Group 1 (for LUT1)
// in[7:4] -> Group 2 (for LUT2)
// in[11:8] -> Group 3 (for LUT3)
// in[15:12] -> Group 4 (for LUT4)
input [15:0] in,
output out
);
// Slice the 80-bit LUTS_INIT into five 16-bit LUT INIT values.
localparam [15:0] LUT1_INIT = LUTS_INIT[15:0];
localparam [15:0] LUT2_INIT = LUTS_INIT[31:16];
localparam [15:0] LUT3_INIT = LUTS_INIT[47:32];
localparam [15:0] LUT4_INIT = LUTS_INIT[63:48];
localparam [15:0] LUT5_INIT = LUTS_INIT[79:64];
// Wires for outputs of the first-stage LUTs.
wire group1_out, group2_out, group3_out, group4_out;
// First-stage LUTs: each processes one group of 4 input bits.
LUT4 #(.INIT(LUT1_INIT)) lut1 (
.I0(in[0]),
.I1(in[1]),
.I2(in[2]),
.I3(in[3]),
.O(group1_out)
);
LUT4 #(.INIT(LUT2_INIT)) lut2 (
.I0(in[4]),
.I1(in[5]),
.I2(in[6]),
.I3(in[7]),
.O(group2_out)
);
LUT4 #(.INIT(LUT3_INIT)) lut3 (
.I0(in[8]),
.I1(in[9]),
.I2(in[10]),
.I3(in[11]),
.O(group3_out)
);
LUT4 #(.INIT(LUT4_INIT)) lut4 (
.I0(in[12]),
.I1(in[13]),
.I2(in[14]),
.I3(in[15]),
.O(group4_out)
);
// Second-stage LUT: combines the four group outputs.
LUT4 #(.INIT(LUT5_INIT)) lut5 (
.I0(group1_out),
.I1(group2_out),
.I2(group3_out),
.I3(group4_out),
.O(out)
);
endmodule
module main (
// 16 switch inputs
input CLBSWIN0, CLBSWIN8, CLBSWIN16, CLBSWIN24,
input CLBSWIN1, CLBSWIN9, CLBSWIN17, CLBSWIN25,
input CLBSWIN2, CLBSWIN10, CLBSWIN18, CLBSWIN26,
input CLBSWIN3, CLBSWIN11, CLBSWIN19, CLBSWIN27,
// Four outputs
output PPS_OUT0,
output PPS_OUT1,
output PPS_OUT2,
output PPS_OUT3,
output PPS_OUT4,
output PPS_OUT5,
);
// Combine the 16 individual inputs into one 16-bit signal.
wire [15:0] my_inputs = {
CLBSWIN27, CLBSWIN19, CLBSWIN11, CLBSWIN3,
CLBSWIN26, CLBSWIN18, CLBSWIN10, CLBSWIN2,
CLBSWIN25, CLBSWIN17, CLBSWIN9, CLBSWIN1,
CLBSWIN24, CLBSWIN16, CLBSWIN8, CLBSWIN0
};
// Instantiate the first LUT grouping module using a single 80-bit literal.
// The 80-bit value encodes five 16-bit LUT INIT values.
// Bits [15:0] -> LUT1_INIT, [31:16] -> LUT2_INIT,
// Bits [47:32] -> LUT3_INIT, [63:48] -> LUT4_INIT,
// Bits [79:64] -> LUT5_INIT.
lut5group #(
.LUTS_INIT(80'hf6074086bd89639debf4)
) my_lut_group (
.in(my_inputs),
.out(PPS_OUT0)
);
// Instantiate the second LUT grouping module.
lut5group #(
.LUTS_INIT(80'he698a9fb27e04f49ffda)
) my_lut_group2 (
.in(my_inputs),
.out(PPS_OUT1)
);
// Instantiate the third LUT grouping module.
lut5group #(
.LUTS_INIT(80'hfb8a1ade450a1e5c9408)
) my_lut_group3 (
.in(my_inputs),
.out(PPS_OUT2)
);
LUT4 #(.INIT(16'hf607)) lut1 (
.I0(my_inputs[4]),
.I1(my_inputs[5]),
.I2(my_inputs[6]),
.I3(my_inputs[7]),
.O(PPS_OUT3)
);
LUT4 #(.INIT(16'h8821)) lut2 (
.I0(my_inputs[0]),
.I1(my_inputs[1]),
.I2(my_inputs[2]),
.I3(my_inputs[3]),
.O(PPS_OUT4)
);
endmodule
Identifying BLE LUT Locations
But once I have this file I can submit it to make the base configuration, and then generate variants where I flip one bit in the LUT config, then look for a corresponding bit in the bitstream output.
After we submit all the variants we now need to process the results. I want to process the fasm
file because
it has an exact description of the hardware, but it looks like this:
BLE_X1Y2.BLE0.LUT.INIT[15:0] = 16'b1110101111110100
BLE_X1Y3.BLE0.LUT.INIT[15:0] = 16'b1111111100000000
BLE_X1Y4.BLE0.LUT.INIT[15:0] = 16'b1001010000001000
BLE_X1Y6.BLE0.LUT.INIT[15:0] = 16'b0001111001011100
BLE_X1Y7.BLE0.LUT.INIT[15:0] = 1111000001100111
BLE_X1Y8.BLE0.LUT.INIT[15:0] = 1010101010101010
BLE_X1Y9.BLE0.LUT.INIT[15:0] = 1111111100000000
BLE_X2Y3.BLE0.LUT.INIT[15:0] = 0001101011011110
BLE_X2Y4.BLE0.LUT.INIT[15:0] = 0110001110011101
BLE_X2Y6.BLE0.LUT.INIT[15:0] = 0100111101001001
BLE_X2Y8.BLE0.LUT.INIT[15:0] = 0100000010000110
BLE_X2Y9.BLE0.LUT.INIT[15:0] = 1111111111011010
BLE_X3Y3.BLE0.LUT.INIT[15:0] = 1111011000000111
BLE_X3Y5.BLE0.LUT.INIT[15:0] = 1111000011110000
BLE_X3Y7.BLE0.LUT.INIT[15:0] = 1111111100000000
BLE_X3Y8.BLE0.LUT.INIT[15:0] = 1011110110001001
BLE_X3Y9.BLE0.LUT.INIT[15:0] = 1000100000100001
BLE_X4Y3.BLE0.LUT.INIT[15:0] = 1010100111111011
BLE_X4Y4.BLE0.LUT.INIT[15:0] = 0100010100001010
BLE_X4Y5.BLE0.LUT.INIT[15:0] = 0010011111100000
BLE_X4Y7.BLE0.LUT.INIT[15:0] = 1101101010100100
BLE_X4Y8.BLE0.LUT.INIT[15:0] = 1111101110001010
BLE_X4Y9.BLE0.LUT.INIT[15:0] = 1100110011001100
We need to map the BLE_X3Y3
etc. to the BLE number, luckely we have the routed.svg
:
routed.svg
If you squint long and hard enough you will realize the BLEs are arranged in rows of 4:
Y | X=1 | X=2 | X=3 | X=4 |
---|---|---|---|---|
1 | ||||
2 | BLE0 | BLE1 | BLE2 | BLE3 |
3 | BLE4 | BLE5 | BLE6 | BLE7 |
4 | BLE8 | BLE9 | BLE10 | BLE11 |
5 | BLE12 | BLE13 | BLE14 | BLE15 |
6 | BLE16 | BLE17 | BLE18 | BLE19 |
7 | BLE20 | BLE21 | BLE22 | BLE23 |
8 | BLE24 | BLE25 | BLE26 | BLE27 |
9 | BLE28 | BLE29 | BLE30 | BLE31 |
We can add this to our data model:
class BLEXY(Enum):
BLE_X1Y2 = 0
...
BLE_X4Y9 = 31
Full BLE Location Data Model
The names exactly match the fasm
to ease parsing.
class BLEXY(Enum):
BLE_X1Y2 = 0
BLE_X2Y2 = 1
BLE_X3Y2 = 2
BLE_X4Y2 = 3
BLE_X1Y3 = 4
BLE_X2Y3 = 5
BLE_X3Y3 = 6
BLE_X4Y3 = 7
BLE_X1Y4 = 8
BLE_X2Y4 = 9
BLE_X3Y4 = 10
BLE_X4Y4 = 11
BLE_X1Y5 = 12
BLE_X2Y5 = 13
BLE_X3Y5 = 14
BLE_X4Y5 = 15
BLE_X1Y6 = 16
BLE_X2Y6 = 17
BLE_X3Y6 = 18
BLE_X4Y6 = 19
BLE_X1Y7 = 20
BLE_X2Y7 = 21
BLE_X3Y7 = 22
BLE_X4Y7 = 23
BLE_X1Y8 = 24
BLE_X2Y8 = 25
BLE_X3Y8 = 26
BLE_X4Y8 = 27
BLE_X1Y9 = 28
BLE_X2Y9 = 29
BLE_X3Y9 = 30
BLE_X4Y9 = 31
We can now make our first pass FASM
parsing class for the BLE FLOPSEL
and LUT
lines
example fasm
lines:
BLE_X1Y2.BLE0.FLOPSEL.DISABLE
BLE_X1Y2.BLE0.LUT.INIT[15:0] = 16'b1110101111110100
class FASM:
def __init__(self, fasm_file: Path):
self.LUTS = defaultdict(BLE_CFG)
with open(fasm_file, "r") as f:
for l in f.readlines():
if l.startswith("#"):
continue
if l.startswith("BLE_X"):
self.parse_ble(l)
continue
print(f"Unhandled line: {repr(l)}")
def parse_ble(self, line):
parts = line.split(".")
try:
ble_sel = BLEXY.__members__[parts[0]]
if parts[1] == "BLE0":
if parts[2] == "FLOPSEL":
# ex: BLE_X1Y2.BLE0.FLOPSEL.DISABLE
self.LUTS[ble_sel].FLOPSEL = FLOPSEL.__members__[parts[3].strip()]
return
if parts[2] == "LUT":
# ex: BLE_X1Y2.BLE0.LUT.INIT[15:0] = 16'b1110101111110100
self.LUTS[ble_sel].LUT_CONFIG = parts[-1][-17:-1]
return
except Exception as e:
raise RuntimeError(f"Error parsing line '{line}'") from e
print(f"Unhandled ble line: {parts}")
return
After parsing both the generated bitstreams and the FASM files we can visualize the result below, it’s sparse because
we only obtained data for 28 more or less random LUTs in the output. Below, I annotate them in the format <BLE #>:<LUT Bit>
. Luckily, we got
BLE 0 and BLE 31 so we know the bounds. If you look long enough you will see there is a pattern repeating every 8 words
(I added a bold line to help you see it).
28 BLE LUT Bit Map
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||||||
1 | ||||||||||||||
2 | ||||||||||||||
3 | ||||||||||||||
4 | ||||||||||||||
5 | ||||||||||||||
6 | ||||||||||||||
7 | ||||||||||||||
8 | ||||||||||||||
9 | ||||||||||||||
10 | ||||||||||||||
11 | ||||||||||||||
12 | ||||||||||||||
13 | ||||||||||||||
14 | ||||||||||||||
15 | ||||||||||||||
16 | ||||||||||||||
17 | 31:12 | 31:13 | 31:14 | 31:15 | 31:8 | 31:9 | 31:10 | 31:11 | ||||||
18 | 31:4 | 31:5 | 31:6 | 31:7 | ||||||||||
19 | 31:0 | 31:1 | 31:2 | 31:3 | 30:12 | 30:13 | 30:14 | 30:15 | ||||||
20 | 30:8 | 30:9 | 30:10 | 30:11 | ||||||||||
21 | 30:4 | 30:5 | 30:6 | 30:7 | 30:0 | 30:1 | 30:2 | 30:3 | ||||||
22 | 29:12 | 29:13 | 29:14 | 29:15 | ||||||||||
23 | 29:8 | 29:9 | 29:10 | 29:11 | 29:4 | 29:5 | 29:6 | 29:7 | ||||||
24 | 29:0 | 29:1 | 29:2 | 29:3 | ||||||||||
25 | 28:12 | 28:13 | 28:14 | 28:15 | 28:8 | 28:9 | 28:10 | 28:11 | ||||||
26 | 28:4 | 28:5 | 28:6 | 28:7 | ||||||||||
27 | 28:0 | 28:1 | 28:2 | 28:3 | 27:12 | 27:13 | 27:14 | 27:15 | ||||||
28 | 27:8 | 27:9 | 27:10 | 27:11 | ||||||||||
29 | 27:4 | 27:5 | 27:6 | 27:7 | 27:0 | 27:1 | 27:2 | 27:3 | ||||||
30 | 26:12 | 26:13 | 26:14 | 26:15 | ||||||||||
31 | 26:8 | 26:9 | 26:10 | 26:11 | 26:4 | 26:5 | 26:6 | 26:7 | ||||||
32 | 26:0 | 26:1 | 26:2 | 26:3 | ||||||||||
33 | 25:12 | 25:13 | 25:14 | 25:15 | 25:8 | 25:9 | 25:10 | 25:11 | ||||||
34 | 25:4 | 25:5 | 25:6 | 25:7 | ||||||||||
35 | 25:0 | 25:1 | 25:2 | 25:3 | 24:12 | 24:13 | 24:14 | 24:15 | ||||||
36 | 24:8 | 24:9 | 24:10 | 24:11 | ||||||||||
37 | 24:4 | 24:5 | 24:6 | 24:7 | 24:0 | 24:1 | 24:2 | 24:3 | ||||||
38 | 23:12 | 23:13 | 23:14 | 23:15 | ||||||||||
39 | 23:8 | 23:9 | 23:10 | 23:11 | 23:4 | 23:5 | 23:6 | 23:7 | ||||||
40 | 23:0 | 23:1 | 23:2 | 23:3 | ||||||||||
41 | 22:12 | 22:13 | 22:14 | 22:15 | 22:8 | 22:9 | 22:10 | 22:11 | ||||||
42 | 22:4 | 22:5 | 22:6 | 22:7 | ||||||||||
43 | 22:0 | 22:1 | 22:2 | 22:3 | ||||||||||
44 | ||||||||||||||
45 | ||||||||||||||
46 | 20:12 | 20:13 | 20:14 | 20:15 | ||||||||||
47 | 20:8 | 20:9 | 20:10 | 20:11 | 20:4 | 20:5 | 20:6 | 20:7 | ||||||
48 | 20:0 | 20:1 | 20:2 | 20:3 | ||||||||||
49 | ||||||||||||||
50 | ||||||||||||||
51 | ||||||||||||||
52 | ||||||||||||||
53 | ||||||||||||||
54 | 17:12 | 17:13 | 17:14 | 17:15 | ||||||||||
55 | 17:8 | 17:9 | 17:10 | 17:11 | 17:4 | 17:5 | 17:6 | 17:7 | ||||||
56 | 17:0 | 17:1 | 17:2 | 17:3 | ||||||||||
57 | 16:12 | 16:13 | 16:14 | 16:15 | 16:8 | 16:9 | 16:10 | 16:11 | ||||||
58 | 16:4 | 16:5 | 16:6 | 16:7 | ||||||||||
59 | 16:0 | 16:1 | 16:2 | 16:3 | 15:12 | 15:13 | 15:14 | 15:15 | ||||||
60 | 15:8 | 15:9 | 15:10 | 15:11 | ||||||||||
61 | 15:4 | 15:5 | 15:6 | 15:7 | 15:0 | 15:1 | 15:2 | 15:3 | ||||||
62 | 14:12 | 14:13 | 14:14 | 14:15 | ||||||||||
63 | 14:8 | 14:9 | 14:10 | 14:11 | 14:4 | 14:5 | 14:6 | 14:7 | ||||||
64 | 14:0 | 14:1 | 14:2 | 14:3 | ||||||||||
65 | ||||||||||||||
66 | ||||||||||||||
67 | ||||||||||||||
68 | ||||||||||||||
69 | ||||||||||||||
70 | 11:12 | 11:13 | 11:14 | 11:15 | ||||||||||
71 | 11:8 | 11:9 | 11:10 | 11:11 | 11:4 | 11:5 | 11:6 | 11:7 | ||||||
72 | 11:0 | 11:1 | 11:2 | 11:3 | ||||||||||
73 | ||||||||||||||
74 | ||||||||||||||
75 | 9:12 | 9:13 | 9:14 | 9:15 | ||||||||||
76 | 9:8 | 9:9 | 9:10 | 9:11 | ||||||||||
77 | 9:4 | 9:5 | 9:6 | 9:7 | 9:0 | 9:1 | 9:2 | 9:3 | ||||||
78 | 8:12 | 8:13 | 8:14 | 8:15 | ||||||||||
79 | 8:8 | 8:9 | 8:10 | 8:11 | 8:4 | 8:5 | 8:6 | 8:7 | ||||||
80 | 8:0 | 8:1 | 8:2 | 8:3 | ||||||||||
81 | 7:12 | 7:13 | 7:14 | 7:15 | 7:8 | 7:9 | 7:10 | 7:11 | ||||||
82 | 7:4 | 7:5 | 7:6 | 7:7 | ||||||||||
83 | 7:0 | 7:1 | 7:2 | 7:3 | 6:12 | 6:13 | 6:14 | 6:15 | ||||||
84 | 6:8 | 6:9 | 6:10 | 6:11 | ||||||||||
85 | 6:4 | 6:5 | 6:6 | 6:7 | 6:0 | 6:1 | 6:2 | 6:3 | ||||||
86 | 5:12 | 5:13 | 5:14 | 5:15 | ||||||||||
87 | 5:8 | 5:9 | 5:10 | 5:11 | 5:4 | 5:5 | 5:6 | 5:7 | ||||||
88 | 5:0 | 5:1 | 5:2 | 5:3 | ||||||||||
89 | 4:12 | 4:13 | 4:14 | 4:15 | 4:8 | 4:9 | 4:10 | 4:11 | ||||||
90 | 4:4 | 4:5 | 4:6 | 4:7 | ||||||||||
91 | 4:0 | 4:1 | 4:2 | 4:3 | ||||||||||
92 | ||||||||||||||
93 | ||||||||||||||
94 | ||||||||||||||
95 | ||||||||||||||
96 | ||||||||||||||
97 | ||||||||||||||
98 | ||||||||||||||
99 | 0:12 | 0:13 | 0:14 | 0:15 | ||||||||||
100 | 0:8 | 0:9 | 0:10 | 0:11 | ||||||||||
101 | 0:4 | 0:5 | 0:6 | 0:7 | 0:0 | 0:1 | 0:2 | 0:3 |
We can use that to extrapolate the LUT locations for every BLE:
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||||||
1 | ||||||||||||||
2 | ||||||||||||||
3 | ||||||||||||||
4 | ||||||||||||||
5 | ||||||||||||||
6 | ||||||||||||||
7 | ||||||||||||||
8 | ||||||||||||||
9 | ||||||||||||||
10 | ||||||||||||||
11 | ||||||||||||||
12 | ||||||||||||||
13 | ||||||||||||||
14 | ||||||||||||||
15 | ||||||||||||||
16 | ||||||||||||||
17 | 31:12 | 31:13 | 31:14 | 31:15 | 31:8 | 31:9 | 31:10 | 31:11 | ||||||
18 | 31:4 | 31:5 | 31:6 | 31:7 | ||||||||||
19 | 31:0 | 31:1 | 31:2 | 31:3 | 30:12 | 30:13 | 30:14 | 30:15 | ||||||
20 | 30:8 | 30:9 | 30:10 | 30:11 | ||||||||||
21 | 30:4 | 30:5 | 30:6 | 30:7 | 30:0 | 30:1 | 30:2 | 30:3 | ||||||
22 | 29:12 | 29:13 | 29:14 | 29:15 | ||||||||||
23 | 29:8 | 29:9 | 29:10 | 29:11 | 29:4 | 29:5 | 29:6 | 29:7 | ||||||
24 | 29:0 | 29:1 | 29:2 | 29:3 | ||||||||||
25 | 28:12 | 28:13 | 28:14 | 28:15 | 28:8 | 28:9 | 28:10 | 28:11 | ||||||
26 | 28:4 | 28:5 | 28:6 | 28:7 | ||||||||||
27 | 28:0 | 28:1 | 28:2 | 28:3 | 27:12 | 27:13 | 27:14 | 27:15 | ||||||
28 | 27:8 | 27:9 | 27:10 | 27:11 | ||||||||||
29 | 27:4 | 27:5 | 27:6 | 27:7 | 27:0 | 27:1 | 27:2 | 27:3 | ||||||
30 | 26:12 | 26:13 | 26:14 | 26:15 | ||||||||||
31 | 26:8 | 26:9 | 26:10 | 26:11 | 26:4 | 26:5 | 26:6 | 26:7 | ||||||
32 | 26:0 | 26:1 | 26:2 | 26:3 | ||||||||||
33 | 25:12 | 25:13 | 25:14 | 25:15 | 25:8 | 25:9 | 25:10 | 25:11 | ||||||
34 | 25:4 | 25:5 | 25:6 | 25:7 | ||||||||||
35 | 25:0 | 25:1 | 25:2 | 25:3 | 24:12 | 24:13 | 24:14 | 24:15 | ||||||
36 | 24:8 | 24:9 | 24:10 | 24:11 | ||||||||||
37 | 24:4 | 24:5 | 24:6 | 24:7 | 24:0 | 24:1 | 24:2 | 24:3 | ||||||
38 | 23:12 | 23:13 | 23:14 | 23:15 | ||||||||||
39 | 23:8 | 23:9 | 23:10 | 23:11 | 23:4 | 23:5 | 23:6 | 23:7 | ||||||
40 | 23:0 | 23:1 | 23:2 | 23:3 | ||||||||||
41 | 22:12 | 22:13 | 22:14 | 22:15 | 22:8 | 22:9 | 22:10 | 22:11 | ||||||
42 | 22:4 | 22:5 | 22:6 | 22:7 | ||||||||||
43 | 22:0 | 22:1 | 22:2 | 22:3 | 21:12 | 21:13 | 21:14 | 21:15 | ||||||
44 | 21:8 | 21:9 | 21:10 | 21:11 | ||||||||||
45 | 21:4 | 21:5 | 21:6 | 21:7 | 21:0 | 21:1 | 21:2 | 21:3 | ||||||
46 | 20:12 | 20:13 | 20:14 | 20:15 | ||||||||||
47 | 20:8 | 20:9 | 20:10 | 20:11 | 20:4 | 20:5 | 20:6 | 20:7 | ||||||
48 | 20:0 | 20:1 | 20:2 | 20:3 | ||||||||||
49 | 19:12 | 19:13 | 19:14 | 19:15 | 19:8 | 19:9 | 19:10 | 19:11 | ||||||
50 | 19:4 | 19:5 | 19:6 | 19:7 | ||||||||||
51 | 19:0 | 19:1 | 19:2 | 19:3 | 18:12 | 18:13 | 18:14 | 18:15 | ||||||
52 | 18:8 | 18:9 | 18:10 | 18:11 | ||||||||||
53 | 18:4 | 18:5 | 18:6 | 18:7 | 18:0 | 18:1 | 18:2 | 18:3 | ||||||
54 | 17:12 | 17:13 | 17:14 | 17:15 | ||||||||||
55 | 17:8 | 17:9 | 17:10 | 17:11 | 17:4 | 17:5 | 17:6 | 17:7 | ||||||
56 | 17:0 | 17:1 | 17:2 | 17:3 | ||||||||||
57 | 16:12 | 16:13 | 16:14 | 16:15 | 16:8 | 16:9 | 16:10 | 16:11 | ||||||
58 | 16:4 | 16:5 | 16:6 | 16:7 | ||||||||||
59 | 16:0 | 16:1 | 16:2 | 16:3 | 15:12 | 15:13 | 15:14 | 15:15 | ||||||
60 | 15:8 | 15:9 | 15:10 | 15:11 | ||||||||||
61 | 15:4 | 15:5 | 15:6 | 15:7 | 15:0 | 15:1 | 15:2 | 15:3 | ||||||
62 | 14:12 | 14:13 | 14:14 | 14:15 | ||||||||||
63 | 14:8 | 14:9 | 14:10 | 14:11 | 14:4 | 14:5 | 14:6 | 14:7 | ||||||
64 | 14:0 | 14:1 | 14:2 | 14:3 | ||||||||||
65 | 13:12 | 13:13 | 13:14 | 13:15 | 13:8 | 13:9 | 13:10 | 13:11 | ||||||
66 | 13:4 | 13:5 | 13:6 | 13:7 | ||||||||||
67 | 13:0 | 13:1 | 13:2 | 13:3 | 12:12 | 12:13 | 12:14 | 12:15 | ||||||
68 | 12:8 | 12:9 | 12:10 | 12:11 | ||||||||||
69 | 12:4 | 12:5 | 12:6 | 12:7 | 12:0 | 12:1 | 12:2 | 12:3 | ||||||
70 | 11:12 | 11:13 | 11:14 | 11:15 | ||||||||||
71 | 11:8 | 11:9 | 11:10 | 11:11 | 11:4 | 11:5 | 11:6 | 11:7 | ||||||
72 | 11:0 | 11:1 | 11:2 | 11:3 | ||||||||||
73 | 10:12 | 10:13 | 10:14 | 10:15 | 10:8 | 10:9 | 10:10 | 10:11 | ||||||
74 | 10:4 | 10:5 | 10:6 | 10:7 | ||||||||||
75 | 10:0 | 10:1 | 10:2 | 10:3 | 9:12 | 9:13 | 9:14 | 9:15 | ||||||
76 | 9:8 | 9:9 | 9:10 | 9:11 | ||||||||||
77 | 9:4 | 9:5 | 9:6 | 9:7 | 9:0 | 9:1 | 9:2 | 9:3 | ||||||
78 | 8:12 | 8:13 | 8:14 | 8:15 | ||||||||||
79 | 8:8 | 8:9 | 8:10 | 8:11 | 8:4 | 8:5 | 8:6 | 8:7 | ||||||
80 | 8:0 | 8:1 | 8:2 | 8:3 | ||||||||||
81 | 7:12 | 7:13 | 7:14 | 7:15 | 7:8 | 7:9 | 7:10 | 7:11 | ||||||
82 | 7:4 | 7:5 | 7:6 | 7:7 | ||||||||||
83 | 7:0 | 7:1 | 7:2 | 7:3 | 6:12 | 6:13 | 6:14 | 6:15 | ||||||
84 | 6:8 | 6:9 | 6:10 | 6:11 | ||||||||||
85 | 6:4 | 6:5 | 6:6 | 6:7 | 6:0 | 6:1 | 6:2 | 6:3 | ||||||
86 | 5:12 | 5:13 | 5:14 | 5:15 | ||||||||||
87 | 5:8 | 5:9 | 5:10 | 5:11 | 5:4 | 5:5 | 5:6 | 5:7 | ||||||
88 | 5:0 | 5:1 | 5:2 | 5:3 | ||||||||||
89 | 4:12 | 4:13 | 4:14 | 4:15 | 4:8 | 4:9 | 4:10 | 4:11 | ||||||
90 | 4:4 | 4:5 | 4:6 | 4:7 | ||||||||||
91 | 4:0 | 4:1 | 4:2 | 4:3 | 3:12 | 3:13 | 3:14 | 3:15 | ||||||
92 | 3:8 | 3:9 | 3:10 | 3:11 | ||||||||||
93 | 3:4 | 3:5 | 3:6 | 3:7 | 3:0 | 3:1 | 3:2 | 3:3 | ||||||
94 | 2:12 | 2:13 | 2:14 | 2:15 | ||||||||||
95 | 2:8 | 2:9 | 2:10 | 2:11 | 2:4 | 2:5 | 2:6 | 2:7 | ||||||
96 | 2:0 | 2:1 | 2:2 | 2:3 | ||||||||||
97 | 1:12 | 1:13 | 1:14 | 1:15 | 1:8 | 1:9 | 1:10 | 1:11 | ||||||
98 | 1:4 | 1:5 | 1:6 | 1:7 | ||||||||||
99 | 1:0 | 1:1 | 1:2 | 1:3 | 0:12 | 0:13 | 0:14 | 0:15 | ||||||
100 | 0:8 | 0:9 | 0:10 | 0:11 | ||||||||||
101 | 0:4 | 0:5 | 0:6 | 0:7 | 0:0 | 0:1 | 0:2 | 0:3 |
Decoding the BLE LUT Input Configuration
Next we need to decode what bits in the BLE configuration correspond to the input settings. To do this we need to change the inputs for a LUT without the rest of the design changing, this is much more challenging.
The first thing to notice is that if we look at the settings for the LUT input register, we see that the SW setting bits cover the whole bit width of the input register (i.e., the most significant bit (MSB) isn’t fixed but varies with the SWIN selection). So, if we iterate through the CLBSWIN values we can cover all the bit changes.
class LUT_IN_A(IntEnum):
"""BLE INPUT A[4:0]"""
# ...
CLBSWIN0 = 0b01100
CLBSWIN1 = 0b01101
CLBSWIN2 = 0b01110
CLBSWIN3 = 0b01111
CLBSWIN4 = 0b10000
CLBSWIN5 = 0b10001
CLBSWIN6 = 0b10010
CLBSWIN7 = 0b10011
# ...
Generating Variants
We can iterate on this simple base design cycling the inputs through the SWIN values (generating all 32 variants).
module main (
input CLBSWIN0, CLBSWIN8, CLBSWIN16, CLBSWIN24,
output BLE_OUT
);
LUT4 #(.INIT(16'hABCD)) ble (
.I0(CLBSWIN0),
.I1(CLBSWIN8),
.I2(CLBSWIN16),
.I3(CLBSWIN24),
.O(BLE_OUT)
);
endmodule
Iter SWIN Vals
import shutil, tempfile
from pathlib import Path
from request_func import submit_and_extract
base_dir = Path("blog_single_lut_in_iter")
out_dir = Path("blog_single_lut_in_iter_out")
out_dir.mkdir(exist_ok=True)
# Submit base configuration
with tempfile.TemporaryDirectory() as tdir:
proj = Path(tdir) / "project"
shutil.copytree(base_dir, proj)
print("Submitting base configuration")
res = submit_and_extract(proj, out_dir)
print("Output at", res.output_dir.resolve())
# Base mapping: I0->CLBSWIN0, I1->CLBSWIN8, I2->CLBSWIN16, I3->CLBSWIN24.
base_ports = {0: "CLBSWIN0", 1: "CLBSWIN8", 2: "CLBSWIN16", 3: "CLBSWIN24"}
port_ranges = {0: range(0, 8), 1: range(8, 16), 2: range(16, 24), 3: range(24, 32)}
for i, rng in port_ranges.items():
for num in rng:
new_port = f"CLBSWIN{num}"
if new_port == base_ports[i]:
continue
with tempfile.TemporaryDirectory() as tdir:
proj = Path(tdir) / "project"
shutil.copytree(base_dir, proj)
for fname in ["main.v", "main.xdc"]:
content = (proj / fname).read_text(encoding="utf-8")
(proj / fname).write_text(content.replace(base_ports[i], new_port), encoding="utf-8")
print(f"Submitting I{i} with {new_port}")
res = submit_and_extract(proj, out_dir)
print("Output at", res.output_dir.resolve())
Decoding the Results
We first update our FASM to parse the input lines:
FASM with Input
class FASM:
def __init__(self, fasm_file: Path):
self.LUTS = defaultdict(BLE_CFG)
self.PPS_OUT = dict()
self.IRQ_OUT = dict()
self.OE = defaultdict(OESELn)
self.MUXS = defaultdict(MUX_CFG)
self.CLKDIV = CLKDIV.DIV_BY_1
self.COUNTER = COUNTER()
self.TIMR0_IN = None
with open(fasm_file, "r") as f:
for l in f.readlines():
if l.startswith("#"):
continue
if l.startswith("BLE_X"):
self.parse_ble(l)
continue
print(f"Unhandled line: {repr(l)}")
@staticmethod
def lo_to_ble(lo_str: str) -> str:
"""Convert 'LO_Y_X' to 'BLE_XY'."""
_, lo_y, lo_x = lo_str.split("_")
return f"BLE_X{int(lo_x) + 1}Y{int(lo_y) + 2}"
@staticmethod
def ble_to_lo(ble_str: str) -> str:
"""Convert 'BLE_XY' to 'LO_Y_X'."""
ble_x, ble_y = ble_str[5:].split("Y")
return f"LO_{int(ble_y) - 2}_{int(ble_x) - 1}"
def parse_ble(self, line):
parts = line.split(".")
try:
ble_sel = BLEXY.__members__[parts[0]]
if parts[1] == "BLE0":
if parts[2] == "FLOPSEL":
# ex: BLE_X1Y2.BLE0.FLOPSEL.DISABLE
self.LUTS[ble_sel].FLOPSEL = FLOPSEL.__members__[parts[3].strip()]
return
if parts[2] == "LUT":
# ex: BLE_X1Y2.BLE0.LUT.INIT[15:0] = 16'b1110101111110100
self.LUTS[ble_sel].LUT_CONFIG = parts[-1][-17:-1]
return
if parts[1].startswith("BLE0_LI"):
if parts[2].startswith("LO_"):
# ex. BLE_X3Y2.BLE0_LI0.LO_1_2
# Input from another BLE
ble_num = BLEXY.__members__[self.lo_to_ble(parts[2])].value
ble_id = f"CLB_BLE_{ble_num}"
else:
# ex. BLE_X3Y3.BLE0_LI0.CLBSWIN0
ble_id = parts[2].strip()
if parts[1][-1] == "0":
self.LUTS[ble_sel].LUT_I_A = LUT_IN_A.__members__[ble_id]
return
if parts[1][-1] == "1":
self.LUTS[ble_sel].LUT_I_B = LUT_IN_B.__members__[ble_id]
return
if parts[1][-1] == "2":
self.LUTS[ble_sel].LUT_I_C = LUT_IN_C.__members__[ble_id]
return
if parts[1][-1] == "3":
self.LUTS[ble_sel].LUT_I_D = LUT_IN_D.__members__[ble_id]
return
except Exception as e:
raise RuntimeError(f"Error parsing line '{line}'") from e
print(f"Unhandled ble line: {parts}")
return
Now to analyze what bits correspond with these changes we can track correlated bit changes. For example if we see a setting (like the LUT input) has a bit change from 0 to 1 and from a 1 to a 0 we can then look for correlated changes in the bitstream.
If we see a couple of bits in the input and output change, we can save a list of candidate bitstream bits for the setting bit. If we see it change again, we can identify which bitstream bits also changed and then find the intersection of bitstream changes.
The code is deceptively simple, but this powerful technique will serve us for the rest of our reverse engineering.
Note
Throughout the Python code, I use strings of bits (i.e., 10101
) for bits because they are easier and clearer to manipulate in Python.
Bit Corr Tracker
class BitCorrelationTracker:
def __init__(self):
"""
A dictionary that maps:
setting_bit (hashable) -> set of candidate bitstream indices
"""
self.candidates_for_setting_bit = {}
def add_observation(
self,
bitstream_changes: list[tuple[int, bool]],
setting_changes: list[tuple[object, bool]],
):
"""
bitstream_changes:
A list of (bit_index, direction) for bits that changed in the bitstream.
direction = True means 1->0, direction = False means 0->1.
setting_changes:
A list of (setting_bit, direction) for bits that changed in the setting.
setting_bit can be any hashable (str, int, tuple, etc.).
direction = True means 1->0, direction = False means 0->1.
Raises:
ValueError if a new observation contradicts the existing candidates (i.e.,
it would force a candidate set to become empty).
"""
# Group bitstream bits by direction
bitstream_by_dir = {True: set(), False: set()}
for bit_idx, direction in bitstream_changes:
bitstream_by_dir[direction].add(bit_idx)
# Update candidate sets for each setting bit
for setting_bit, set_dir in setting_changes:
# All bitstream bits that moved in the same direction
possible_bits_now = bitstream_by_dir[set_dir]
# If we've never seen this setting bit before, just initialize:
if setting_bit not in self.candidates_for_setting_bit:
self.candidates_for_setting_bit[setting_bit] = set(possible_bits_now)
else:
# Intersect the old candidate set with the new set of possible bits
current_candidates = self.candidates_for_setting_bit[setting_bit]
new_candidates = current_candidates.intersection(possible_bits_now)
if not new_candidates:
# Contradiction – we have no overlap
raise ValueError(
f"Contradiction for setting bit {setting_bit} – "
f"no common bitstream bits remain candidates."
)
self.candidates_for_setting_bit[setting_bit] = new_candidates
def get_candidates(self) -> dict:
"""
Returns the current dictionary of candidate bitstream bits for each setting bit.
Format:
{
setting_bit (hashable): set of candidate bitstream_indices (int),
...
}
"""
return self.candidates_for_setting_bit
def find_delta_bits(old_attr: str, new_attr: str) -> list[tuple[int, bool]]:
"""
Returns a list of tuples:
- Each tuple contains:
- The bit index (with LSB at index 0) where old_attr and new_attr differ.
- A boolean indicating direction of change: True for 1 -> 0, False for 0 -> 1.
old_attr and new_attr are binary strings, MSB first.
"""
# Reverse both strings so that index 0 = LSB
old_attr_reversed = old_attr[::-1]
new_attr_reversed = new_attr[::-1]
changes = []
for i, (b_old, b_new) in enumerate(zip(old_attr_reversed, new_attr_reversed)):
if b_old != b_new:
changes.append((i, b_old == "1" and b_new == "0"))
return changes
Find Delta LUT Input Bits
import itertools
import json
import pprint
from collections import defaultdict
from pathlib import Path
from bit_corr_tracker import BitCorrelationTracker, find_delta_bits
from data_model import (
FASM,
get_lut_setting_bits,
)
def load_bitstream_hex_array(json_file: Path) -> str | None:
"""
Loads a .bitstream.json file that contains a list of hex strings
and converts it into a single concatenated binary string.
"""
data = json.loads(json_file.read_text(encoding="utf-8"))
bit_chunks = []
for hex_str in data["bitstream"]:
val = int(hex_str, 16)
bits_16 = f"{val:016b}" # Convert to 16-bit binary
bit_chunks.append(bits_16)
return "".join(bit_chunks)
def convert_defaultdict(d):
"""Recursively converts defaultdict to dict."""
if isinstance(d, defaultdict):
d = dict(d) # Convert to a regular dict
if isinstance(d, dict):
return {k: convert_defaultdict(v) for k, v in d.items()}
elif isinstance(d, list): # Handle nested lists
return [convert_defaultdict(v) for v in d]
return d # Return the value if it's neither dict nor list
def mask_bitstream(bitstream: str, ignored_positions: set[int]) -> str:
"""
Given a binary string (MSB first) and a set of bit positions (LSB = position 0),
returns a new binary string with the bits at those positions forced to '0'.
"""
bits = list(bitstream)
n = len(bits)
for pos in ignored_positions:
index = n - 1 - pos
if 0 <= index < n:
bits[index] = "0"
return "".join(bits)
def load_bs_and_config(request_dir: Path):
build_dir = request_dir / "response_files" / "build"
bitstream_file = list(build_dir.glob("bitstream.json"))[0]
bitstream = load_bitstream_hex_array(bitstream_file)
fasm_file = list(build_dir.glob("*.fasm"))[0]
config = FASM(fasm_file)
return {"bitstream": bitstream, "config": config}
def build_known_bits() -> set[int]:
known = set()
for lut in range(0, 31 + 1):
setting = get_lut_setting_bits(lut)
known.update(setting.values())
return known
def main():
root = Path(".\\blog_single_lut_in_iter_out")
print(f"Processing: {root}")
tracker = BitCorrelationTracker()
all_data = {p: load_bs_and_config(p) for p in root.iterdir() if p.is_dir()}
print(f"Loaded {len(all_data)} experiments.")
print("Comparing...")
input_attr_names = ["LUT_I_A", "LUT_I_B", "LUT_I_C", "LUT_I_D"]
for dataA, dataB in itertools.combinations(all_data.values(), 2):
bitsA, configA = dataA["bitstream"], dataA["config"]
bitsB, configB = dataB["bitstream"], dataB["config"]
if bitsA == bitsB: continue
bitstream_changes = find_delta_bits(bitsA, bitsB)
if not bitstream_changes: continue
# --- Find ALL setting bit changes for this pair ---
all_setting_changes = []
for key in configA.LUTS.keys() | configB.LUTS.keys():
lut_idx = key.value
bleA = configA.LUTS.get(key)
bleB = configB.LUTS.get(key)
for attr_name in input_attr_names:
enumA = getattr(bleA, attr_name, None) if bleA else None
valA = enumA.value if enumA is not None else 0
binA = format(valA, '05b')
enumB = getattr(bleB, attr_name, None) if bleB else None
valB = enumB.value if enumB is not None else 0
binB = format(valB, '05b')
if binA != binB:
for local_bit_pos, direction in find_delta_bits(binA, binB):
setting_id = (lut_idx, attr_name, local_bit_pos)
all_setting_changes.append((setting_id, direction))
if all_setting_changes:
tracker.add_observation(bitstream_changes, all_setting_changes)
print("\n--- Raw Tracker Results ---")
print("Format: (lut_idx, 'A'|'B'|'C'|'D', bit_pos)")
pprint.pprint(tracker.candidates_for_setting_bit, width=120)
if __name__ == "__main__":
main()
This code returns:
--- Raw Tracker Results ---
Format: (lut_idx, 'A'|'B'|'C'|'D', bit_pos)
{(6, 'LUT_I_A', 0): {1365},
(6, 'LUT_I_A', 1): {1366},
(6, 'LUT_I_A', 2): {1368, 1367},
(6, 'LUT_I_A', 3): {1368, 1367},
(6, 'LUT_I_A', 4): {1369},
(6, 'LUT_I_B', 0): {1354},
(6, 'LUT_I_B', 1): {1355},
(6, 'LUT_I_B', 2): {1356, 1357},
(6, 'LUT_I_B', 3): {1356, 1357},
(6, 'LUT_I_B', 4): {1360},
(6, 'LUT_I_C', 0): {1344},
(6, 'LUT_I_C', 1): {1345},
(6, 'LUT_I_C', 2): {1346, 1347},
(6, 'LUT_I_C', 3): {1346, 1347},
(6, 'LUT_I_C', 4): {1348},
(6, 'LUT_I_D', 0): {1332},
(6, 'LUT_I_D', 1): {1333},
(6, 'LUT_I_D', 2): {1334, 1335},
(6, 'LUT_I_D', 3): {1334, 1335},
(6, 'LUT_I_D', 4): {1336}}
It’s pretty easy to see the ambiguous bits are in a row, i.e. LUT_I_A[0:4]
is bits [1365:1369]
. We can then fill this in
(BLE:INP:Bit
format):
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
83 | 6:D:0 | 6:D:1 | 6:D:2 | 6:D:3 | 6:D:4 | 6:12 | 6:13 | 6:14 | 6:15 | |||||
84 | 6:C:0 | 6:C:1 | 6:C:2 | 6:C:3 | 6:C:4 | 6:8 | 6:9 | 6:10 | 6:11 | 6:B:0 | 6:B:1 | 6:B:2 | 6:B:3 | |
85 | 6:B:4 | 6:4 | 6:5 | 6:6 | 6:7 | 6:A:0 | 6:A:1 | 6:A:2 | 6:A:3 | 6:A:4 | 6:0 | 6:1 | 6:2 | 6:3 |
Expanding for all BLEs
Now we just need to get this for all the other LUTs. But this leaves us with a problem: how can we change which BLEs are used by our test logic?
We can do this by adding random LUTs to the design forcing the layout engine to move around the block we are working on.
This code accepts an output and a set of inputs (here we allow CLBSWIN0-31
). Then it makes a random layout by iteratively
keeping track of the available inputs (just the output port in the beginning) and then placing a LUT connecting it to one of them;
the placed LUT’s inputs are added to the list of global inputs. After the specified number of LUTs have been placed,
the remaining ones are filled from the allowed inputs.
We return a function that allows you to include the static Verilog you want to actually change.
Gen Rand Layout
import random
from pathlib import Path
stats_json_content = ...
project_json_content = ...
design_clb_content = ...
def generate_design_structure(dynamic_output_name, available_inputs, num_luts):
def random_init():
return f"16'h{random.getrandbits(16):04X}"
dynamic_luts = []
unconnected_dynamic = []
for i in range(num_luts):
lut = {
"name": f"lut{i}",
"output": f"lut{i}_out",
"inputs": [f"lut{i}_in{k}" for k in range(4)],
"init": random_init()
}
dynamic_luts.append(lut)
if i > 0:
if not unconnected_dynamic:
raise RuntimeError("No free inputs.")
tgt_lut_idx, tgt_pin_idx = random.choice(unconnected_dynamic)
unconnected_dynamic.remove((tgt_lut_idx, tgt_pin_idx))
dynamic_luts[tgt_lut_idx]["inputs"][tgt_pin_idx] = lut["output"]
unconnected_dynamic.extend([(i, k) for k in range(4)])
used_dynamic_top_inputs = set()
if not available_inputs and unconnected_dynamic:
raise ValueError("No inputs.")
for lut_idx, pin_idx in unconnected_dynamic:
chosen_input = random.choice(available_inputs)
dynamic_luts[lut_idx]["inputs"][pin_idx] = chosen_input
used_dynamic_top_inputs.add(chosen_input)
def _write_design_variant(static_verilog_block, static_inputs_list, static_outputs_list, subfolder):
subfolder = Path(subfolder)
subfolder.mkdir(parents=True, exist_ok=True)
all_inputs = sorted(list(used_dynamic_top_inputs.union(static_inputs_list)))
all_outputs = sorted(list(set(static_outputs_list + [dynamic_output_name])))
port_defs = ([f"input {p}" for p in all_inputs] + [f"output {p}" for p in all_outputs])
wire_defs = ([f"wire {lut['output']};" for lut in dynamic_luts] +
[f"wire {p};" for p in static_outputs_list])
lut_insts = []
for lut in dynamic_luts:
pins = [f".I{n}({lut['inputs'][n]})" for n in range(4)] + [f".O({lut['output']})"]
lut_insts.append(f"LUT4 #(.INIT({lut['init']})) {lut['name']} ({', '.join(pins)});")
v_content = f"""
module main (
{", ".join(port_defs)}
);
{chr(10).join(wire_defs)}
{static_verilog_block if static_verilog_block else ''}
{chr(10).join(lut_insts)}
assign {dynamic_output_name} = {dynamic_luts[0]['output'] if dynamic_luts else "1'b0"};
endmodule
"""
xdc_content = "\n".join(f"set_property PACKAGE_PIN {p} [get_ports {p}]" for p in (all_inputs+all_outputs))+"\n"
(subfolder / "main.v").write_text(v_content)
(subfolder / "main.xdc").write_text(xdc_content)
(subfolder / "stats.json").write_text(stats_json_content)
(subfolder / "project.json").write_text(project_json_content)
(subfolder / "design.clb").write_text(design_clb_content)
return subfolder
return _write_design_variant
Iter with rand layout
import random
import shutil, tempfile, os
from pathlib import Path
from gen_rand_layout import generate_design_structure
from request_func import submit_and_extract
STATIC_CODE_TEMPLATE = "LUT4 #(.INIT(16'hABCD)) ble (.I0({i0}),.I1({i1}),.I2({i2}),.I3({i3}),.O({o0}));"
STATIC_TEMPLATE_PORTS = {"inputs": ["i0", "i1", "i2", "i3"], "outputs": ["o0"]}
BASE_STATIC_INPUT_MAP = {"i0": "CLBSWIN0", "i1": "CLBSWIN8", "i2": "CLBSWIN16", "i3": "CLBSWIN24"}
STATIC_INPUT_RANGES = {0: range(0, 8), 1: range(8, 16), 2: range(16, 24), 3: range(24, 32)}
STATIC_OUTPUT_MAP = {STATIC_TEMPLATE_PORTS["outputs"][0]: "PPS_OUT0"}
base_output_dir = Path("blog_lut_swap_iter_out") # Define once for clarity
base_output_dir.mkdir(exist_ok=True)
print(f"--- Outputs -> subfolders in: {base_output_dir.resolve()} ---")
total_vars_processed = 0
for layout_idx in range(10):
print(f"\n--- Layout {layout_idx} ---")
try:
write_variant = generate_design_structure(
"PPS_OUT1",
[f"CLBSWIN{i}" for i in range(32)],
random.randint(4, 10)
)
except Exception as e:
print(f" Structure Gen Error: {e}")
continue
for i, swap_range in STATIC_INPUT_RANGES.items():
template_in_name = STATIC_TEMPLATE_PORTS["inputs"][i]
for pin_num in swap_range:
total_vars_processed += 1
new_phys_port = f"CLBSWIN{pin_num}"
variant_id = f"l{layout_idx}_sI{i}_{new_phys_port}"
print(f" Var {total_vars_processed}: {template_in_name}={new_phys_port} ({variant_id})", end="")
current_input_map = BASE_STATIC_INPUT_MAP.copy()
current_input_map[template_in_name] = new_phys_port
static_block = STATIC_CODE_TEMPLATE.format(**current_input_map, **STATIC_OUTPUT_MAP)
static_inputs = list(current_input_map.values())
static_outputs = list(STATIC_OUTPUT_MAP.values())
with tempfile.TemporaryDirectory() as tdir:
try:
temp_proj = Path(tdir) / "proj"
write_variant(static_block, static_inputs, static_outputs, temp_proj)
res = submit_and_extract(in_folder=temp_proj, out_dir=base_output_dir)
final_out = res.unzipped_files_dir if res.unzipped_files_dir else res.output_dir
print(f" -> OK ({final_out.name if final_out else '?'})")
except Exception as e:
print(f" -> FAIL ({e})")
print(f"\n--- Complete ({total_vars_processed} variations) ---")
After running a few variants we have enough data to extrapolate for all the LUTs! The itertools.combinations(all_data.values(), 2)
line in our analysis script is critical, by computing the full mesh of every combination of inputs we get significantly better results;
generating 10 designs and iterating the static data for them gives us over 80% of bits! The rest are easily identified by pattern.
--- Overall Coverage Stats ---
0-candidate bits: 57 (8.9%)
1-candidate bits: 514 (80.3%)
>1-candidate bits: 69 (10.8%)
--- Per-BLE Coverage Stats ---
BLE zero single multi [out of 20 bits total]
0 0 17 3
1 0 20 0
2 0 18 2
3 2 12 6
4 0 20 0
5 0 20 0
6 0 18 2
7 0 18 2
8 1 19 0
9 0 20 0
10 0 20 0
11 1 14 5
12 1 19 0
13 0 20 0
14 0 18 2
15 0 16 4
16 0 20 0
17 0 20 0
18 0 20 0
19 3 16 1
20 0 15 5
21 0 18 2
22 0 20 0
23 7 6 7
24 5 15 0
25 0 20 0
26 1 19 0
27 7 0 13
28 6 9 5
29 1 12 7
30 2 15 3
31 20 0 0
Layout with all LUT inputs
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||||||
1 | ||||||||||||||
2 | ||||||||||||||
3 | ||||||||||||||
4 | ||||||||||||||
5 | ||||||||||||||
6 | ||||||||||||||
7 | ||||||||||||||
8 | ||||||||||||||
9 | ||||||||||||||
10 | ||||||||||||||
11 | ||||||||||||||
12 | ||||||||||||||
13 | ||||||||||||||
14 | ||||||||||||||
15 | ||||||||||||||
16 | 31:D:0 | 31:D:1 | 31:D:2 | 31:D:3 | 31:D:4 | |||||||||
17 | 31:12 | 31:13 | 31:14 | 31:15 | 31:C:0 | 31:C:1 | 31:C:2 | 31:C:3 | 31:C:4 | 31:8 | 31:9 | 31:10 | 31:11 | |
18 | 31:B:0 | 31:B:1 | 31:B:2 | 31:B:3 | 31:B:4 | 31:4 | 31:5 | 31:6 | 31:7 | 31:A:0 | 31:A:1 | 31:A:2 | 31:A:3 | 31:A:4 |
19 | 31:0 | 31:1 | 31:2 | 31:3 | 30:D:0 | 30:D:1 | 30:D:2 | 30:D:3 | 30:D:4 | 30:12 | 30:13 | 30:14 | 30:15 | |
20 | 30:C:0 | 30:C:1 | 30:C:2 | 30:C:3 | 30:C:4 | 30:8 | 30:9 | 30:10 | 30:11 | 30:B:0 | 30:B:1 | 30:B:2 | 30:B:3 | |
21 | 30:B:4 | 30:4 | 30:5 | 30:6 | 30:7 | 30:A:0 | 30:A:1 | 30:A:2 | 30:A:3 | 30:A:4 | 30:0 | 30:1 | 30:2 | 30:3 |
22 | 29:D:0 | 29:D:1 | 29:D:2 | 29:D:3 | 29:D:4 | 29:12 | 29:13 | 29:14 | 29:15 | 29:C:0 | 29:C:1 | 29:C:2 | 29:C:3 | |
23 | 29:C:4 | 29:8 | 29:9 | 29:10 | 29:11 | 29:B:0 | 29:B:1 | 29:B:2 | 29:B:3 | 29:B:4 | 29:4 | 29:5 | 29:6 | 29:7 |
24 | 29:A:0 | 29:A:1 | 29:A:2 | 29:A:3 | 29:A:4 | 29:0 | 29:1 | 29:2 | 29:3 | 28:D:0 | 28:D:1 | 28:D:2 | 28:D:3 | 28:D:4 |
25 | 28:12 | 28:13 | 28:14 | 28:15 | 28:C:0 | 28:C:1 | 28:C:2 | 28:C:3 | 28:C:4 | 28:8 | 28:9 | 28:10 | 28:11 | |
26 | 28:B:0 | 28:B:1 | 28:B:2 | 28:B:3 | 28:B:4 | 28:4 | 28:5 | 28:6 | 28:7 | 28:A:0 | 28:A:1 | 28:A:2 | 28:A:3 | 28:A:4 |
27 | 28:0 | 28:1 | 28:2 | 28:3 | 27:D:0 | 27:D:1 | 27:D:2 | 27:D:3 | 27:D:4 | 27:12 | 27:13 | 27:14 | 27:15 | |
28 | 27:C:0 | 27:C:1 | 27:C:2 | 27:C:3 | 27:C:4 | 27:8 | 27:9 | 27:10 | 27:11 | 27:B:0 | 27:B:1 | 27:B:2 | 27:B:3 | |
29 | 27:B:4 | 27:4 | 27:5 | 27:6 | 27:7 | 27:A:0 | 27:A:1 | 27:A:2 | 27:A:3 | 27:A:4 | 27:0 | 27:1 | 27:2 | 27:3 |
30 | 26:D:0 | 26:D:1 | 26:D:2 | 26:D:3 | 26:D:4 | 26:12 | 26:13 | 26:14 | 26:15 | 26:C:0 | 26:C:1 | 26:C:2 | 26:C:3 | |
31 | 26:C:4 | 26:8 | 26:9 | 26:10 | 26:11 | 26:B:0 | 26:B:1 | 26:B:2 | 26:B:3 | 26:B:4 | 26:4 | 26:5 | 26:6 | 26:7 |
32 | 26:A:0 | 26:A:1 | 26:A:2 | 26:A:3 | 26:A:4 | 26:0 | 26:1 | 26:2 | 26:3 | 25:D:0 | 25:D:1 | 25:D:2 | 25:D:3 | 25:D:4 |
33 | 25:12 | 25:13 | 25:14 | 25:15 | 25:C:0 | 25:C:1 | 25:C:2 | 25:C:3 | 25:C:4 | 25:8 | 25:9 | 25:10 | 25:11 | |
34 | 25:B:0 | 25:B:1 | 25:B:2 | 25:B:3 | 25:B:4 | 25:4 | 25:5 | 25:6 | 25:7 | 25:A:0 | 25:A:1 | 25:A:2 | 25:A:3 | 25:A:4 |
35 | 25:0 | 25:1 | 25:2 | 25:3 | 24:D:0 | 24:D:1 | 24:D:2 | 24:D:3 | 24:D:4 | 24:12 | 24:13 | 24:14 | 24:15 | |
36 | 24:C:0 | 24:C:1 | 24:C:2 | 24:C:3 | 24:C:4 | 24:8 | 24:9 | 24:10 | 24:11 | 24:B:0 | 24:B:1 | 24:B:2 | 24:B:3 | |
37 | 24:B:4 | 24:4 | 24:5 | 24:6 | 24:7 | 24:A:0 | 24:A:1 | 24:A:2 | 24:A:3 | 24:A:4 | 24:0 | 24:1 | 24:2 | 24:3 |
38 | 23:D:0 | 23:D:1 | 23:D:2 | 23:D:3 | 23:D:4 | 23:12 | 23:13 | 23:14 | 23:15 | 23:C:0 | 23:C:1 | 23:C:2 | 23:C:3 | |
39 | 23:C:4 | 23:8 | 23:9 | 23:10 | 23:11 | 23:B:0 | 23:B:1 | 23:B:2 | 23:B:3 | 23:B:4 | 23:4 | 23:5 | 23:6 | 23:7 |
40 | 23:A:0 | 23:A:1 | 23:A:2 | 23:A:3 | 23:A:4 | 23:0 | 23:1 | 23:2 | 23:3 | 22:D:0 | 22:D:1 | 22:D:2 | 22:D:3 | 22:D:4 |
41 | 22:12 | 22:13 | 22:14 | 22:15 | 22:C:0 | 22:C:1 | 22:C:2 | 22:C:3 | 22:C:4 | 22:8 | 22:9 | 22:10 | 22:11 | |
42 | 22:B:0 | 22:B:1 | 22:B:2 | 22:B:3 | 22:B:4 | 22:4 | 22:5 | 22:6 | 22:7 | 22:A:0 | 22:A:1 | 22:A:2 | 22:A:3 | 22:A:4 |
43 | 22:0 | 22:1 | 22:2 | 22:3 | 21:D:0 | 21:D:1 | 21:D:2 | 21:D:3 | 21:D:4 | 21:12 | 21:13 | 21:14 | 21:15 | |
44 | 21:C:0 | 21:C:1 | 21:C:2 | 21:C:3 | 21:C:4 | 21:8 | 21:9 | 21:10 | 21:11 | 21:B:0 | 21:B:1 | 21:B:2 | 21:B:3 | |
45 | 21:B:4 | 21:4 | 21:5 | 21:6 | 21:7 | 21:A:0 | 21:A:1 | 21:A:2 | 21:A:3 | 21:A:4 | 21:0 | 21:1 | 21:2 | 21:3 |
46 | 20:D:0 | 20:D:1 | 20:D:2 | 20:D:3 | 20:D:4 | 20:12 | 20:13 | 20:14 | 20:15 | 20:C:0 | 20:C:1 | 20:C:2 | 20:C:3 | |
47 | 20:C:4 | 20:8 | 20:9 | 20:10 | 20:11 | 20:B:0 | 20:B:1 | 20:B:2 | 20:B:3 | 20:B:4 | 20:4 | 20:5 | 20:6 | 20:7 |
48 | 20:A:0 | 20:A:1 | 20:A:2 | 20:A:3 | 20:A:4 | 20:0 | 20:1 | 20:2 | 20:3 | 19:D:0 | 19:D:1 | 19:D:2 | 19:D:3 | 19:D:4 |
49 | 19:12 | 19:13 | 19:14 | 19:15 | 19:C:0 | 19:C:1 | 19:C:2 | 19:C:3 | 19:C:4 | 19:8 | 19:9 | 19:10 | 19:11 | |
50 | 19:B:0 | 19:B:1 | 19:B:2 | 19:B:3 | 19:B:4 | 19:4 | 19:5 | 19:6 | 19:7 | 19:A:0 | 19:A:1 | 19:A:2 | 19:A:3 | 19:A:4 |
51 | 19:0 | 19:1 | 19:2 | 19:3 | 18:D:0 | 18:D:1 | 18:D:2 | 18:D:3 | 18:D:4 | 18:12 | 18:13 | 18:14 | 18:15 | |
52 | 18:C:0 | 18:C:1 | 18:C:2 | 18:C:3 | 18:C:4 | 18:8 | 18:9 | 18:10 | 18:11 | 18:B:0 | 18:B:1 | 18:B:2 | 18:B:3 | |
53 | 18:B:4 | 18:4 | 18:5 | 18:6 | 18:7 | 18:A:0 | 18:A:1 | 18:A:2 | 18:A:3 | 18:A:4 | 18:0 | 18:1 | 18:2 | 18:3 |
54 | 17:D:0 | 17:D:1 | 17:D:2 | 17:D:3 | 17:D:4 | 17:12 | 17:13 | 17:14 | 17:15 | 17:C:0 | 17:C:1 | 17:C:2 | 17:C:3 | |
55 | 17:C:4 | 17:8 | 17:9 | 17:10 | 17:11 | 17:B:0 | 17:B:1 | 17:B:2 | 17:B:3 | 17:B:4 | 17:4 | 17:5 | 17:6 | 17:7 |
56 | 17:A:0 | 17:A:1 | 17:A:2 | 17:A:3 | 17:A:4 | 17:0 | 17:1 | 17:2 | 17:3 | 16:D:0 | 16:D:1 | 16:D:2 | 16:D:3 | 16:D:4 |
57 | 16:12 | 16:13 | 16:14 | 16:15 | 16:C:0 | 16:C:1 | 16:C:2 | 16:C:3 | 16:C:4 | 16:8 | 16:9 | 16:10 | 16:11 | |
58 | 16:B:0 | 16:B:1 | 16:B:2 | 16:B:3 | 16:B:4 | 16:4 | 16:5 | 16:6 | 16:7 | 16:A:0 | 16:A:1 | 16:A:2 | 16:A:3 | 16:A:4 |
59 | 16:0 | 16:1 | 16:2 | 16:3 | 15:D:0 | 15:D:1 | 15:D:2 | 15:D:3 | 15:D:4 | 15:12 | 15:13 | 15:14 | 15:15 | |
60 | 15:C:0 | 15:C:1 | 15:C:2 | 15:C:3 | 15:C:4 | 15:8 | 15:9 | 15:10 | 15:11 | 15:B:0 | 15:B:1 | 15:B:2 | 15:B:3 | |
61 | 15:B:4 | 15:4 | 15:5 | 15:6 | 15:7 | 15:A:0 | 15:A:1 | 15:A:2 | 15:A:3 | 15:A:4 | 15:0 | 15:1 | 15:2 | 15:3 |
62 | 14:D:0 | 14:D:1 | 14:D:2 | 14:D:3 | 14:D:4 | 14:12 | 14:13 | 14:14 | 14:15 | 14:C:0 | 14:C:1 | 14:C:2 | 14:C:3 | |
63 | 14:C:4 | 14:8 | 14:9 | 14:10 | 14:11 | 14:B:0 | 14:B:1 | 14:B:2 | 14:B:3 | 14:B:4 | 14:4 | 14:5 | 14:6 | 14:7 |
64 | 14:A:0 | 14:A:1 | 14:A:2 | 14:A:3 | 14:A:4 | 14:0 | 14:1 | 14:2 | 14:3 | 13:D:0 | 13:D:1 | 13:D:2 | 13:D:3 | 13:D:4 |
65 | 13:12 | 13:13 | 13:14 | 13:15 | 13:C:0 | 13:C:1 | 13:C:2 | 13:C:3 | 13:C:4 | 13:8 | 13:9 | 13:10 | 13:11 | |
66 | 13:B:0 | 13:B:1 | 13:B:2 | 13:B:3 | 13:B:4 | 13:4 | 13:5 | 13:6 | 13:7 | 13:A:0 | 13:A:1 | 13:A:2 | 13:A:3 | 13:A:4 |
67 | 13:0 | 13:1 | 13:2 | 13:3 | 12:D:0 | 12:D:1 | 12:D:2 | 12:D:3 | 12:D:4 | 12:12 | 12:13 | 12:14 | 12:15 | |
68 | 12:C:0 | 12:C:1 | 12:C:2 | 12:C:3 | 12:C:4 | 12:8 | 12:9 | 12:10 | 12:11 | 12:B:0 | 12:B:1 | 12:B:2 | 12:B:3 | |
69 | 12:B:4 | 12:4 | 12:5 | 12:6 | 12:7 | 12:A:0 | 12:A:1 | 12:A:2 | 12:A:3 | 12:A:4 | 12:0 | 12:1 | 12:2 | 12:3 |
70 | 11:D:0 | 11:D:1 | 11:D:2 | 11:D:3 | 11:D:4 | 11:12 | 11:13 | 11:14 | 11:15 | 11:C:0 | 11:C:1 | 11:C:2 | 11:C:3 | |
71 | 11:C:4 | 11:8 | 11:9 | 11:10 | 11:11 | 11:B:0 | 11:B:1 | 11:B:2 | 11:B:3 | 11:B:4 | 11:4 | 11:5 | 11:6 | 11:7 |
72 | 11:A:0 | 11:A:1 | 11:A:2 | 11:A:3 | 11:A:4 | 11:0 | 11:1 | 11:2 | 11:3 | 10:D:0 | 10:D:1 | 10:D:2 | 10:D:3 | 10:D:4 |
73 | 10:12 | 10:13 | 10:14 | 10:15 | 10:C:0 | 10:C:1 | 10:C:2 | 10:C:3 | 10:C:4 | 10:8 | 10:9 | 10:10 | 10:11 | |
74 | 10:B:0 | 10:B:1 | 10:B:2 | 10:B:3 | 10:B:4 | 10:4 | 10:5 | 10:6 | 10:7 | 10:A:0 | 10:A:1 | 10:A:2 | 10:A:3 | 10:A:4 |
75 | 10:0 | 10:1 | 10:2 | 10:3 | 9:D:0 | 9:D:1 | 9:D:2 | 9:D:3 | 9:D:4 | 9:12 | 9:13 | 9:14 | 9:15 | |
76 | 9:C:0 | 9:C:1 | 9:C:2 | 9:C:3 | 9:C:4 | 9:8 | 9:9 | 9:10 | 9:11 | 9:B:0 | 9:B:1 | 9:B:2 | 9:B:3 | |
77 | 9:B:4 | 9:4 | 9:5 | 9:6 | 9:7 | 9:A:0 | 9:A:1 | 9:A:2 | 9:A:3 | 9:A:4 | 9:0 | 9:1 | 9:2 | 9:3 |
78 | 8:D:0 | 8:D:1 | 8:D:2 | 8:D:3 | 8:D:4 | 8:12 | 8:13 | 8:14 | 8:15 | 8:C:0 | 8:C:1 | 8:C:2 | 8:C:3 | |
79 | 8:C:4 | 8:8 | 8:9 | 8:10 | 8:11 | 8:B:0 | 8:B:1 | 8:B:2 | 8:B:3 | 8:B:4 | 8:4 | 8:5 | 8:6 | 8:7 |
80 | 8:A:0 | 8:A:1 | 8:A:2 | 8:A:3 | 8:A:4 | 8:0 | 8:1 | 8:2 | 8:3 | 7:D:0 | 7:D:1 | 7:D:2 | 7:D:3 | 7:D:4 |
81 | 7:12 | 7:13 | 7:14 | 7:15 | 7:C:0 | 7:C:1 | 7:C:2 | 7:C:3 | 7:C:4 | 7:8 | 7:9 | 7:10 | 7:11 | |
82 | 7:B:0 | 7:B:1 | 7:B:2 | 7:B:3 | 7:B:4 | 7:4 | 7:5 | 7:6 | 7:7 | 7:A:0 | 7:A:1 | 7:A:2 | 7:A:3 | 7:A:4 |
83 | 7:0 | 7:1 | 7:2 | 7:3 | 6:D:0 | 6:D:1 | 6:D:2 | 6:D:3 | 6:D:4 | 6:12 | 6:13 | 6:14 | 6:15 | |
84 | 6:C:0 | 6:C:1 | 6:C:2 | 6:C:3 | 6:C:4 | 6:8 | 6:9 | 6:10 | 6:11 | 6:B:0 | 6:B:1 | 6:B:2 | 6:B:3 | |
85 | 6:B:4 | 6:4 | 6:5 | 6:6 | 6:7 | 6:A:0 | 6:A:1 | 6:A:2 | 6:A:3 | 6:A:4 | 6:0 | 6:1 | 6:2 | 6:3 |
86 | 5:D:0 | 5:D:1 | 5:D:2 | 5:D:3 | 5:D:4 | 5:12 | 5:13 | 5:14 | 5:15 | 5:C:0 | 5:C:1 | 5:C:2 | 5:C:3 | |
87 | 5:C:4 | 5:8 | 5:9 | 5:10 | 5:11 | 5:B:0 | 5:B:1 | 5:B:2 | 5:B:3 | 5:B:4 | 5:4 | 5:5 | 5:6 | 5:7 |
88 | 5:A:0 | 5:A:1 | 5:A:2 | 5:A:3 | 5:A:4 | 5:0 | 5:1 | 5:2 | 5:3 | 4:D:0 | 4:D:1 | 4:D:2 | 4:D:3 | 4:D:4 |
89 | 4:12 | 4:13 | 4:14 | 4:15 | 4:C:0 | 4:C:1 | 4:C:2 | 4:C:3 | 4:C:4 | 4:8 | 4:9 | 4:10 | 4:11 | |
90 | 4:B:0 | 4:B:1 | 4:B:2 | 4:B:3 | 4:B:4 | 4:4 | 4:5 | 4:6 | 4:7 | 4:A:0 | 4:A:1 | 4:A:2 | 4:A:3 | 4:A:4 |
91 | 4:0 | 4:1 | 4:2 | 4:3 | 3:D:0 | 3:D:1 | 3:D:2 | 3:D:3 | 3:D:4 | 3:12 | 3:13 | 3:14 | 3:15 | |
92 | 3:C:0 | 3:C:1 | 3:C:2 | 3:C:3 | 3:C:4 | 3:8 | 3:9 | 3:10 | 3:11 | 3:B:0 | 3:B:1 | 3:B:2 | 3:B:3 | |
93 | 3:B:4 | 3:4 | 3:5 | 3:6 | 3:7 | 3:A:0 | 3:A:1 | 3:A:2 | 3:A:3 | 3:A:4 | 3:0 | 3:1 | 3:2 | 3:3 |
94 | 2:D:0 | 2:D:1 | 2:D:2 | 2:D:3 | 2:D:4 | 2:12 | 2:13 | 2:14 | 2:15 | 2:C:0 | 2:C:1 | 2:C:2 | 2:C:3 | |
95 | 2:C:4 | 2:8 | 2:9 | 2:10 | 2:11 | 2:B:0 | 2:B:1 | 2:B:2 | 2:B:3 | 2:B:4 | 2:4 | 2:5 | 2:6 | 2:7 |
96 | 2:A:0 | 2:A:1 | 2:A:2 | 2:A:3 | 2:A:4 | 2:0 | 2:1 | 2:2 | 2:3 | 1:D:0 | 1:D:1 | 1:D:2 | 1:D:3 | 1:D:4 |
97 | 1:12 | 1:13 | 1:14 | 1:15 | 1:C:0 | 1:C:1 | 1:C:2 | 1:C:3 | 1:C:4 | 1:8 | 1:9 | 1:10 | 1:11 | |
98 | 1:B:0 | 1:B:1 | 1:B:2 | 1:B:3 | 1:B:4 | 1:4 | 1:5 | 1:6 | 1:7 | 1:A:0 | 1:A:1 | 1:A:2 | 1:A:3 | 1:A:4 |
99 | 1:0 | 1:1 | 1:2 | 1:3 | 0:D:0 | 0:D:1 | 0:D:2 | 0:D:3 | 0:D:4 | 0:12 | 0:13 | 0:14 | 0:15 | |
100 | 0:C:0 | 0:C:1 | 0:C:2 | 0:C:3 | 0:C:4 | 0:8 | 0:9 | 0:10 | 0:11 | 0:B:0 | 0:B:1 | 0:B:2 | 0:B:3 | |
101 | 0:B:4 | 0:4 | 0:5 | 0:6 | 0:7 | 0:A:0 | 0:A:1 | 0:A:2 | 0:A:3 | 0:A:4 | 0:0 | 0:1 | 0:2 | 0:3 |
FLOPSEL
For FLOPSEL, we repeat the above, but instead of varying the inputs, we vary if the LUT has a flip-flop after it, i.e., add this block:
always @(posedge CLK)
begin
PPS_OUT1 <= LUT_OUT;
end
I will spare you the details, but the process is similar.
Here is the updated table with FLOPSEL (<BLE>:F
).
BLE Map with FLOPSEL
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||||||
1 | ||||||||||||||
2 | ||||||||||||||
3 | ||||||||||||||
4 | ||||||||||||||
5 | ||||||||||||||
6 | ||||||||||||||
7 | ||||||||||||||
8 | ||||||||||||||
9 | ||||||||||||||
10 | ||||||||||||||
11 | ||||||||||||||
12 | ||||||||||||||
13 | ||||||||||||||
14 | ||||||||||||||
15 | ||||||||||||||
16 | 31:D:0 | 31:D:1 | 31:D:2 | 31:D:3 | 31:D:4 | |||||||||
17 | 31:12 | 31:13 | 31:14 | 31:15 | 31:F | 31:C:0 | 31:C:1 | 31:C:2 | 31:C:3 | 31:C:4 | 31:8 | 31:9 | 31:10 | 31:11 |
18 | 31:B:0 | 31:B:1 | 31:B:2 | 31:B:3 | 31:B:4 | 31:4 | 31:5 | 31:6 | 31:7 | 31:A:0 | 31:A:1 | 31:A:2 | 31:A:3 | 31:A:4 |
19 | 31:0 | 31:1 | 31:2 | 31:3 | 30:D:0 | 30:D:1 | 30:D:2 | 30:D:3 | 30:D:4 | 30:12 | 30:13 | 30:14 | 30:15 | 30:F |
20 | 30:C:0 | 30:C:1 | 30:C:2 | 30:C:3 | 30:C:4 | 30:8 | 30:9 | 30:10 | 30:11 | 30:B:0 | 30:B:1 | 30:B:2 | 30:B:3 | |
21 | 30:B:4 | 30:4 | 30:5 | 30:6 | 30:7 | 30:A:0 | 30:A:1 | 30:A:2 | 30:A:3 | 30:A:4 | 30:0 | 30:1 | 30:2 | 30:3 |
22 | 29:D:0 | 29:D:1 | 29:D:2 | 29:D:3 | 29:D:4 | 29:12 | 29:13 | 29:14 | 29:15 | 29:F | 29:C:0 | 29:C:1 | 29:C:2 | 29:C:3 |
23 | 29:C:4 | 29:8 | 29:9 | 29:10 | 29:11 | 29:B:0 | 29:B:1 | 29:B:2 | 29:B:3 | 29:B:4 | 29:4 | 29:5 | 29:6 | 29:7 |
24 | 29:A:0 | 29:A:1 | 29:A:2 | 29:A:3 | 29:A:4 | 29:0 | 29:1 | 29:2 | 29:3 | 28:D:0 | 28:D:1 | 28:D:2 | 28:D:3 | 28:D:4 |
25 | 28:12 | 28:13 | 28:14 | 28:15 | 28:F | 28:C:0 | 28:C:1 | 28:C:2 | 28:C:3 | 28:C:4 | 28:8 | 28:9 | 28:10 | 28:11 |
26 | 28:B:0 | 28:B:1 | 28:B:2 | 28:B:3 | 28:B:4 | 28:4 | 28:5 | 28:6 | 28:7 | 28:A:0 | 28:A:1 | 28:A:2 | 28:A:3 | 28:A:4 |
27 | 28:0 | 28:1 | 28:2 | 28:3 | 27:D:0 | 27:D:1 | 27:D:2 | 27:D:3 | 27:D:4 | 27:12 | 27:13 | 27:14 | 27:15 | 27:F |
28 | 27:C:0 | 27:C:1 | 27:C:2 | 27:C:3 | 27:C:4 | 27:8 | 27:9 | 27:10 | 27:11 | 27:B:0 | 27:B:1 | 27:B:2 | 27:B:3 | |
29 | 27:B:4 | 27:4 | 27:5 | 27:6 | 27:7 | 27:A:0 | 27:A:1 | 27:A:2 | 27:A:3 | 27:A:4 | 27:0 | 27:1 | 27:2 | 27:3 |
30 | 26:D:0 | 26:D:1 | 26:D:2 | 26:D:3 | 26:D:4 | 26:12 | 26:13 | 26:14 | 26:15 | 26:F | 26:C:0 | 26:C:1 | 26:C:2 | 26:C:3 |
31 | 26:C:4 | 26:8 | 26:9 | 26:10 | 26:11 | 26:B:0 | 26:B:1 | 26:B:2 | 26:B:3 | 26:B:4 | 26:4 | 26:5 | 26:6 | 26:7 |
32 | 26:A:0 | 26:A:1 | 26:A:2 | 26:A:3 | 26:A:4 | 26:0 | 26:1 | 26:2 | 26:3 | 25:D:0 | 25:D:1 | 25:D:2 | 25:D:3 | 25:D:4 |
33 | 25:12 | 25:13 | 25:14 | 25:15 | 25:F | 25:C:0 | 25:C:1 | 25:C:2 | 25:C:3 | 25:C:4 | 25:8 | 25:9 | 25:10 | 25:11 |
34 | 25:B:0 | 25:B:1 | 25:B:2 | 25:B:3 | 25:B:4 | 25:4 | 25:5 | 25:6 | 25:7 | 25:A:0 | 25:A:1 | 25:A:2 | 25:A:3 | 25:A:4 |
35 | 25:0 | 25:1 | 25:2 | 25:3 | 24:D:0 | 24:D:1 | 24:D:2 | 24:D:3 | 24:D:4 | 24:12 | 24:13 | 24:14 | 24:15 | 24:F |
36 | 24:C:0 | 24:C:1 | 24:C:2 | 24:C:3 | 24:C:4 | 24:8 | 24:9 | 24:10 | 24:11 | 24:B:0 | 24:B:1 | 24:B:2 | 24:B:3 | |
37 | 24:B:4 | 24:4 | 24:5 | 24:6 | 24:7 | 24:A:0 | 24:A:1 | 24:A:2 | 24:A:3 | 24:A:4 | 24:0 | 24:1 | 24:2 | 24:3 |
38 | 23:D:0 | 23:D:1 | 23:D:2 | 23:D:3 | 23:D:4 | 23:12 | 23:13 | 23:14 | 23:15 | 23:F | 23:C:0 | 23:C:1 | 23:C:2 | 23:C:3 |
39 | 23:C:4 | 23:8 | 23:9 | 23:10 | 23:11 | 23:B:0 | 23:B:1 | 23:B:2 | 23:B:3 | 23:B:4 | 23:4 | 23:5 | 23:6 | 23:7 |
40 | 23:A:0 | 23:A:1 | 23:A:2 | 23:A:3 | 23:A:4 | 23:0 | 23:1 | 23:2 | 23:3 | 22:D:0 | 22:D:1 | 22:D:2 | 22:D:3 | 22:D:4 |
41 | 22:12 | 22:13 | 22:14 | 22:15 | 22:F | 22:C:0 | 22:C:1 | 22:C:2 | 22:C:3 | 22:C:4 | 22:8 | 22:9 | 22:10 | 22:11 |
42 | 22:B:0 | 22:B:1 | 22:B:2 | 22:B:3 | 22:B:4 | 22:4 | 22:5 | 22:6 | 22:7 | 22:A:0 | 22:A:1 | 22:A:2 | 22:A:3 | 22:A:4 |
43 | 22:0 | 22:1 | 22:2 | 22:3 | 21:D:0 | 21:D:1 | 21:D:2 | 21:D:3 | 21:D:4 | 21:12 | 21:13 | 21:14 | 21:15 | 21:F |
44 | 21:C:0 | 21:C:1 | 21:C:2 | 21:C:3 | 21:C:4 | 21:8 | 21:9 | 21:10 | 21:11 | 21:B:0 | 21:B:1 | 21:B:2 | 21:B:3 | |
45 | 21:B:4 | 21:4 | 21:5 | 21:6 | 21:7 | 21:A:0 | 21:A:1 | 21:A:2 | 21:A:3 | 21:A:4 | 21:0 | 21:1 | 21:2 | 21:3 |
46 | 20:D:0 | 20:D:1 | 20:D:2 | 20:D:3 | 20:D:4 | 20:12 | 20:13 | 20:14 | 20:15 | 20:F | 20:C:0 | 20:C:1 | 20:C:2 | 20:C:3 |
47 | 20:C:4 | 20:8 | 20:9 | 20:10 | 20:11 | 20:B:0 | 20:B:1 | 20:B:2 | 20:B:3 | 20:B:4 | 20:4 | 20:5 | 20:6 | 20:7 |
48 | 20:A:0 | 20:A:1 | 20:A:2 | 20:A:3 | 20:A:4 | 20:0 | 20:1 | 20:2 | 20:3 | 19:D:0 | 19:D:1 | 19:D:2 | 19:D:3 | 19:D:4 |
49 | 19:12 | 19:13 | 19:14 | 19:15 | 19:F | 19:C:0 | 19:C:1 | 19:C:2 | 19:C:3 | 19:C:4 | 19:8 | 19:9 | 19:10 | 19:11 |
50 | 19:B:0 | 19:B:1 | 19:B:2 | 19:B:3 | 19:B:4 | 19:4 | 19:5 | 19:6 | 19:7 | 19:A:0 | 19:A:1 | 19:A:2 | 19:A:3 | 19:A:4 |
51 | 19:0 | 19:1 | 19:2 | 19:3 | 18:D:0 | 18:D:1 | 18:D:2 | 18:D:3 | 18:D:4 | 18:12 | 18:13 | 18:14 | 18:15 | 18:F |
52 | 18:C:0 | 18:C:1 | 18:C:2 | 18:C:3 | 18:C:4 | 18:8 | 18:9 | 18:10 | 18:11 | 18:B:0 | 18:B:1 | 18:B:2 | 18:B:3 | |
53 | 18:B:4 | 18:4 | 18:5 | 18:6 | 18:7 | 18:A:0 | 18:A:1 | 18:A:2 | 18:A:3 | 18:A:4 | 18:0 | 18:1 | 18:2 | 18:3 |
54 | 17:D:0 | 17:D:1 | 17:D:2 | 17:D:3 | 17:D:4 | 17:12 | 17:13 | 17:14 | 17:15 | 17:F | 17:C:0 | 17:C:1 | 17:C:2 | 17:C:3 |
55 | 17:C:4 | 17:8 | 17:9 | 17:10 | 17:11 | 17:B:0 | 17:B:1 | 17:B:2 | 17:B:3 | 17:B:4 | 17:4 | 17:5 | 17:6 | 17:7 |
56 | 17:A:0 | 17:A:1 | 17:A:2 | 17:A:3 | 17:A:4 | 17:0 | 17:1 | 17:2 | 17:3 | 16:D:0 | 16:D:1 | 16:D:2 | 16:D:3 | 16:D:4 |
57 | 16:12 | 16:13 | 16:14 | 16:15 | 16:F | 16:C:0 | 16:C:1 | 16:C:2 | 16:C:3 | 16:C:4 | 16:8 | 16:9 | 16:10 | 16:11 |
58 | 16:B:0 | 16:B:1 | 16:B:2 | 16:B:3 | 16:B:4 | 16:4 | 16:5 | 16:6 | 16:7 | 16:A:0 | 16:A:1 | 16:A:2 | 16:A:3 | 16:A:4 |
59 | 16:0 | 16:1 | 16:2 | 16:3 | 15:D:0 | 15:D:1 | 15:D:2 | 15:D:3 | 15:D:4 | 15:12 | 15:13 | 15:14 | 15:15 | 15:F |
60 | 15:C:0 | 15:C:1 | 15:C:2 | 15:C:3 | 15:C:4 | 15:8 | 15:9 | 15:10 | 15:11 | 15:B:0 | 15:B:1 | 15:B:2 | 15:B:3 | |
61 | 15:B:4 | 15:4 | 15:5 | 15:6 | 15:7 | 15:A:0 | 15:A:1 | 15:A:2 | 15:A:3 | 15:A:4 | 15:0 | 15:1 | 15:2 | 15:3 |
62 | 14:D:0 | 14:D:1 | 14:D:2 | 14:D:3 | 14:D:4 | 14:12 | 14:13 | 14:14 | 14:15 | 14:F | 14:C:0 | 14:C:1 | 14:C:2 | 14:C:3 |
63 | 14:C:4 | 14:8 | 14:9 | 14:10 | 14:11 | 14:B:0 | 14:B:1 | 14:B:2 | 14:B:3 | 14:B:4 | 14:4 | 14:5 | 14:6 | 14:7 |
64 | 14:A:0 | 14:A:1 | 14:A:2 | 14:A:3 | 14:A:4 | 14:0 | 14:1 | 14:2 | 14:3 | 13:D:0 | 13:D:1 | 13:D:2 | 13:D:3 | 13:D:4 |
65 | 13:12 | 13:13 | 13:14 | 13:15 | 13:F | 13:C:0 | 13:C:1 | 13:C:2 | 13:C:3 | 13:C:4 | 13:8 | 13:9 | 13:10 | 13:11 |
66 | 13:B:0 | 13:B:1 | 13:B:2 | 13:B:3 | 13:B:4 | 13:4 | 13:5 | 13:6 | 13:7 | 13:A:0 | 13:A:1 | 13:A:2 | 13:A:3 | 13:A:4 |
67 | 13:0 | 13:1 | 13:2 | 13:3 | 12:D:0 | 12:D:1 | 12:D:2 | 12:D:3 | 12:D:4 | 12:12 | 12:13 | 12:14 | 12:15 | 12:F |
68 | 12:C:0 | 12:C:1 | 12:C:2 | 12:C:3 | 12:C:4 | 12:8 | 12:9 | 12:10 | 12:11 | 12:B:0 | 12:B:1 | 12:B:2 | 12:B:3 | |
69 | 12:B:4 | 12:4 | 12:5 | 12:6 | 12:7 | 12:A:0 | 12:A:1 | 12:A:2 | 12:A:3 | 12:A:4 | 12:0 | 12:1 | 12:2 | 12:3 |
70 | 11:D:0 | 11:D:1 | 11:D:2 | 11:D:3 | 11:D:4 | 11:12 | 11:13 | 11:14 | 11:15 | 11:F | 11:C:0 | 11:C:1 | 11:C:2 | 11:C:3 |
71 | 11:C:4 | 11:8 | 11:9 | 11:10 | 11:11 | 11:B:0 | 11:B:1 | 11:B:2 | 11:B:3 | 11:B:4 | 11:4 | 11:5 | 11:6 | 11:7 |
72 | 11:A:0 | 11:A:1 | 11:A:2 | 11:A:3 | 11:A:4 | 11:0 | 11:1 | 11:2 | 11:3 | 10:D:0 | 10:D:1 | 10:D:2 | 10:D:3 | 10:D:4 |
73 | 10:12 | 10:13 | 10:14 | 10:15 | 10:F | 10:C:0 | 10:C:1 | 10:C:2 | 10:C:3 | 10:C:4 | 10:8 | 10:9 | 10:10 | 10:11 |
74 | 10:B:0 | 10:B:1 | 10:B:2 | 10:B:3 | 10:B:4 | 10:4 | 10:5 | 10:6 | 10:7 | 10:A:0 | 10:A:1 | 10:A:2 | 10:A:3 | 10:A:4 |
75 | 10:0 | 10:1 | 10:2 | 10:3 | 9:D:0 | 9:D:1 | 9:D:2 | 9:D:3 | 9:D:4 | 9:12 | 9:13 | 9:14 | 9:15 | 9:F |
76 | 9:C:0 | 9:C:1 | 9:C:2 | 9:C:3 | 9:C:4 | 9:8 | 9:9 | 9:10 | 9:11 | 9:B:0 | 9:B:1 | 9:B:2 | 9:B:3 | |
77 | 9:B:4 | 9:4 | 9:5 | 9:6 | 9:7 | 9:A:0 | 9:A:1 | 9:A:2 | 9:A:3 | 9:A:4 | 9:0 | 9:1 | 9:2 | 9:3 |
78 | 8:D:0 | 8:D:1 | 8:D:2 | 8:D:3 | 8:D:4 | 8:12 | 8:13 | 8:14 | 8:15 | 8:F | 8:C:0 | 8:C:1 | 8:C:2 | 8:C:3 |
79 | 8:C:4 | 8:8 | 8:9 | 8:10 | 8:11 | 8:B:0 | 8:B:1 | 8:B:2 | 8:B:3 | 8:B:4 | 8:4 | 8:5 | 8:6 | 8:7 |
80 | 8:A:0 | 8:A:1 | 8:A:2 | 8:A:3 | 8:A:4 | 8:0 | 8:1 | 8:2 | 8:3 | 7:D:0 | 7:D:1 | 7:D:2 | 7:D:3 | 7:D:4 |
81 | 7:12 | 7:13 | 7:14 | 7:15 | 7:F | 7:C:0 | 7:C:1 | 7:C:2 | 7:C:3 | 7:C:4 | 7:8 | 7:9 | 7:10 | 7:11 |
82 | 7:B:0 | 7:B:1 | 7:B:2 | 7:B:3 | 7:B:4 | 7:4 | 7:5 | 7:6 | 7:7 | 7:A:0 | 7:A:1 | 7:A:2 | 7:A:3 | 7:A:4 |
83 | 7:0 | 7:1 | 7:2 | 7:3 | 6:D:0 | 6:D:1 | 6:D:2 | 6:D:3 | 6:D:4 | 6:12 | 6:13 | 6:14 | 6:15 | 6:F |
84 | 6:C:0 | 6:C:1 | 6:C:2 | 6:C:3 | 6:C:4 | 6:8 | 6:9 | 6:10 | 6:11 | 6:B:0 | 6:B:1 | 6:B:2 | 6:B:3 | |
85 | 6:B:4 | 6:4 | 6:5 | 6:6 | 6:7 | 6:A:0 | 6:A:1 | 6:A:2 | 6:A:3 | 6:A:4 | 6:0 | 6:1 | 6:2 | 6:3 |
86 | 5:D:0 | 5:D:1 | 5:D:2 | 5:D:3 | 5:D:4 | 5:12 | 5:13 | 5:14 | 5:15 | 5:F | 5:C:0 | 5:C:1 | 5:C:2 | 5:C:3 |
87 | 5:C:4 | 5:8 | 5:9 | 5:10 | 5:11 | 5:B:0 | 5:B:1 | 5:B:2 | 5:B:3 | 5:B:4 | 5:4 | 5:5 | 5:6 | 5:7 |
88 | 5:A:0 | 5:A:1 | 5:A:2 | 5:A:3 | 5:A:4 | 5:0 | 5:1 | 5:2 | 5:3 | 4:D:0 | 4:D:1 | 4:D:2 | 4:D:3 | 4:D:4 |
89 | 4:12 | 4:13 | 4:14 | 4:15 | 4:F | 4:C:0 | 4:C:1 | 4:C:2 | 4:C:3 | 4:C:4 | 4:8 | 4:9 | 4:10 | 4:11 |
90 | 4:B:0 | 4:B:1 | 4:B:2 | 4:B:3 | 4:B:4 | 4:4 | 4:5 | 4:6 | 4:7 | 4:A:0 | 4:A:1 | 4:A:2 | 4:A:3 | 4:A:4 |
91 | 4:0 | 4:1 | 4:2 | 4:3 | 3:D:0 | 3:D:1 | 3:D:2 | 3:D:3 | 3:D:4 | 3:12 | 3:13 | 3:14 | 3:15 | 3:F |
92 | 3:C:0 | 3:C:1 | 3:C:2 | 3:C:3 | 3:C:4 | 3:8 | 3:9 | 3:10 | 3:11 | 3:B:0 | 3:B:1 | 3:B:2 | 3:B:3 | |
93 | 3:B:4 | 3:4 | 3:5 | 3:6 | 3:7 | 3:A:0 | 3:A:1 | 3:A:2 | 3:A:3 | 3:A:4 | 3:0 | 3:1 | 3:2 | 3:3 |
94 | 2:D:0 | 2:D:1 | 2:D:2 | 2:D:3 | 2:D:4 | 2:12 | 2:13 | 2:14 | 2:15 | 2:F | 2:C:0 | 2:C:1 | 2:C:2 | 2:C:3 |
95 | 2:C:4 | 2:8 | 2:9 | 2:10 | 2:11 | 2:B:0 | 2:B:1 | 2:B:2 | 2:B:3 | 2:B:4 | 2:4 | 2:5 | 2:6 | 2:7 |
96 | 2:A:0 | 2:A:1 | 2:A:2 | 2:A:3 | 2:A:4 | 2:0 | 2:1 | 2:2 | 2:3 | 1:D:0 | 1:D:1 | 1:D:2 | 1:D:3 | 1:D:4 |
97 | 1:12 | 1:13 | 1:14 | 1:15 | 1:F | 1:C:0 | 1:C:1 | 1:C:2 | 1:C:3 | 1:C:4 | 1:8 | 1:9 | 1:10 | 1:11 |
98 | 1:B:0 | 1:B:1 | 1:B:2 | 1:B:3 | 1:B:4 | 1:4 | 1:5 | 1:6 | 1:7 | 1:A:0 | 1:A:1 | 1:A:2 | 1:A:3 | 1:A:4 |
99 | 1:0 | 1:1 | 1:2 | 1:3 | 0:D:0 | 0:D:1 | 0:D:2 | 0:D:3 | 0:D:4 | 0:12 | 0:13 | 0:14 | 0:15 | 0:F |
100 | 0:C:0 | 0:C:1 | 0:C:2 | 0:C:3 | 0:C:4 | 0:8 | 0:9 | 0:10 | 0:11 | 0:B:0 | 0:B:1 | 0:B:2 | 0:B:3 | |
101 | 0:B:4 | 0:4 | 0:5 | 0:6 | 0:7 | 0:A:0 | 0:A:1 | 0:A:2 | 0:A:3 | 0:A:4 | 0:0 | 0:1 | 0:2 | 0:3 |
Counter
The counter has two inputs, Stop
and Reset
. They helpfully document the inputs, and the COUNT_IS_B1
etc. outputs. The only thing to figure out is the Counter latches.

Source: Microchip, PIC16F13145 Datasheet
I derived them similarly with the Bit Corr Tracker and random counter configurations.
class COUNTERIN(IntEnum):
CLB_BLE_31 = 0b11111
# ...
CLB_BLE_0 = 0b00000
class CNTMUX(IntEnum):
CNT0_COUNT_IS_0 = 0b000
CNT0_COUNT_IS_1 = 0b001
CNT0_COUNT_IS_2 = 0b010
CNT0_COUNT_IS_3 = 0b011
CNT0_COUNT_IS_4 = 0b100
CNT0_COUNT_IS_5 = 0b101
CNT0_COUNT_IS_6 = 0b110
CNT0_COUNT_IS_7 = 0b111
@dataclass
class COUNTER:
CNT_STOP: COUNTERIN = None
CNT_RESET: COUNTERIN = None
COUNT_IS_A1: CNTMUX = None
COUNT_IS_A2: CNTMUX = None
COUNT_IS_B1: CNTMUX = None
COUNT_IS_B2: CNTMUX = None
COUNT_IS_C1: CNTMUX = None
COUNT_IS_C2: CNTMUX = None
COUNT_IS_D1: CNTMUX = None
COUNT_IS_D2: CNTMUX = None
Full Counter Model and Bits
class COUNTERIN(IntEnum):
CLB_BLE_31 = 0b11111
CLB_BLE_30 = 0b11110
CLB_BLE_29 = 0b11101
CLB_BLE_28 = 0b11100
CLB_BLE_27 = 0b11011
CLB_BLE_26 = 0b11010
CLB_BLE_25 = 0b11001
CLB_BLE_24 = 0b11000
CLB_BLE_23 = 0b10111
CLB_BLE_22 = 0b10110
CLB_BLE_21 = 0b10101
CLB_BLE_20 = 0b10100
CLB_BLE_19 = 0b10011
CLB_BLE_18 = 0b10010
CLB_BLE_17 = 0b10001
CLB_BLE_16 = 0b10000
CLB_BLE_15 = 0b01111
CLB_BLE_14 = 0b01110
CLB_BLE_13 = 0b01101
CLB_BLE_12 = 0b01100
CLB_BLE_11 = 0b01011
CLB_BLE_10 = 0b01010
CLB_BLE_9 = 0b01001
CLB_BLE_8 = 0b01000
CLB_BLE_7 = 0b00111
CLB_BLE_6 = 0b00110
CLB_BLE_5 = 0b00101
CLB_BLE_4 = 0b00100
CLB_BLE_3 = 0b00011
CLB_BLE_2 = 0b00010
CLB_BLE_1 = 0b00001
CLB_BLE_0 = 0b00000
class CNTMUX(IntEnum):
CNT0_COUNT_IS_0 = 0b000
CNT0_COUNT_IS_1 = 0b001
CNT0_COUNT_IS_2 = 0b010
CNT0_COUNT_IS_3 = 0b011
CNT0_COUNT_IS_4 = 0b100
CNT0_COUNT_IS_5 = 0b101
CNT0_COUNT_IS_6 = 0b110
CNT0_COUNT_IS_7 = 0b111
@dataclass
class COUNTER:
CNT_STOP: COUNTERIN = None
CNT_RESET: COUNTERIN = None
COUNT_IS_A1: CNTMUX = None
COUNT_IS_A2: CNTMUX = None
COUNT_IS_B1: CNTMUX = None
COUNT_IS_B2: CNTMUX = None
COUNT_IS_C1: CNTMUX = None
COUNT_IS_C2: CNTMUX = None
COUNT_IS_D1: CNTMUX = None
COUNT_IS_D2: CNTMUX = None
COUNT_MUX_CFG_bits = {
"COUNT_IS_A1": {0: 41, 1: 42, 2: 43},
"COUNT_IS_A2": {0: 44, 1: 45, 2: 48},
"COUNT_IS_B1": {0: 49, 1: 50, 2: 51},
"COUNT_IS_B2": {0: 32, 1: 33, 2: 34},
"COUNT_IS_C1": {0: 35, 1: 36, 2: 37},
"COUNT_IS_C2": {0: 38, 1: 39, 2: 40},
"COUNT_IS_D1": {0: 21, 1: 22, 2: 23},
"COUNT_IS_D2": {0: 24, 1: 25, 2: 26},
}
Inputs
You might wonder how the IN0-15
inputs map to the listed inputs; the PIC16F13145 Datasheet
documents the below table (transcribed into Python). But how are those connected? Turns out there is an input mux, where you select which of the CLBIN
values you want to go to each mux; you can even route the same input to more than one INx
location.
In addition, you can select how you want the externally, possibly asynchronous logic to be synchronized with
the CLB global clock. There are 3 setting bits; you can see the CLB Input Synchronizer[0:2]
below.

Source: Microchip, PIC16F13145 Datasheet
I reverse engineered these similarly by selecting a few of the below, and generating random BLE configs that use the inputs to identify the IN mux locations.
class MUX_CFG:
INSYNC: CLBInputSync = None
CLBIN: CLBIN = None
class CLBInputSync(IntFlag):
DIRECT_IN = 0b000
SYNC = 0b100
EDGE_DETECT = 0b010
EDGE_INVERT = 0b001
class CLBIN(IntFlag):
RESERVED_BIT = 0b100000
ZERO = 0b11111
C2_OUT = 0b11100
# ...
CLBIN1PPS = 0b00001
CLBIN0PPS = 0b00000
IN Mux Model
@dataclass
class MUX_CFG:
INSYNC: CLBInputSync = None
CLBIN: CLBIN = None
class CLBInputSync(IntFlag):
DIRECT_IN = 0b000
SYNC = 0b100
EDGE_DETECT = 0b010
EDGE_INVERT = 0b001
class CLBIN(IntFlag):
RESERVED_BIT = 0b100000
ZERO = 0b11111
C2_OUT = 0b11100
C1_OUT = 0b11011
CLBSWIN_WRITE_HOLD = 0b11010
SCK1 = 0b11001
SDO1 = 0b11000
TX1 = 0b10111
CLC4_OUT = 0b10110
CLC3_OUT = 0b10101
CLC2_OUT = 0b10100
CLC1_OUT = 0b10011
IOCIF = 0b10010
PWM2_OUT = 0b10001
PWM1_OUT = 0b10000
CCP2_OUT = 0b01111
CCP1_OUT = 0b01110
TMR2_POSTSCALED_OUT = 0b01101
TMR1_OVERFLOW_OUT = 0b01100
TMR0_OVERFLOW_OUT = 0b01011
ADCRC = 0b01010
EXTOSC = 0b01001
MFINTOSC_32KHZ = 0b01000
MFINTOSC_500KHZ = 0b00111
LFINTOSC = 0b00110
HFINTOSC = 0b00101
FOSC = 0b00100
CLBIN3PPS = 0b00011
CLBIN2PPS = 0b00010
CLBIN1PPS = 0b00001
CLBIN0PPS = 0b00000
MUX_CFG_bits = {
0: {
"CLBIN": {0: 256, 1: 257, 2: 258, 3: 259, 4: 260, 5: 261},
"INSYNC": {0: 262, 1: 263, 2: 264},
},
1: {
"CLBIN": {0: 245, 1: 246, 2: 247, 3: 248, 4: 249, 5: 250},
"INSYNC": {0: 251, 1: 252, 2: 253},
},
2: {
"CLBIN": {0: 234, 1: 235, 2: 236, 3: 237, 4: 240, 5: 241},
"INSYNC": {0: 242, 1: 243, 2: 244},
},
3: {
"CLBIN": {0: 224, 1: 225, 2: 226, 3: 227, 4: 228, 5: 229},
"INSYNC": {0: 230, 1: 231, 2: 232},
},
4: {
"CLBIN": {0: 213, 1: 214, 2: 215, 3: 216, 4: 217, 5: 218},
"INSYNC": {0: 219, 1: 220, 2: 221},
},
5: {
"CLBIN": {0: 202, 1: 203, 2: 204, 3: 205, 4: 208, 5: 209},
"INSYNC": {0: 210, 1: 211, 2: 212},
},
6: {
"CLBIN": {0: 192, 1: 193, 2: 194, 3: 195, 4: 196, 5: 197},
"INSYNC": {0: 198, 1: 199, 2: 200},
},
7: {
"CLBIN": {0: 180, 1: 181, 2: 182, 3: 183, 4: 184, 5: 185},
"INSYNC": {0: 186, 1: 187, 2: 188},
},
8: {
"CLBIN": {0: 169, 1: 170, 2: 171, 3: 172, 4: 173, 5: 176},
"INSYNC": {0: 177, 1: 178, 2: 179},
},
9: {
"CLBIN": {0: 160, 1: 161, 2: 162, 3: 163, 4: 164, 5: 165},
"INSYNC": {0: 166, 1: 167, 2: 168},
},
10: {
"CLBIN": {0: 149, 1: 150, 2: 151, 3: 152, 4: 153, 5: 154},
"INSYNC": {0: 155, 1: 156, 2: 157},
},
11: {
"CLBIN": {0: 137, 1: 138, 2: 139, 3: 140, 4: 141, 5: 144},
"INSYNC": {0: 145, 1: 146, 2: 147},
},
12: {
"CLBIN": {0: 128, 1: 129, 2: 130, 3: 131, 4: 132, 5: 133},
"INSYNC": {0: 134, 1: 135, 2: 136},
},
13: {
"CLBIN": {0: 117, 1: 118, 2: 119, 3: 120, 4: 121, 5: 122},
"INSYNC": {0: 123, 1: 124, 2: 125},
},
14: {
"CLBIN": {0: 106, 1: 107, 2: 108, 3: 109, 4: 112, 5: 113},
"INSYNC": {0: 114, 1: 115, 2: 116},
},
15: {
"CLBIN": {0: 96, 1: 97, 2: 98, 3: 99, 4: 100, 5: 101},
"INSYNC": {0: 102, 1: 103, 2: 104},
},
}
Outputs
PPS Out
The PPS outputs are selected from within the bitstream, they helpfully provide the mapping.
Just needed to iterate and get the bit positions.
PPS Model/Bits
class CLBPPSOUT0(IntEnum):
CLB_BLE_0 = 0b00
CLB_BLE_1 = 0b01
CLB_BLE_2 = 0b10
CLB_BLE_3 = 0b11
class CLBPPSOUT1(IntEnum):
CLB_BLE_4 = 0b00
CLB_BLE_5 = 0b01
CLB_BLE_6 = 0b10
CLB_BLE_7 = 0b11
class CLBPPSOUT2(IntEnum):
CLB_BLE_8 = 0b00
CLB_BLE_9 = 0b01
CLB_BLE_10 = 0b10
CLB_BLE_11 = 0b11
class CLBPPSOUT3(IntEnum):
CLB_BLE_12 = 0b00
CLB_BLE_13 = 0b01
CLB_BLE_14 = 0b10
CLB_BLE_15 = 0b11
class CLBPPSOUT4(IntEnum):
CLB_BLE_16 = 0b00
CLB_BLE_17 = 0b01
CLB_BLE_18 = 0b10
CLB_BLE_19 = 0b11
class CLBPPSOUT5(IntEnum):
CLB_BLE_20 = 0b00
CLB_BLE_21 = 0b01
CLB_BLE_22 = 0b10
CLB_BLE_23 = 0b11
class CLBPPSOUT6(IntEnum):
CLB_BLE_24 = 0b00
CLB_BLE_25 = 0b01
CLB_BLE_26 = 0b10
CLB_BLE_27 = 0b11
class CLBPPSOUT7(IntEnum):
CLB_BLE_28 = 0b00
CLB_BLE_29 = 0b01
CLB_BLE_30 = 0b10
CLB_BLE_31 = 0b11
PPS_OUT_BITS = {
PPS_OUT0: {0: 85, 1: 86},
PPS_OUT1: {0: 87, 1: 88},
PPS_OUT2: {0: 74, 1: 75},
PPS_OUT3: {0: 76, 1: 77},
PPS_OUT4: {0: 64, 1: 65},
PPS_OUT5: {0: 66, 1: 67},
PPS_OUT6: {0: 52, 1: 53},
PPS_OUT7: {0: 54, 1: 55},
}
@dataclass
class PPS_OUT0:
OUT: CLBPPSOUT0 = None
@dataclass
class PPS_OUT1:
OUT: CLBPPSOUT1 = None
@dataclass
class PPS_OUT2:
OUT: CLBPPSOUT2 = None
@dataclass
class PPS_OUT3:
OUT: CLBPPSOUT3 = None
@dataclass
class PPS_OUT4:
OUT: CLBPPSOUT4 = None
@dataclass
class PPS_OUT5:
OUT: CLBPPSOUT5 = None
@dataclass
class PPS_OUT6:
OUT: CLBPPSOUT6 = None
@dataclass
class PPS_OUT7:
OUT: CLBPPSOUT7 = None
Interrupts
Just like PPS, they helpfully provide the mapping.
Just needed to iterate and get the bit positions.
IRQ Model/Bits
@dataclass
class IRQ_OUT0:
OUT: CLB1IF0 = None
@dataclass
class IRQ_OUT1:
OUT: CLB1IF1 = None
@dataclass
class IRQ_OUT2:
OUT: CLB1IF2 = None
@dataclass
class IRQ_OUT3:
OUT: CLB1IF3 = None
# Interrupts
class CLB1IF0(IntEnum):
CLB_BLE_0 = 0b000
CLB_BLE_1 = 0b001
CLB_BLE_2 = 0b010
CLB_BLE_3 = 0b011
CLB_BLE_4 = 0b100
CLB_BLE_5 = 0b101
CLB_BLE_6 = 0b110
CLB_BLE_7 = 0b111
class CLB1IF1(IntEnum):
CLB_BLE_8 = 0b000
CLB_BLE_9 = 0b001
CLB_BLE_10 = 0b010
CLB_BLE_11 = 0b011
CLB_BLE_12 = 0b100
CLB_BLE_13 = 0b101
CLB_BLE_14 = 0b110
CLB_BLE_15 = 0b111
class CLB1IF2(IntEnum):
CLB_BLE_16 = 0b000
CLB_BLE_17 = 0b001
CLB_BLE_18 = 0b010
CLB_BLE_19 = 0b011
CLB_BLE_20 = 0b100
CLB_BLE_21 = 0b101
CLB_BLE_22 = 0b110
CLB_BLE_23 = 0b111
class CLB1IF3(IntEnum):
CLB_BLE_24 = 0b000
CLB_BLE_25 = 0b001
CLB_BLE_26 = 0b010
CLB_BLE_27 = 0b011
CLB_BLE_28 = 0b100
CLB_BLE_29 = 0b101
CLB_BLE_30 = 0b110
CLB_BLE_31 = 0b111
IRQ_bits = {
0: {0: 89, 1: 90, 2: 91},
1: {0: 80, 1: 81, 2: 82},
2: {0: 68, 1: 69, 2: 70},
3: {0: 56, 1: 57, 2: 58},
}
PPS Output Enable
They helpfully document these
as well, I actually spent an embarrassingly long time trying to find where they were in the bits before reading the datasheet and they are set in a register outside the bistream
CLBPPSCON
.
ADC/CCP/TIMR0-3
The inputs to the ADC/CCP/TMRs are configured at the far end by bits in those peripherals.
- ADC can use BLE0-7 as conversion inputs
- The CCP can use BLE14-18 as capture inputs
- TIMR0 can use BLE31 as a clock source
- TIMR1 can use BLE0-3 as a clock source and BLE2-5 as gate inputs
- TIMR2 can use BLE7-10 as a clock source and BLE7-14 as reset sources
CLKDIV
CLKDIV
is just a bitfield that sets a flip-flop divider, which I manually iterated through
settings to identify.
class CLKDIV(IntEnum):
DIV_BY_1 = 0b000
DIV_BY_2 = 0b001
DIV_BY_4 = 0b010
DIV_BY_8 = 0b011
DIV_BY_16 = 0b100
DIV_BY_32 = 0b101
DIV_BY_64 = 0b110
DIV_BY_128 = 0b111
CLKDIV_bits = {0: 0, 1: 1, 2: 2}
Final Config Table
Final Config Table
Word | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | CKDIV:0 | CKDIV:1 | CKDIV:2 | CNT:S:0 | CNT:S:1 | CNT:S:2 | CNT:S:3 | CNT:S:4 | ||||||
1 | CNT:R:0 | CNT:R:1 | CNT:R:2 | CNT:R:3 | CNT:R:4 | CNT:D1:0 | CNT:D1:1 | CNT:D1:2 | CNT:D2:0 | CNT:D2:1 | CNT:D2:2 | |||
2 | CNT:B2:0 | CNT:B2:1 | CNT:B2:2 | CNT:C1:0 | CNT:C1:1 | CNT:C1:2 | CNT:C2:0 | CNT:C2:1 | CNT:C2:2 | CNT:A1:0 | CNT:A1:1 | CNT:A1:2 | CNT:A2:0 | CNT:A2:1 |
3 | CNT:A2:2 | CNT:B1:0 | CNT:B1:1 | CNT:B1:2 | PPS:6:0 | PPS:6:1 | PPS:7:0 | PPS:7:1 | IRQ3:0 | IRQ3:1 | IRQ3:2 | |||
4 | PPS:4:0 | PPS:4:1 | PPS:5:0 | PPS:5:1 | IRQ2:0 | IRQ2:1 | IRQ2:2 | PPS:2:0 | PPS:2:1 | PPS:3:0 | PPS:3:1 | |||
5 | IRQ1:0 | IRQ1:1 | IRQ1:2 | PPS:0:0 | PPS:0:1 | PPS:1:0 | PPS:1:1 | IRQ0:0 | IRQ0:1 | IRQ0:2 | ||||
6 | MX:15:I:0 | MX:15:I:1 | MX:15:I:2 | MX:15:I:3 | MX:15:I:4 | MX:15:I:5 | MX:15:S:0 | MX:15:S:1 | MX:15:S:2 | MX:14:I:0 | MX:14:I:1 | MX:14:I:2 | MX:14:I:3 | |
7 | MX:14:I:4 | MX:14:I:5 | MX:14:S:0 | MX:14:S:1 | MX:14:S:2 | MX:13:I:0 | MX:13:I:1 | MX:13:I:2 | MX:13:I:3 | MX:13:I:4 | MX:13:I:5 | MX:13:S:0 | MX:13:S:1 | MX:13:S:2 |
8 | MX:12:I:0 | MX:12:I:1 | MX:12:I:2 | MX:12:I:3 | MX:12:I:4 | MX:12:I:5 | MX:12:S:0 | MX:12:S:1 | MX:12:S:2 | MX:11:I:0 | MX:11:I:1 | MX:11:I:2 | MX:11:I:3 | MX:11:I:4 |
9 | MX:11:I:5 | MX:11:S:0 | MX:11:S:1 | MX:11:S:2 | MX:10:I:0 | MX:10:I:1 | MX:10:I:2 | MX:10:I:3 | MX:10:I:4 | MX:10:I:5 | MX:10:S:0 | MX:10:S:1 | MX:10:S:2 | |
10 | MX:9:I:0 | MX:9:I:1 | MX:9:I:2 | MX:9:I:3 | MX:9:I:4 | MX:9:I:5 | MX:9:S:0 | MX:9:S:1 | MX:9:S:2 | MX:8:I:0 | MX:8:I:1 | MX:8:I:2 | MX:8:I:3 | MX:8:I:4 |
11 | MX:8:I:5 | MX:8:S:0 | MX:8:S:1 | MX:8:S:2 | MX:7:I:0 | MX:7:I:1 | MX:7:I:2 | MX:7:I:3 | MX:7:I:4 | MX:7:I:5 | MX:7:S:0 | MX:7:S:1 | MX:7:S:2 | |
12 | MX:6:I:0 | MX:6:I:1 | MX:6:I:2 | MX:6:I:3 | MX:6:I:4 | MX:6:I:5 | MX:6:S:0 | MX:6:S:1 | MX:6:S:2 | MX:5:I:0 | MX:5:I:1 | MX:5:I:2 | MX:5:I:3 | |
13 | MX:5:I:4 | MX:5:I:5 | MX:5:S:0 | MX:5:S:1 | MX:5:S:2 | MX:4:I:0 | MX:4:I:1 | MX:4:I:2 | MX:4:I:3 | MX:4:I:4 | MX:4:I:5 | MX:4:S:0 | MX:4:S:1 | MX:4:S:2 |
14 | MX:3:I:0 | MX:3:I:1 | MX:3:I:2 | MX:3:I:3 | MX:3:I:4 | MX:3:I:5 | MX:3:S:0 | MX:3:S:1 | MX:3:S:2 | MX:2:I:0 | MX:2:I:1 | MX:2:I:2 | MX:2:I:3 | |
15 | MX:2:I:4 | MX:2:I:5 | MX:2:S:0 | MX:2:S:1 | MX:2:S:2 | MX:1:I:0 | MX:1:I:1 | MX:1:I:2 | MX:1:I:3 | MX:1:I:4 | MX:1:I:5 | MX:1:S:0 | MX:1:S:1 | MX:1:S:2 |
16 | MX:0:I:0 | MX:0:I:1 | MX:0:I:2 | MX:0:I:3 | MX:0:I:4 | MX:0:I:5 | MX:0:S:0 | MX:0:S:1 | MX:0:S:2 | 31:D:0 | 31:D:1 | 31:D:2 | 31:D:3 | 31:D:4 |
17 | 31:12 | 31:13 | 31:14 | 31:15 | 31:FS | 31:C:0 | 31:C:1 | 31:C:2 | 31:C:3 | 31:C:4 | 31:8 | 31:9 | 31:10 | 31:11 |
18 | 31:B:0 | 31:B:1 | 31:B:2 | 31:B:3 | 31:B:4 | 31:4 | 31:5 | 31:6 | 31:7 | 31:A:0 | 31:A:1 | 31:A:2 | 31:A:3 | 31:A:4 |
19 | 31:0 | 31:1 | 31:2 | 31:3 | 30:D:0 | 30:D:1 | 30:D:2 | 30:D:3 | 30:D:4 | 30:12 | 30:13 | 30:14 | 30:15 | 30:FS |
20 | 30:C:0 | 30:C:1 | 30:C:2 | 30:C:3 | 30:C:4 | 30:8 | 30:9 | 30:10 | 30:11 | 30:B:0 | 30:B:1 | 30:B:2 | 30:B:3 | |
21 | 30:B:4 | 30:4 | 30:5 | 30:6 | 30:7 | 30:A:0 | 30:A:1 | 30:A:2 | 30:A:3 | 30:A:4 | 30:0 | 30:1 | 30:2 | 30:3 |
22 | 29:D:0 | 29:D:1 | 29:D:2 | 29:D:3 | 29:D:4 | 29:12 | 29:13 | 29:14 | 29:15 | 29:FS | 29:C:0 | 29:C:1 | 29:C:2 | 29:C:3 |
23 | 29:C:4 | 29:8 | 29:9 | 29:10 | 29:11 | 29:B:0 | 29:B:1 | 29:B:2 | 29:B:3 | 29:B:4 | 29:4 | 29:5 | 29:6 | 29:7 |
24 | 29:A:0 | 29:A:1 | 29:A:2 | 29:A:3 | 29:A:4 | 29:0 | 29:1 | 29:2 | 29:3 | 28:D:0 | 28:D:1 | 28:D:2 | 28:D:3 | 28:D:4 |
25 | 28:12 | 28:13 | 28:14 | 28:15 | 28:FS | 28:C:0 | 28:C:1 | 28:C:2 | 28:C:3 | 28:C:4 | 28:8 | 28:9 | 28:10 | 28:11 |
26 | 28:B:0 | 28:B:1 | 28:B:2 | 28:B:3 | 28:B:4 | 28:4 | 28:5 | 28:6 | 28:7 | 28:A:0 | 28:A:1 | 28:A:2 | 28:A:3 | 28:A:4 |
27 | 28:0 | 28:1 | 28:2 | 28:3 | 27:D:0 | 27:D:1 | 27:D:2 | 27:D:3 | 27:D:4 | 27:12 | 27:13 | 27:14 | 27:15 | 27:FS |
28 | 27:C:0 | 27:C:1 | 27:C:2 | 27:C:3 | 27:C:4 | 27:8 | 27:9 | 27:10 | 27:11 | 27:B:0 | 27:B:1 | 27:B:2 | 27:B:3 | |
29 | 27:B:4 | 27:4 | 27:5 | 27:6 | 27:7 | 27:A:0 | 27:A:1 | 27:A:2 | 27:A:3 | 27:A:4 | 27:0 | 27:1 | 27:2 | 27:3 |
30 | 26:D:0 | 26:D:1 | 26:D:2 | 26:D:3 | 26:D:4 | 26:12 | 26:13 | 26:14 | 26:15 | 26:FS | 26:C:0 | 26:C:1 | 26:C:2 | 26:C:3 |
31 | 26:C:4 | 26:8 | 26:9 | 26:10 | 26:11 | 26:B:0 | 26:B:1 | 26:B:2 | 26:B:3 | 26:B:4 | 26:4 | 26:5 | 26:6 | 26:7 |
32 | 26:A:0 | 26:A:1 | 26:A:2 | 26:A:3 | 26:A:4 | 26:0 | 26:1 | 26:2 | 26:3 | 25:D:0 | 25:D:1 | 25:D:2 | 25:D:3 | 25:D:4 |
33 | 25:12 | 25:13 | 25:14 | 25:15 | 25:FS | 25:C:0 | 25:C:1 | 25:C:2 | 25:C:3 | 25:C:4 | 25:8 | 25:9 | 25:10 | 25:11 |
34 | 25:B:0 | 25:B:1 | 25:B:2 | 25:B:3 | 25:B:4 | 25:4 | 25:5 | 25:6 | 25:7 | 25:A:0 | 25:A:1 | 25:A:2 | 25:A:3 | 25:A:4 |
35 | 25:0 | 25:1 | 25:2 | 25:3 | 24:D:0 | 24:D:1 | 24:D:2 | 24:D:3 | 24:D:4 | 24:12 | 24:13 | 24:14 | 24:15 | 24:FS |
36 | 24:C:0 | 24:C:1 | 24:C:2 | 24:C:3 | 24:C:4 | 24:8 | 24:9 | 24:10 | 24:11 | 24:B:0 | 24:B:1 | 24:B:2 | 24:B:3 | |
37 | 24:B:4 | 24:4 | 24:5 | 24:6 | 24:7 | 24:A:0 | 24:A:1 | 24:A:2 | 24:A:3 | 24:A:4 | 24:0 | 24:1 | 24:2 | 24:3 |
38 | 23:D:0 | 23:D:1 | 23:D:2 | 23:D:3 | 23:D:4 | 23:12 | 23:13 | 23:14 | 23:15 | 23:FS | 23:C:0 | 23:C:1 | 23:C:2 | 23:C:3 |
39 | 23:C:4 | 23:8 | 23:9 | 23:10 | 23:11 | 23:B:0 | 23:B:1 | 23:B:2 | 23:B:3 | 23:B:4 | 23:4 | 23:5 | 23:6 | 23:7 |
40 | 23:A:0 | 23:A:1 | 23:A:2 | 23:A:3 | 23:A:4 | 23:0 | 23:1 | 23:2 | 23:3 | 22:D:0 | 22:D:1 | 22:D:2 | 22:D:3 | 22:D:4 |
41 | 22:12 | 22:13 | 22:14 | 22:15 | 22:FS | 22:C:0 | 22:C:1 | 22:C:2 | 22:C:3 | 22:C:4 | 22:8 | 22:9 | 22:10 | 22:11 |
42 | 22:B:0 | 22:B:1 | 22:B:2 | 22:B:3 | 22:B:4 | 22:4 | 22:5 | 22:6 | 22:7 | 22:A:0 | 22:A:1 | 22:A:2 | 22:A:3 | 22:A:4 |
43 | 22:0 | 22:1 | 22:2 | 22:3 | 21:D:0 | 21:D:1 | 21:D:2 | 21:D:3 | 21:D:4 | 21:12 | 21:13 | 21:14 | 21:15 | 21:FS |
44 | 21:C:0 | 21:C:1 | 21:C:2 | 21:C:3 | 21:C:4 | 21:8 | 21:9 | 21:10 | 21:11 | 21:B:0 | 21:B:1 | 21:B:2 | 21:B:3 | |
45 | 21:B:4 | 21:4 | 21:5 | 21:6 | 21:7 | 21:A:0 | 21:A:1 | 21:A:2 | 21:A:3 | 21:A:4 | 21:0 | 21:1 | 21:2 | 21:3 |
46 | 20:D:0 | 20:D:1 | 20:D:2 | 20:D:3 | 20:D:4 | 20:12 | 20:13 | 20:14 | 20:15 | 20:FS | 20:C:0 | 20:C:1 | 20:C:2 | 20:C:3 |
47 | 20:C:4 | 20:8 | 20:9 | 20:10 | 20:11 | 20:B:0 | 20:B:1 | 20:B:2 | 20:B:3 | 20:B:4 | 20:4 | 20:5 | 20:6 | 20:7 |
48 | 20:A:0 | 20:A:1 | 20:A:2 | 20:A:3 | 20:A:4 | 20:0 | 20:1 | 20:2 | 20:3 | 19:D:0 | 19:D:1 | 19:D:2 | 19:D:3 | 19:D:4 |
49 | 19:12 | 19:13 | 19:14 | 19:15 | 19:FS | 19:C:0 | 19:C:1 | 19:C:2 | 19:C:3 | 19:C:4 | 19:8 | 19:9 | 19:10 | 19:11 |
50 | 19:B:0 | 19:B:1 | 19:B:2 | 19:B:3 | 19:B:4 | 19:4 | 19:5 | 19:6 | 19:7 | 19:A:0 | 19:A:1 | 19:A:2 | 19:A:3 | 19:A:4 |
51 | 19:0 | 19:1 | 19:2 | 19:3 | 18:D:0 | 18:D:1 | 18:D:2 | 18:D:3 | 18:D:4 | 18:12 | 18:13 | 18:14 | 18:15 | 18:FS |
52 | 18:C:0 | 18:C:1 | 18:C:2 | 18:C:3 | 18:C:4 | 18:8 | 18:9 | 18:10 | 18:11 | 18:B:0 | 18:B:1 | 18:B:2 | 18:B:3 | |
53 | 18:B:4 | 18:4 | 18:5 | 18:6 | 18:7 | 18:A:0 | 18:A:1 | 18:A:2 | 18:A:3 | 18:A:4 | 18:0 | 18:1 | 18:2 | 18:3 |
54 | 17:D:0 | 17:D:1 | 17:D:2 | 17:D:3 | 17:D:4 | 17:12 | 17:13 | 17:14 | 17:15 | 17:FS | 17:C:0 | 17:C:1 | 17:C:2 | 17:C:3 |
55 | 17:C:4 | 17:8 | 17:9 | 17:10 | 17:11 | 17:B:0 | 17:B:1 | 17:B:2 | 17:B:3 | 17:B:4 | 17:4 | 17:5 | 17:6 | 17:7 |
56 | 17:A:0 | 17:A:1 | 17:A:2 | 17:A:3 | 17:A:4 | 17:0 | 17:1 | 17:2 | 17:3 | 16:D:0 | 16:D:1 | 16:D:2 | 16:D:3 | 16:D:4 |
57 | 16:12 | 16:13 | 16:14 | 16:15 | 16:FS | 16:C:0 | 16:C:1 | 16:C:2 | 16:C:3 | 16:C:4 | 16:8 | 16:9 | 16:10 | 16:11 |
58 | 16:B:0 | 16:B:1 | 16:B:2 | 16:B:3 | 16:B:4 | 16:4 | 16:5 | 16:6 | 16:7 | 16:A:0 | 16:A:1 | 16:A:2 | 16:A:3 | 16:A:4 |
59 | 16:0 | 16:1 | 16:2 | 16:3 | 15:D:0 | 15:D:1 | 15:D:2 | 15:D:3 | 15:D:4 | 15:12 | 15:13 | 15:14 | 15:15 | 15:FS |
60 | 15:C:0 | 15:C:1 | 15:C:2 | 15:C:3 | 15:C:4 | 15:8 | 15:9 | 15:10 | 15:11 | 15:B:0 | 15:B:1 | 15:B:2 | 15:B:3 | |
61 | 15:B:4 | 15:4 | 15:5 | 15:6 | 15:7 | 15:A:0 | 15:A:1 | 15:A:2 | 15:A:3 | 15:A:4 | 15:0 | 15:1 | 15:2 | 15:3 |
62 | 14:D:0 | 14:D:1 | 14:D:2 | 14:D:3 | 14:D:4 | 14:12 | 14:13 | 14:14 | 14:15 | 14:FS | 14:C:0 | 14:C:1 | 14:C:2 | 14:C:3 |
63 | 14:C:4 | 14:8 | 14:9 | 14:10 | 14:11 | 14:B:0 | 14:B:1 | 14:B:2 | 14:B:3 | 14:B:4 | 14:4 | 14:5 | 14:6 | 14:7 |
64 | 14:A:0 | 14:A:1 | 14:A:2 | 14:A:3 | 14:A:4 | 14:0 | 14:1 | 14:2 | 14:3 | 13:D:0 | 13:D:1 | 13:D:2 | 13:D:3 | 13:D:4 |
65 | 13:12 | 13:13 | 13:14 | 13:15 | 13:FS | 13:C:0 | 13:C:1 | 13:C:2 | 13:C:3 | 13:C:4 | 13:8 | 13:9 | 13:10 | 13:11 |
66 | 13:B:0 | 13:B:1 | 13:B:2 | 13:B:3 | 13:B:4 | 13:4 | 13:5 | 13:6 | 13:7 | 13:A:0 | 13:A:1 | 13:A:2 | 13:A:3 | 13:A:4 |
67 | 13:0 | 13:1 | 13:2 | 13:3 | 12:D:0 | 12:D:1 | 12:D:2 | 12:D:3 | 12:D:4 | 12:12 | 12:13 | 12:14 | 12:15 | 12:FS |
68 | 12:C:0 | 12:C:1 | 12:C:2 | 12:C:3 | 12:C:4 | 12:8 | 12:9 | 12:10 | 12:11 | 12:B:0 | 12:B:1 | 12:B:2 | 12:B:3 | |
69 | 12:B:4 | 12:4 | 12:5 | 12:6 | 12:7 | 12:A:0 | 12:A:1 | 12:A:2 | 12:A:3 | 12:A:4 | 12:0 | 12:1 | 12:2 | 12:3 |
70 | 11:D:0 | 11:D:1 | 11:D:2 | 11:D:3 | 11:D:4 | 11:12 | 11:13 | 11:14 | 11:15 | 11:FS | 11:C:0 | 11:C:1 | 11:C:2 | 11:C:3 |
71 | 11:C:4 | 11:8 | 11:9 | 11:10 | 11:11 | 11:B:0 | 11:B:1 | 11:B:2 | 11:B:3 | 11:B:4 | 11:4 | 11:5 | 11:6 | 11:7 |
72 | 11:A:0 | 11:A:1 | 11:A:2 | 11:A:3 | 11:A:4 | 11:0 | 11:1 | 11:2 | 11:3 | 10:D:0 | 10:D:1 | 10:D:2 | 10:D:3 | 10:D:4 |
73 | 10:12 | 10:13 | 10:14 | 10:15 | 10:FS | 10:C:0 | 10:C:1 | 10:C:2 | 10:C:3 | 10:C:4 | 10:8 | 10:9 | 10:10 | 10:11 |
74 | 10:B:0 | 10:B:1 | 10:B:2 | 10:B:3 | 10:B:4 | 10:4 | 10:5 | 10:6 | 10:7 | 10:A:0 | 10:A:1 | 10:A:2 | 10:A:3 | 10:A:4 |
75 | 10:0 | 10:1 | 10:2 | 10:3 | 9:D:0 | 9:D:1 | 9:D:2 | 9:D:3 | 9:D:4 | 9:12 | 9:13 | 9:14 | 9:15 | 9:FS |
76 | 9:C:0 | 9:C:1 | 9:C:2 | 9:C:3 | 9:C:4 | 9:8 | 9:9 | 9:10 | 9:11 | 9:B:0 | 9:B:1 | 9:B:2 | 9:B:3 | |
77 | 9:B:4 | 9:4 | 9:5 | 9:6 | 9:7 | 9:A:0 | 9:A:1 | 9:A:2 | 9:A:3 | 9:A:4 | 9:0 | 9:1 | 9:2 | 9:3 |
78 | 8:D:0 | 8:D:1 | 8:D:2 | 8:D:3 | 8:D:4 | 8:12 | 8:13 | 8:14 | 8:15 | 8:FS | 8:C:0 | 8:C:1 | 8:C:2 | 8:C:3 |
79 | 8:C:4 | 8:8 | 8:9 | 8:10 | 8:11 | 8:B:0 | 8:B:1 | 8:B:2 | 8:B:3 | 8:B:4 | 8:4 | 8:5 | 8:6 | 8:7 |
80 | 8:A:0 | 8:A:1 | 8:A:2 | 8:A:3 | 8:A:4 | 8:0 | 8:1 | 8:2 | 8:3 | 7:D:0 | 7:D:1 | 7:D:2 | 7:D:3 | 7:D:4 |
81 | 7:12 | 7:13 | 7:14 | 7:15 | 7:FS | 7:C:0 | 7:C:1 | 7:C:2 | 7:C:3 | 7:C:4 | 7:8 | 7:9 | 7:10 | 7:11 |
82 | 7:B:0 | 7:B:1 | 7:B:2 | 7:B:3 | 7:B:4 | 7:4 | 7:5 | 7:6 | 7:7 | 7:A:0 | 7:A:1 | 7:A:2 | 7:A:3 | 7:A:4 |
83 | 7:0 | 7:1 | 7:2 | 7:3 | 6:D:0 | 6:D:1 | 6:D:2 | 6:D:3 | 6:D:4 | 6:12 | 6:13 | 6:14 | 6:15 | 6:FS |
84 | 6:C:0 | 6:C:1 | 6:C:2 | 6:C:3 | 6:C:4 | 6:8 | 6:9 | 6:10 | 6:11 | 6:B:0 | 6:B:1 | 6:B:2 | 6:B:3 | |
85 | 6:B:4 | 6:4 | 6:5 | 6:6 | 6:7 | 6:A:0 | 6:A:1 | 6:A:2 | 6:A:3 | 6:A:4 | 6:0 | 6:1 | 6:2 | 6:3 |
86 | 5:D:0 | 5:D:1 | 5:D:2 | 5:D:3 | 5:D:4 | 5:12 | 5:13 | 5:14 | 5:15 | 5:FS | 5:C:0 | 5:C:1 | 5:C:2 | 5:C:3 |
87 | 5:C:4 | 5:8 | 5:9 | 5:10 | 5:11 | 5:B:0 | 5:B:1 | 5:B:2 | 5:B:3 | 5:B:4 | 5:4 | 5:5 | 5:6 | 5:7 |
88 | 5:A:0 | 5:A:1 | 5:A:2 | 5:A:3 | 5:A:4 | 5:0 | 5:1 | 5:2 | 5:3 | 4:D:0 | 4:D:1 | 4:D:2 | 4:D:3 | 4:D:4 |
89 | 4:12 | 4:13 | 4:14 | 4:15 | 4:FS | 4:C:0 | 4:C:1 | 4:C:2 | 4:C:3 | 4:C:4 | 4:8 | 4:9 | 4:10 | 4:11 |
90 | 4:B:0 | 4:B:1 | 4:B:2 | 4:B:3 | 4:B:4 | 4:4 | 4:5 | 4:6 | 4:7 | 4:A:0 | 4:A:1 | 4:A:2 | 4:A:3 | 4:A:4 |
91 | 4:0 | 4:1 | 4:2 | 4:3 | 3:D:0 | 3:D:1 | 3:D:2 | 3:D:3 | 3:D:4 | 3:12 | 3:13 | 3:14 | 3:15 | 3:FS |
92 | 3:C:0 | 3:C:1 | 3:C:2 | 3:C:3 | 3:C:4 | 3:8 | 3:9 | 3:10 | 3:11 | 3:B:0 | 3:B:1 | 3:B:2 | 3:B:3 | |
93 | 3:B:4 | 3:4 | 3:5 | 3:6 | 3:7 | 3:A:0 | 3:A:1 | 3:A:2 | 3:A:3 | 3:A:4 | 3:0 | 3:1 | 3:2 | 3:3 |
94 | 2:D:0 | 2:D:1 | 2:D:2 | 2:D:3 | 2:D:4 | 2:12 | 2:13 | 2:14 | 2:15 | 2:FS | 2:C:0 | 2:C:1 | 2:C:2 | 2:C:3 |
95 | 2:C:4 | 2:8 | 2:9 | 2:10 | 2:11 | 2:B:0 | 2:B:1 | 2:B:2 | 2:B:3 | 2:B:4 | 2:4 | 2:5 | 2:6 | 2:7 |
96 | 2:A:0 | 2:A:1 | 2:A:2 | 2:A:3 | 2:A:4 | 2:0 | 2:1 | 2:2 | 2:3 | 1:D:0 | 1:D:1 | 1:D:2 | 1:D:3 | 1:D:4 |
97 | 1:12 | 1:13 | 1:14 | 1:15 | 1:FS | 1:C:0 | 1:C:1 | 1:C:2 | 1:C:3 | 1:C:4 | 1:8 | 1:9 | 1:10 | 1:11 |
98 | 1:B:0 | 1:B:1 | 1:B:2 | 1:B:3 | 1:B:4 | 1:4 | 1:5 | 1:6 | 1:7 | 1:A:0 | 1:A:1 | 1:A:2 | 1:A:3 | 1:A:4 |
99 | 1:0 | 1:1 | 1:2 | 1:3 | 0:D:0 | 0:D:1 | 0:D:2 | 0:D:3 | 0:D:4 | 0:12 | 0:13 | 0:14 | 0:15 | 0:FS |
100 | 0:C:0 | 0:C:1 | 0:C:2 | 0:C:3 | 0:C:4 | 0:8 | 0:9 | 0:10 | 0:11 | 0:B:0 | 0:B:1 | 0:B:2 | 0:B:3 | |
101 | 0:B:4 | 0:4 | 0:5 | 0:6 | 0:7 | 0:A:0 | 0:A:1 | 0:A:2 | 0:A:3 | 0:A:4 | 0:0 | 0:1 | 0:2 | 0:3 |
Try it yourself in browser here.
You can live in the browser generate bitstreams and visualize them.