bitlab 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {bitlab-0.1.0 → bitlab-0.2.0}/CHANGELOG.md +34 -7
  2. bitlab-0.2.0/PKG-INFO +325 -0
  3. bitlab-0.2.0/README.md +301 -0
  4. {bitlab-0.1.0 → bitlab-0.2.0}/pyproject.toml +4 -1
  5. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/__init__.py +7 -5
  6. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/cli.py +66 -0
  7. bitlab-0.2.0/src/bitlab/registers/__init__.py +25 -0
  8. bitlab-0.2.0/src/bitlab/registers/codegen.py +128 -0
  9. bitlab-0.2.0/src/bitlab/registers/core.py +181 -0
  10. bitlab-0.2.0/src/bitlab/registers/explain.py +74 -0
  11. bitlab-0.2.0/tests/test_registers_codegen.py +123 -0
  12. bitlab-0.2.0/tests/test_registers_core.py +94 -0
  13. bitlab-0.2.0/tests/test_registers_explain.py +33 -0
  14. bitlab-0.1.0/PKG-INFO +0 -266
  15. bitlab-0.1.0/README.md +0 -242
  16. {bitlab-0.1.0 → bitlab-0.2.0}/.github/workflows/ci.yml +0 -0
  17. {bitlab-0.1.0 → bitlab-0.2.0}/.github/workflows/publish.yml +0 -0
  18. {bitlab-0.1.0 → bitlab-0.2.0}/.gitignore +0 -0
  19. {bitlab-0.1.0 → bitlab-0.2.0}/CONTRIBUTING.md +0 -0
  20. {bitlab-0.1.0 → bitlab-0.2.0}/LICENSE +0 -0
  21. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/bitutils/__init__.py +0 -0
  22. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/bitutils/core.py +0 -0
  23. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/__init__.py +0 -0
  24. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/codegen.py +0 -0
  25. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/engine.py +0 -0
  26. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/explain.py +0 -0
  27. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/functions.py +0 -0
  28. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/crc/presets.py +0 -0
  29. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/parity/__init__.py +0 -0
  30. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/parity/hamming.py +0 -0
  31. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/parity/parity.py +0 -0
  32. {bitlab-0.1.0 → bitlab-0.2.0}/src/bitlab/py.typed +0 -0
  33. {bitlab-0.1.0 → bitlab-0.2.0}/tests/__init__.py +0 -0
  34. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_bitutils.py +0 -0
  35. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_crc_codegen.py +0 -0
  36. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_crc_engine.py +0 -0
  37. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_crc_explain.py +0 -0
  38. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_crc_functions.py +0 -0
  39. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_hamming.py +0 -0
  40. {bitlab-0.1.0 → bitlab-0.2.0}/tests/test_parity.py +0 -0
@@ -8,9 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
8
8
  ## [Unreleased]
9
9
 
10
10
  ### Planned
11
- - Register field mapper (`bitlab.registers`) — define a hardware register's
12
- bit layout in Python, pack/unpack values, export to C `#define`s or a
13
- bitfield struct.
14
11
  - Computer architecture toolkit (`bitlab.arch`) — IEEE 754 float
15
12
  deconstruction, endianness swapping, Gray code, Q-format fixed-point
16
13
  conversion.
@@ -19,10 +16,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
19
16
  - Reed-Solomon burst error correction — planned as a later, dedicated
20
17
  release given its complexity.
21
18
 
22
- ## [0.1.0] - 2026-07-14
19
+ ## [0.2.0] - 15-07-2026
23
20
 
24
21
  ### Added
25
- - **Named the project `bitlab`** and
22
+ - **New `bitlab.registers` submodule**: hardware register bit-field
23
+ mapping, the signature embedded-dev feature.
24
+ - `Register(name, size_bits=8)` — define a register's layout with
25
+ `add_field(name, bit_index=...)` or `add_field(name, bit_range=(start, end))`.
26
+ Validates overlapping fields and out-of-bounds fields at definition time.
27
+ - `pack(**values)` / `unpack(raw)` — convert between named field values
28
+ and a raw integer.
29
+ - `get_field(raw, name)` / `set_field(raw, name, value)` — read/modify a
30
+ single field via read-modify-write, preserving other bits.
31
+ - `reserved_ranges()` — reports unassigned bit ranges.
32
+ - `explain(reg, **values)`: datasheet-style bit diagram, field list, and
33
+ (when field values are given) a step-by-step pack trace.
34
+ - `export_c(reg, style="defines"|"struct")`:
35
+ - `"defines"` (default) — portable `#define` position/mask macros plus
36
+ `SET_`/`GET_` helper macros. Recommended: plain bitwise ops behave
37
+ identically on every compiler/target.
38
+ - `"struct"` — a C bit-field struct, with an explicit comment noting
39
+ that C leaves bit-field allocation order implementation-defined
40
+ (C99 6.7.2.1p10), so exact hardware bit placement should be verified
41
+ against your compiler's documentation.
42
+ - Both C export styles are validated by actually compiling the generated
43
+ code with `gcc` and comparing the result to Python's `pack()`.
44
+ - `bitlab registers explain` and `bitlab registers export-c` CLI
45
+ subcommands, using a `--field NAME:BIT` / `NAME:START-END` syntax.
46
+ - 21 new tests (77 total), including compiled-C correctness checks for both
47
+ export styles.
48
+
49
+ ## [0.1.0] - 14-07-2026
50
+
51
+ ### Added
52
+ - **Renamed the project from `parity-toolkit` to `bitlab`**, and
26
53
  restructured it as submodules under one umbrella package rather than a
27
54
  flat namespace, to give the project room to grow beyond parity:
28
55
  ```
@@ -58,7 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
58
85
  vs. table-driven CRC implementations and end-to-end compilation of
59
86
  generated C source.
60
87
 
61
- ---
62
88
 
63
- [Unreleased]: https://github.com/KenKambi/bitlab/compare/v0.1.0...HEAD
89
+ [Unreleased]: https://github.com/KenKambi/bitlab/compare/v0.2.0...HEAD
90
+ [0.2.0]: https://github.com/KenKambi/bitlab/compare/v0.1.0...v0.2.0
64
91
  [0.1.0]: https://github.com/KenKambi/bitlab/releases/tag/v0.1.0
bitlab-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,325 @@
1
+ Metadata-Version: 2.4
2
+ Name: bitlab
3
+ Version: 0.2.0
4
+ Summary: A toolkit for embedded systems, binary data, and digital communications: parity, Hamming codes, and CRC — with C code export.
5
+ Project-URL: Homepage, https://github.com/KenKambi/bitlab
6
+ Project-URL: Issues, https://github.com/KenKambi/bitlab/issues
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: bit-manipulation,bitfield,c-code-generation,crc,crc16,crc32,crc8,embedded,error-correction,error-detection,hamming-code,microcontroller,parity,register,serial,uart
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Communications
15
+ Classifier: Topic :: Education
16
+ Classifier: Topic :: Software Development :: Embedded Systems
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.8
19
+ Provides-Extra: dev
20
+ Requires-Dist: build; extra == 'dev'
21
+ Requires-Dist: pytest>=7.0; extra == 'dev'
22
+ Requires-Dist: twine; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # bitlab
26
+
27
+ [![PyPI](https://img.shields.io/pypi/v/bitlab.svg)](https://pypi.org/project/bitlab/)
28
+ [![CI](https://github.com/KenKambi/bitlab/actions/workflows/ci.yml/badge.svg)](https://github.com/KenKambi/bitlab/actions/workflows/ci.yml)
29
+ [![Downloads](https://img.shields.io/pepy/dt/bitlab)](https://pepy.tech/projects/bitlab)
30
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
31
+
32
+ **Design. Understand. Ship it.**
33
+
34
+ bitlab is a toolkit for embedded systems, binary data, and digital
35
+ communications — parity checking, Hamming error correction, CRC, and
36
+ hardware register bit-fields.
37
+
38
+
39
+ | Layer | What it's for |
40
+ |---|---|
41
+ | **Compute** | Fast, production-ready functions (`crc32()`, `pack()`, ...) |
42
+ | **Explain** | The same computation, narrated step by step — for learning or debugging |
43
+ | **Export** | Generate compiled-and-tested C source for the embedded target |
44
+
45
+
46
+ ```bash
47
+ pip install bitlab
48
+ ```
49
+
50
+ ## See it in 20 seconds
51
+
52
+ Most CRC libraries give you a black box. `explain()` shows you the real
53
+ algorithm running, bit by bit:
54
+
55
+ ```python
56
+ >>> from bitlab.crc import explain, CRC8_SMBUS
57
+ >>> print(explain(b"AB", CRC8_SMBUS))
58
+ CRC algorithm: CRC-8/SMBUS
59
+ width=8 poly=0x07 init=0x00 refin=False refout=False xorout=0x00
60
+
61
+ Input bytes (2 total): 41 42
62
+
63
+ Initial register: 0x00
64
+
65
+ Byte 0: 0x41 -> XORed in, register = 0x41
66
+ bit 0: MSB=0 -> shift left -> 0x82
67
+ bit 1: MSB=1 -> shift left, XOR polynomial -> 0x03
68
+ ...
69
+
70
+ Final CRC: 0x87
71
+ ```
72
+
73
+ And when you're ready for hardware, the same configuration exports straight
74
+ to a firmware-ready C function — no rewriting the algorithm by hand:
75
+
76
+ ```python
77
+ >>> from bitlab.crc import export_c, CRC32_ISO_HDLC
78
+ >>> print(export_c(CRC32_ISO_HDLC, function_name="my_crc32"))
79
+ ```
80
+ ```c
81
+ uint32_t my_crc32(const uint8_t *data, size_t len)
82
+ {
83
+ uint32_t crc = 0xFFFFFFFFU;
84
+ for (size_t i = 0; i < len; i++) {
85
+ uint8_t idx = (uint8_t)(crc ^ data[i]);
86
+ crc = (crc >> 8) ^ my_crc32_table[idx];
87
+ }
88
+ return (crc ^ 0xFFFFFFFFU);
89
+ }
90
+ ```
91
+
92
+ The test suite compiles every
93
+ generated C function with `gcc` and checks the output against the official
94
+ CRC catalogue check values.
95
+
96
+ ## Modules
97
+
98
+ ```
99
+ bitlab/
100
+ ├── bitutils/ foundational bit ops shared by every submodule
101
+ ├── parity/ parity bit checking + Hamming(7,4) error correction
102
+ ├── crc/ CRC-8/16/32, generic engine, explain(), export_c()
103
+ └── registers/ hardware register bit-field mapper, explain(), export_c()
104
+ ```
105
+
106
+ ### `bitlab.crc` — crc8/16/32, explain
107
+
108
+ Real hardware (Ethernet, Modbus, firmware integrity checks) uses CRC, not
109
+ simple parity. All four built-in presets are verified against the official
110
+ CRC-catalogue check values, not just "a CRC that happens to run."
111
+
112
+ ```python
113
+ from bitlab.crc import crc8, crc16, crc32
114
+
115
+ crc8(b"hello") # CRC-8/SMBUS
116
+ crc16(b"hello") # CRC-16/CCITT-FALSE (default)
117
+ crc16(b"hello", variant="modbus") # CRC-16/MODBUS
118
+ crc32(b"hello") # CRC-32/ISO-HDLC (Ethernet, zlib, PNG)
119
+ ```
120
+
121
+ Custom polynomials work the same way:
122
+
123
+ ```python
124
+ from bitlab.crc import crc, CRCConfig
125
+
126
+ my_crc = CRCConfig(
127
+ name="my-custom-crc",
128
+ width=16, poly=0x8005, init=0xFFFF,
129
+ refin=True, refout=True, xorout=0x0000,
130
+ check=0x4B37, # CRC of b"123456789", used to self-test your config
131
+ )
132
+ crc(b"some data", my_crc)
133
+ ```
134
+
135
+ ### `bitlab.registers` — embedded, explaim
136
+ Firmware engineers hand-write register bitmasks constantly. Define the
137
+ layout once, pack/unpack named fields instead of shifting magic numbers by
138
+ hand, and export it straight to C.
139
+
140
+ ```python
141
+ from bitlab.registers import Register, explain, export_c
142
+
143
+ reg = Register("UART_CR1", size_bits=8)
144
+ reg.add_field("ENABLE", bit_index=0)
145
+ reg.add_field("MODE", bit_range=(1, 3))
146
+ reg.add_field("TXIE", bit_index=7)
147
+
148
+ reg.pack(ENABLE=1, MODE=0b101, TXIE=1) # -> 0x8B
149
+ reg.unpack(0x8B) # -> {'ENABLE': 1, 'MODE': 5, 'TXIE': 1}
150
+ ```
151
+
152
+ ```python
153
+ >>> print(explain(reg, ENABLE=1, MODE=5, TXIE=1))
154
+ Register: UART_CR1 (8-bit)
155
+
156
+ Bit: 7 6 5 4 3 2 1 0
157
+ Field: TXIE . . . MODE MODE MODE ENABLE
158
+
159
+ Fields (MSB to LSB):
160
+ TXIE bit 7 (max 1)
161
+ MODE bits 1-3 (max 7)
162
+ ENABLE bit 0 (max 1)
163
+
164
+ Reserved (unassigned):
165
+ bits 4-6
166
+
167
+ Packing UART_CR1(ENABLE=1, MODE=5, TXIE=1):
168
+ ENABLE=1 -> 0b1 shifted left 0 -> 0x01 | running value = 0x01
169
+ MODE=5 -> 0b101 shifted left 1 -> 0x0A | running value = 0x0B
170
+ TXIE=1 -> 0b1 shifted left 7 -> 0x80 | running value = 0x8B
171
+
172
+ Final packed value: 0x8B (0b10001011)
173
+ ```
174
+
175
+ `export_c(reg)` gives you portable `#define` position/mask macros with
176
+ `SET_`/`GET_` helpers (the recommended, compiler-independent style).
177
+ `export_c(reg, style="struct")` gives you a readable C bit-field struct
178
+ instead — with an explicit comment flagging that C leaves struct bit-field
179
+ ordering implementation-defined, so you know to double-check it against
180
+ your compiler before trusting exact hardware placement.
181
+
182
+ ```c
183
+ #define UART_CR1_ENABLE_POS 0U
184
+ #define UART_CR1_ENABLE_MASK (0x1U << UART_CR1_ENABLE_POS)
185
+
186
+ #define UART_CR1_SET_ENABLE(reg, val) \
187
+ ((reg) = ((uint8_t)(reg) & ~UART_CR1_ENABLE_MASK) | \
188
+ (((uint8_t)(val) << UART_CR1_ENABLE_POS) & UART_CR1_ENABLE_MASK))
189
+ #define UART_CR1_GET_ENABLE(reg) \
190
+ (((uint8_t)(reg) & UART_CR1_ENABLE_MASK) >> UART_CR1_ENABLE_POS)
191
+ ```
192
+
193
+ ### `bitlab.parity` — parity bit, Hamming(7,4)
194
+
195
+ ```python
196
+ from bitlab.parity import get_parity_bit, append_parity, check_parity
197
+
198
+ get_parity_bit(0b1011, "even", 4) # -> 1
199
+ append_parity(0b1011, "even", 4) # -> 0b11011
200
+ check_parity(0b11011, "even", 4) # -> True
201
+ ```
202
+
203
+ ```python
204
+ from bitlab.parity import encode_hamming, decode_hamming
205
+
206
+ codeword = encode_hamming(0b1101)
207
+ corrupted = codeword ^ 0b0000100 # simulate a single-bit flip
208
+ decode_hamming(corrupted)
209
+ # HammingDecodeResult(data=13, error_position=6, corrected=True, codeword=...)
210
+ ```
211
+
212
+ ### `bitlab.bitutils` — shared foundation
213
+
214
+ ```python
215
+ from bitlab.bitutils import popcount, reflect, rotate_left, rotate_right
216
+
217
+ popcount(0b1011) # -> 3
218
+ reflect(0b1100, 4) # -> 0b0011
219
+ rotate_left(0b10000001, 1, 8) # -> 0b00000011
220
+ ```
221
+
222
+ ## CLI
223
+
224
+ One `bitlab` command covers every module:
225
+
226
+ ```bash
227
+ bitlab crc explain "AB" --preset crc8
228
+ bitlab crc export-c --preset crc32 --name my_crc32
229
+
230
+ bitlab registers explain --name UART_CR1 --size 8 \
231
+ --field ENABLE:0 --field MODE:1-3 --field TXIE:7 --pack ENABLE=1,MODE=5,TXIE=1
232
+ bitlab registers export-c --name UART_CR1 --size 8 \
233
+ --field ENABLE:0 --field MODE:1-3 --field TXIE:7 --style defines
234
+
235
+ bitlab parity bit 0b1011 --type even --width 4
236
+ bitlab hamming encode 13
237
+ ```
238
+
239
+ Run `bitlab --help`, `bitlab crc --help`, `bitlab registers --help` for full usage.
240
+
241
+ ## API reference
242
+
243
+ ### `bitlab.crc`
244
+
245
+ | Function | Description |
246
+ |---|---|
247
+ | `crc8(data)` | CRC-8/SMBUS. |
248
+ | `crc16(data, variant="ccitt")` | CRC-16/CCITT-FALSE or CRC-16/MODBUS. |
249
+ | `crc32(data)` | CRC-32/ISO-HDLC (Ethernet/zlib/PNG). |
250
+ | `crc(data, config)` | Generic entry point. `config` is a `CRCConfig` or a preset name string. |
251
+ | `explain(data, config, max_bytes=4)` | Step-by-step trace of the computation. |
252
+ | `export_c(config, function_name=None)` | Table-driven C99 source implementing `config`. |
253
+ | `build_table(config)` | The 256-entry lookup table (also used by `export_c`). |
254
+ | `compute_bitwise(data, config)` | Canonical bit-by-bit reference implementation. |
255
+ | `compute_table(data, config, table=None)` | Fast table-driven implementation. |
256
+ | `self_test(config)` | Verifies a config against its `check` value. |
257
+ | `CRCConfig` | Dataclass: `name, width, poly, init, refin, refout, xorout, check`. |
258
+ | `CRC8_SMBUS`, `CRC16_CCITT_FALSE`, `CRC16_MODBUS`, `CRC32_ISO_HDLC` | Built-in preset configs. |
259
+
260
+ ### `bitlab.registers`
261
+
262
+ | Function | Description |
263
+ |---|---|
264
+ | `Register(name, size_bits=8)` | Define a register. |
265
+ | `.add_field(name, bit_index=... \| bit_range=(start, end), description=None)` | Add a named field. Raises on overlap or out-of-bounds. |
266
+ | `.pack(**values)` | Pack named field values into a raw integer. |
267
+ | `.unpack(raw)` | Unpack a raw integer into `{field_name: value}`. |
268
+ | `.get_field(raw, name)` / `.set_field(raw, name, value)` | Read/modify a single field (read-modify-write). |
269
+ | `.reserved_ranges()` | Unassigned bit ranges. |
270
+ | `.layout()` | Fields ordered MSB to LSB. |
271
+ | `explain(reg, **values)` | Bit diagram, field list, and (if values given) a pack trace. |
272
+ | `export_c(reg, style="defines"\|"struct")` | C99 source: portable macros or a bit-field struct. |
273
+
274
+ ### `bitlab.parity`
275
+
276
+ | Function | Description |
277
+ |---|---|
278
+ | `get_parity_bit(value, type="even", bit_width=8)` | Computes the parity bit for `value`. |
279
+ | `append_parity(value, type="even", bit_width=8)` | Returns `value` with a parity bit appended as bit `bit_width`. |
280
+ | `check_parity(value_with_parity, type="even", bit_width=8)` | Returns `True` if the parity bit matches the data. |
281
+ | `generate_parity_array(values, type="even", bit_width=8)` | Parity bit for each value in a sequence. |
282
+ | `check_parity_array(values_with_parity, type="even", bit_width=8)` | Parity check for each value in a sequence. |
283
+ | `compute_message_parity(message, type="even")` | Single overall parity bit for a whole string. |
284
+ | `flip_random_bit(value, bit_width=8)` | Flips one random bit — useful for simulating errors in tests. |
285
+ | `encode_hamming(data)` | Encodes a 4-bit value (0–15) into a 7-bit Hamming codeword. |
286
+ | `decode_hamming(codeword)` | Decodes a 7-bit codeword, correcting a single-bit error if present. |
287
+
288
+ ### `bitlab.bitutils`
289
+
290
+ | Function | Description |
291
+ |---|---|
292
+ | `popcount(n)` | Number of 1-bits. |
293
+ | `has_odd_parity(n)` | True if `n` has an odd number of 1-bits. |
294
+ | `get_bit` / `set_bit` / `flip_bit` | Single-bit read/write/toggle. |
295
+ | `reflect(value, width)` | Bit-mirror the lowest `width` bits. |
296
+ | `rotate_left` / `rotate_right` | Bitwise rotation within a fixed-width register. |
297
+
298
+ ## Roadmap
299
+
300
+ - **v0.3.0 — Computer architecture toolkit** (`bitlab.arch`): IEEE 754
301
+ float deconstruction, endianness swapping, Gray code, Q-format
302
+ fixed-point conversion.
303
+ - **v0.4.0 — Protocol framing** (`bitlab.comms`): COBS (Consistent Overhead
304
+ Byte Stuffing).
305
+ - **v1.0.0 — Reed-Solomon** (`bitlab.ecc`): burst error correction (QR
306
+ codes, satellite comms) — planned as a dedicated release given its
307
+ complexity.
308
+
309
+ See [CHANGELOG.md](CHANGELOG.md) for full release history.
310
+
311
+ ## Development
312
+
313
+ ```bash
314
+ pip install -e ".[dev]"
315
+ pytest # 77 tests, including compiling and running generated
316
+ # C against gcc for both crc and registers
317
+ python -m build # produce a wheel + sdist in dist/
318
+ ```
319
+
320
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full dev workflow and release
321
+ process.
322
+
323
+ ## License
324
+
325
+ MIT
bitlab-0.2.0/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # bitlab
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/bitlab.svg)](https://pypi.org/project/bitlab/)
4
+ [![CI](https://github.com/KenKambi/bitlab/actions/workflows/ci.yml/badge.svg)](https://github.com/KenKambi/bitlab/actions/workflows/ci.yml)
5
+ [![Downloads](https://img.shields.io/pepy/dt/bitlab)](https://pepy.tech/projects/bitlab)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ **Design. Understand. Ship it.**
9
+
10
+ bitlab is a toolkit for embedded systems, binary data, and digital
11
+ communications — parity checking, Hamming error correction, CRC, and
12
+ hardware register bit-fields.
13
+
14
+
15
+ | Layer | What it's for |
16
+ |---|---|
17
+ | **Compute** | Fast, production-ready functions (`crc32()`, `pack()`, ...) |
18
+ | **Explain** | The same computation, narrated step by step — for learning or debugging |
19
+ | **Export** | Generate compiled-and-tested C source for the embedded target |
20
+
21
+
22
+ ```bash
23
+ pip install bitlab
24
+ ```
25
+
26
+ ## See it in 20 seconds
27
+
28
+ Most CRC libraries give you a black box. `explain()` shows you the real
29
+ algorithm running, bit by bit:
30
+
31
+ ```python
32
+ >>> from bitlab.crc import explain, CRC8_SMBUS
33
+ >>> print(explain(b"AB", CRC8_SMBUS))
34
+ CRC algorithm: CRC-8/SMBUS
35
+ width=8 poly=0x07 init=0x00 refin=False refout=False xorout=0x00
36
+
37
+ Input bytes (2 total): 41 42
38
+
39
+ Initial register: 0x00
40
+
41
+ Byte 0: 0x41 -> XORed in, register = 0x41
42
+ bit 0: MSB=0 -> shift left -> 0x82
43
+ bit 1: MSB=1 -> shift left, XOR polynomial -> 0x03
44
+ ...
45
+
46
+ Final CRC: 0x87
47
+ ```
48
+
49
+ And when you're ready for hardware, the same configuration exports straight
50
+ to a firmware-ready C function — no rewriting the algorithm by hand:
51
+
52
+ ```python
53
+ >>> from bitlab.crc import export_c, CRC32_ISO_HDLC
54
+ >>> print(export_c(CRC32_ISO_HDLC, function_name="my_crc32"))
55
+ ```
56
+ ```c
57
+ uint32_t my_crc32(const uint8_t *data, size_t len)
58
+ {
59
+ uint32_t crc = 0xFFFFFFFFU;
60
+ for (size_t i = 0; i < len; i++) {
61
+ uint8_t idx = (uint8_t)(crc ^ data[i]);
62
+ crc = (crc >> 8) ^ my_crc32_table[idx];
63
+ }
64
+ return (crc ^ 0xFFFFFFFFU);
65
+ }
66
+ ```
67
+
68
+ The test suite compiles every
69
+ generated C function with `gcc` and checks the output against the official
70
+ CRC catalogue check values.
71
+
72
+ ## Modules
73
+
74
+ ```
75
+ bitlab/
76
+ ├── bitutils/ foundational bit ops shared by every submodule
77
+ ├── parity/ parity bit checking + Hamming(7,4) error correction
78
+ ├── crc/ CRC-8/16/32, generic engine, explain(), export_c()
79
+ └── registers/ hardware register bit-field mapper, explain(), export_c()
80
+ ```
81
+
82
+ ### `bitlab.crc` — crc8/16/32, explain
83
+
84
+ Real hardware (Ethernet, Modbus, firmware integrity checks) uses CRC, not
85
+ simple parity. All four built-in presets are verified against the official
86
+ CRC-catalogue check values, not just "a CRC that happens to run."
87
+
88
+ ```python
89
+ from bitlab.crc import crc8, crc16, crc32
90
+
91
+ crc8(b"hello") # CRC-8/SMBUS
92
+ crc16(b"hello") # CRC-16/CCITT-FALSE (default)
93
+ crc16(b"hello", variant="modbus") # CRC-16/MODBUS
94
+ crc32(b"hello") # CRC-32/ISO-HDLC (Ethernet, zlib, PNG)
95
+ ```
96
+
97
+ Custom polynomials work the same way:
98
+
99
+ ```python
100
+ from bitlab.crc import crc, CRCConfig
101
+
102
+ my_crc = CRCConfig(
103
+ name="my-custom-crc",
104
+ width=16, poly=0x8005, init=0xFFFF,
105
+ refin=True, refout=True, xorout=0x0000,
106
+ check=0x4B37, # CRC of b"123456789", used to self-test your config
107
+ )
108
+ crc(b"some data", my_crc)
109
+ ```
110
+
111
+ ### `bitlab.registers` — embedded, explaim
112
+ Firmware engineers hand-write register bitmasks constantly. Define the
113
+ layout once, pack/unpack named fields instead of shifting magic numbers by
114
+ hand, and export it straight to C.
115
+
116
+ ```python
117
+ from bitlab.registers import Register, explain, export_c
118
+
119
+ reg = Register("UART_CR1", size_bits=8)
120
+ reg.add_field("ENABLE", bit_index=0)
121
+ reg.add_field("MODE", bit_range=(1, 3))
122
+ reg.add_field("TXIE", bit_index=7)
123
+
124
+ reg.pack(ENABLE=1, MODE=0b101, TXIE=1) # -> 0x8B
125
+ reg.unpack(0x8B) # -> {'ENABLE': 1, 'MODE': 5, 'TXIE': 1}
126
+ ```
127
+
128
+ ```python
129
+ >>> print(explain(reg, ENABLE=1, MODE=5, TXIE=1))
130
+ Register: UART_CR1 (8-bit)
131
+
132
+ Bit: 7 6 5 4 3 2 1 0
133
+ Field: TXIE . . . MODE MODE MODE ENABLE
134
+
135
+ Fields (MSB to LSB):
136
+ TXIE bit 7 (max 1)
137
+ MODE bits 1-3 (max 7)
138
+ ENABLE bit 0 (max 1)
139
+
140
+ Reserved (unassigned):
141
+ bits 4-6
142
+
143
+ Packing UART_CR1(ENABLE=1, MODE=5, TXIE=1):
144
+ ENABLE=1 -> 0b1 shifted left 0 -> 0x01 | running value = 0x01
145
+ MODE=5 -> 0b101 shifted left 1 -> 0x0A | running value = 0x0B
146
+ TXIE=1 -> 0b1 shifted left 7 -> 0x80 | running value = 0x8B
147
+
148
+ Final packed value: 0x8B (0b10001011)
149
+ ```
150
+
151
+ `export_c(reg)` gives you portable `#define` position/mask macros with
152
+ `SET_`/`GET_` helpers (the recommended, compiler-independent style).
153
+ `export_c(reg, style="struct")` gives you a readable C bit-field struct
154
+ instead — with an explicit comment flagging that C leaves struct bit-field
155
+ ordering implementation-defined, so you know to double-check it against
156
+ your compiler before trusting exact hardware placement.
157
+
158
+ ```c
159
+ #define UART_CR1_ENABLE_POS 0U
160
+ #define UART_CR1_ENABLE_MASK (0x1U << UART_CR1_ENABLE_POS)
161
+
162
+ #define UART_CR1_SET_ENABLE(reg, val) \
163
+ ((reg) = ((uint8_t)(reg) & ~UART_CR1_ENABLE_MASK) | \
164
+ (((uint8_t)(val) << UART_CR1_ENABLE_POS) & UART_CR1_ENABLE_MASK))
165
+ #define UART_CR1_GET_ENABLE(reg) \
166
+ (((uint8_t)(reg) & UART_CR1_ENABLE_MASK) >> UART_CR1_ENABLE_POS)
167
+ ```
168
+
169
+ ### `bitlab.parity` — parity bit, Hamming(7,4)
170
+
171
+ ```python
172
+ from bitlab.parity import get_parity_bit, append_parity, check_parity
173
+
174
+ get_parity_bit(0b1011, "even", 4) # -> 1
175
+ append_parity(0b1011, "even", 4) # -> 0b11011
176
+ check_parity(0b11011, "even", 4) # -> True
177
+ ```
178
+
179
+ ```python
180
+ from bitlab.parity import encode_hamming, decode_hamming
181
+
182
+ codeword = encode_hamming(0b1101)
183
+ corrupted = codeword ^ 0b0000100 # simulate a single-bit flip
184
+ decode_hamming(corrupted)
185
+ # HammingDecodeResult(data=13, error_position=6, corrected=True, codeword=...)
186
+ ```
187
+
188
+ ### `bitlab.bitutils` — shared foundation
189
+
190
+ ```python
191
+ from bitlab.bitutils import popcount, reflect, rotate_left, rotate_right
192
+
193
+ popcount(0b1011) # -> 3
194
+ reflect(0b1100, 4) # -> 0b0011
195
+ rotate_left(0b10000001, 1, 8) # -> 0b00000011
196
+ ```
197
+
198
+ ## CLI
199
+
200
+ One `bitlab` command covers every module:
201
+
202
+ ```bash
203
+ bitlab crc explain "AB" --preset crc8
204
+ bitlab crc export-c --preset crc32 --name my_crc32
205
+
206
+ bitlab registers explain --name UART_CR1 --size 8 \
207
+ --field ENABLE:0 --field MODE:1-3 --field TXIE:7 --pack ENABLE=1,MODE=5,TXIE=1
208
+ bitlab registers export-c --name UART_CR1 --size 8 \
209
+ --field ENABLE:0 --field MODE:1-3 --field TXIE:7 --style defines
210
+
211
+ bitlab parity bit 0b1011 --type even --width 4
212
+ bitlab hamming encode 13
213
+ ```
214
+
215
+ Run `bitlab --help`, `bitlab crc --help`, `bitlab registers --help` for full usage.
216
+
217
+ ## API reference
218
+
219
+ ### `bitlab.crc`
220
+
221
+ | Function | Description |
222
+ |---|---|
223
+ | `crc8(data)` | CRC-8/SMBUS. |
224
+ | `crc16(data, variant="ccitt")` | CRC-16/CCITT-FALSE or CRC-16/MODBUS. |
225
+ | `crc32(data)` | CRC-32/ISO-HDLC (Ethernet/zlib/PNG). |
226
+ | `crc(data, config)` | Generic entry point. `config` is a `CRCConfig` or a preset name string. |
227
+ | `explain(data, config, max_bytes=4)` | Step-by-step trace of the computation. |
228
+ | `export_c(config, function_name=None)` | Table-driven C99 source implementing `config`. |
229
+ | `build_table(config)` | The 256-entry lookup table (also used by `export_c`). |
230
+ | `compute_bitwise(data, config)` | Canonical bit-by-bit reference implementation. |
231
+ | `compute_table(data, config, table=None)` | Fast table-driven implementation. |
232
+ | `self_test(config)` | Verifies a config against its `check` value. |
233
+ | `CRCConfig` | Dataclass: `name, width, poly, init, refin, refout, xorout, check`. |
234
+ | `CRC8_SMBUS`, `CRC16_CCITT_FALSE`, `CRC16_MODBUS`, `CRC32_ISO_HDLC` | Built-in preset configs. |
235
+
236
+ ### `bitlab.registers`
237
+
238
+ | Function | Description |
239
+ |---|---|
240
+ | `Register(name, size_bits=8)` | Define a register. |
241
+ | `.add_field(name, bit_index=... \| bit_range=(start, end), description=None)` | Add a named field. Raises on overlap or out-of-bounds. |
242
+ | `.pack(**values)` | Pack named field values into a raw integer. |
243
+ | `.unpack(raw)` | Unpack a raw integer into `{field_name: value}`. |
244
+ | `.get_field(raw, name)` / `.set_field(raw, name, value)` | Read/modify a single field (read-modify-write). |
245
+ | `.reserved_ranges()` | Unassigned bit ranges. |
246
+ | `.layout()` | Fields ordered MSB to LSB. |
247
+ | `explain(reg, **values)` | Bit diagram, field list, and (if values given) a pack trace. |
248
+ | `export_c(reg, style="defines"\|"struct")` | C99 source: portable macros or a bit-field struct. |
249
+
250
+ ### `bitlab.parity`
251
+
252
+ | Function | Description |
253
+ |---|---|
254
+ | `get_parity_bit(value, type="even", bit_width=8)` | Computes the parity bit for `value`. |
255
+ | `append_parity(value, type="even", bit_width=8)` | Returns `value` with a parity bit appended as bit `bit_width`. |
256
+ | `check_parity(value_with_parity, type="even", bit_width=8)` | Returns `True` if the parity bit matches the data. |
257
+ | `generate_parity_array(values, type="even", bit_width=8)` | Parity bit for each value in a sequence. |
258
+ | `check_parity_array(values_with_parity, type="even", bit_width=8)` | Parity check for each value in a sequence. |
259
+ | `compute_message_parity(message, type="even")` | Single overall parity bit for a whole string. |
260
+ | `flip_random_bit(value, bit_width=8)` | Flips one random bit — useful for simulating errors in tests. |
261
+ | `encode_hamming(data)` | Encodes a 4-bit value (0–15) into a 7-bit Hamming codeword. |
262
+ | `decode_hamming(codeword)` | Decodes a 7-bit codeword, correcting a single-bit error if present. |
263
+
264
+ ### `bitlab.bitutils`
265
+
266
+ | Function | Description |
267
+ |---|---|
268
+ | `popcount(n)` | Number of 1-bits. |
269
+ | `has_odd_parity(n)` | True if `n` has an odd number of 1-bits. |
270
+ | `get_bit` / `set_bit` / `flip_bit` | Single-bit read/write/toggle. |
271
+ | `reflect(value, width)` | Bit-mirror the lowest `width` bits. |
272
+ | `rotate_left` / `rotate_right` | Bitwise rotation within a fixed-width register. |
273
+
274
+ ## Roadmap
275
+
276
+ - **v0.3.0 — Computer architecture toolkit** (`bitlab.arch`): IEEE 754
277
+ float deconstruction, endianness swapping, Gray code, Q-format
278
+ fixed-point conversion.
279
+ - **v0.4.0 — Protocol framing** (`bitlab.comms`): COBS (Consistent Overhead
280
+ Byte Stuffing).
281
+ - **v1.0.0 — Reed-Solomon** (`bitlab.ecc`): burst error correction (QR
282
+ codes, satellite comms) — planned as a dedicated release given its
283
+ complexity.
284
+
285
+ See [CHANGELOG.md](CHANGELOG.md) for full release history.
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ pip install -e ".[dev]"
291
+ pytest # 77 tests, including compiling and running generated
292
+ # C against gcc for both crc and registers
293
+ python -m build # produce a wheel + sdist in dist/
294
+ ```
295
+
296
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full dev workflow and release
297
+ process.
298
+
299
+ ## License
300
+
301
+ MIT