optimizing a gate level bcm to the end of the earth and back
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Graphviz DOT export for gate diagrams

Adds --format dot option to export circuits as Graphviz DOT files.
Render with: dot -Tpng circuit.dot -o circuit.png

The diagram shows:
- Input nodes (A, B, C, D)
- Inverters for negated signals
- Shared AND gates (product terms) with labels
- OR gates for each segment output
- Output nodes (a-g)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+173 -11
+2 -1
bcd_optimization/__init__.py
··· 3 3 from .solver import BCDTo7SegmentSolver, SynthesisResult 4 4 from .truth_tables import SEGMENT_TRUTH_TABLES, SEGMENT_NAMES, SEGMENT_MINTERMS 5 5 from .quine_mccluskey import Implicant, quine_mccluskey, quine_mccluskey_multi_output 6 - from .export import to_verilog, to_c_code, to_equations 6 + from .export import to_verilog, to_c_code, to_equations, to_dot 7 7 from .verify import verify_result 8 8 9 9 __all__ = [ ··· 18 18 "to_verilog", 19 19 "to_c_code", 20 20 "to_equations", 21 + "to_dot", 21 22 "verify_result", 22 23 ] 23 24 __version__ = "0.1.0"
+5 -2
bcd_optimization/cli.py
··· 5 5 6 6 from .solver import BCDTo7SegmentSolver 7 7 from .truth_tables import print_truth_table 8 - from .export import to_verilog, to_c_code, to_equations 8 + from .export import to_verilog, to_c_code, to_equations, to_dot 9 9 10 10 11 11 def main(): ··· 20 20 bcd-optimize --truth-table Show the BCD truth table 21 21 bcd-optimize --format verilog Output as Verilog module 22 22 bcd-optimize --format c Output as C function 23 + bcd-optimize --format dot Output as Graphviz DOT (render with: dot -Tpng) 23 24 """, 24 25 ) 25 26 ··· 41 42 ) 42 43 parser.add_argument( 43 44 "--format", "-f", 44 - choices=["text", "verilog", "c", "equations"], 45 + choices=["text", "verilog", "c", "equations", "dot"], 45 46 default="text", 46 47 help="Output format (default: text)", 47 48 ) ··· 87 88 print(to_c_code(result)) 88 89 elif args.format == "equations": 89 90 print(to_equations(result)) 91 + elif args.format == "dot": 92 + print(to_dot(result)) 90 93 else: 91 94 print() 92 95 solver.print_result(result)
+166 -8
bcd_optimization/export.py
··· 219 219 return "(" + " & ".join(terms) + ")" 220 220 221 221 222 + def to_dot(result: SynthesisResult, title: str = "BCD to 7-Segment Decoder") -> str: 223 + """ 224 + Export synthesis result as Graphviz DOT format. 225 + 226 + Render with: dot -Tpng circuit.dot -o circuit.png 227 + Or: dot -Tsvg circuit.dot -o circuit.svg 228 + 229 + Args: 230 + result: The synthesis result 231 + title: Title for the diagram 232 + 233 + Returns: 234 + DOT source code as string 235 + """ 236 + lines = [] 237 + lines.append("digraph BCD_7Seg {") 238 + lines.append(f' label="{title}\\n{result.cost} gate inputs, {len(result.shared_implicants)} shared terms";') 239 + lines.append(' labelloc="t";') 240 + lines.append(' fontsize=16;') 241 + lines.append(' rankdir=LR;') 242 + lines.append(' splines=ortho;') 243 + lines.append(' nodesep=0.4;') 244 + lines.append(' ranksep=0.8;') 245 + lines.append("") 246 + 247 + # Input nodes 248 + lines.append(" // Inputs") 249 + lines.append(' subgraph cluster_inputs {') 250 + lines.append(' label="Inputs";') 251 + lines.append(' style=dashed;') 252 + lines.append(' color=gray;') 253 + for var in ['A', 'B', 'C', 'D']: 254 + lines.append(f' {var} [shape=circle, style=filled, fillcolor=lightblue, label="{var}"];') 255 + lines.append(' }') 256 + lines.append("") 257 + 258 + # Inverters for negated inputs 259 + lines.append(" // Inverters") 260 + used_negations = set() 261 + for impl, _ in result.shared_implicants: 262 + for i, var in enumerate(['A', 'B', 'C', 'D']): 263 + bit = 1 << (3 - i) 264 + if impl.mask & bit and not ((impl.value >> (3 - i)) & 1): 265 + used_negations.add(var) 266 + 267 + # Check non-shared implicants too 268 + for segment in SEGMENT_NAMES: 269 + if segment not in result.implicants_by_output: 270 + continue 271 + for impl in result.implicants_by_output[segment]: 272 + is_shared = any(impl == si for si, _ in result.shared_implicants) 273 + if is_shared: 274 + continue 275 + for i, var in enumerate(['A', 'B', 'C', 'D']): 276 + bit = 1 << (3 - i) 277 + if impl.mask & bit and not ((impl.value >> (3 - i)) & 1): 278 + used_negations.add(var) 279 + 280 + for var in sorted(used_negations): 281 + lines.append(f' not_{var} [shape=invtriangle, style=filled, fillcolor=lightyellow, label="NOT", width=0.4, height=0.4];') 282 + lines.append(f' {var} -> not_{var};') 283 + lines.append("") 284 + 285 + # AND gates for shared product terms 286 + lines.append(" // Shared AND gates (product terms)") 287 + lines.append(' subgraph cluster_and {') 288 + lines.append(' label="Product Terms";') 289 + lines.append(' style=dashed;') 290 + lines.append(' color=gray;') 291 + 292 + for i, (impl, outputs) in enumerate(result.shared_implicants): 293 + term_label = impl.to_expr_str() 294 + outputs_str = ",".join(outputs) 295 + lines.append(f' and_{i} [shape=polygon, sides=4, style=filled, fillcolor=lightgreen, label="AND\\n{term_label}"];') 296 + lines.append(' }') 297 + lines.append("") 298 + 299 + # Connect inputs to AND gates 300 + lines.append(" // Input to AND connections") 301 + for i, (impl, _) in enumerate(result.shared_implicants): 302 + for j, var in enumerate(['A', 'B', 'C', 'D']): 303 + bit = 1 << (3 - j) 304 + if impl.mask & bit: 305 + if (impl.value >> (3 - j)) & 1: 306 + lines.append(f' {var} -> and_{i};') 307 + else: 308 + lines.append(f' not_{var} -> and_{i};') 309 + lines.append("") 310 + 311 + # OR gates for outputs 312 + lines.append(" // Output OR gates") 313 + lines.append(' subgraph cluster_or {') 314 + lines.append(' label="Output OR Gates";') 315 + lines.append(' style=dashed;') 316 + lines.append(' color=gray;') 317 + for segment in SEGMENT_NAMES: 318 + lines.append(f' or_{segment} [shape=ellipse, style=filled, fillcolor=lightsalmon, label="OR\\n{segment}"];') 319 + lines.append(' }') 320 + lines.append("") 321 + 322 + # Connect AND gates to OR gates 323 + lines.append(" // AND to OR connections") 324 + for i, (impl, outputs) in enumerate(result.shared_implicants): 325 + for segment in outputs: 326 + lines.append(f' and_{i} -> or_{segment};') 327 + lines.append("") 328 + 329 + # Handle non-shared terms (direct connections or inline ANDs) 330 + lines.append(" // Non-shared terms") 331 + nonshared_idx = 0 332 + for segment in SEGMENT_NAMES: 333 + if segment not in result.implicants_by_output: 334 + continue 335 + for impl in result.implicants_by_output[segment]: 336 + is_shared = any(impl == si for si, _ in result.shared_implicants) 337 + if is_shared: 338 + continue 339 + 340 + # Single literal - direct connection 341 + if impl.num_literals == 1: 342 + for j, var in enumerate(['A', 'B', 'C', 'D']): 343 + bit = 1 << (3 - j) 344 + if impl.mask & bit: 345 + if (impl.value >> (3 - j)) & 1: 346 + lines.append(f' {var} -> or_{segment};') 347 + else: 348 + lines.append(f' not_{var} -> or_{segment};') 349 + else: 350 + # Multi-literal non-shared AND 351 + term_label = impl.to_expr_str() 352 + and_name = f"and_ns_{nonshared_idx}" 353 + lines.append(f' {and_name} [shape=polygon, sides=4, style=filled, fillcolor=palegreen, label="AND\\n{term_label}"];') 354 + for j, var in enumerate(['A', 'B', 'C', 'D']): 355 + bit = 1 << (3 - j) 356 + if impl.mask & bit: 357 + if (impl.value >> (3 - j)) & 1: 358 + lines.append(f' {var} -> {and_name};') 359 + else: 360 + lines.append(f' not_{var} -> {and_name};') 361 + lines.append(f' {and_name} -> or_{segment};') 362 + nonshared_idx += 1 363 + lines.append("") 364 + 365 + # Output nodes 366 + lines.append(" // Outputs") 367 + lines.append(' subgraph cluster_outputs {') 368 + lines.append(' label="Outputs";') 369 + lines.append(' style=dashed;') 370 + lines.append(' color=gray;') 371 + for segment in SEGMENT_NAMES: 372 + lines.append(f' out_{segment} [shape=doublecircle, style=filled, fillcolor=lightpink, label="{segment}"];') 373 + lines.append(' }') 374 + lines.append("") 375 + 376 + # Connect OR gates to outputs 377 + lines.append(" // OR to output connections") 378 + for segment in SEGMENT_NAMES: 379 + lines.append(f' or_{segment} -> out_{segment};') 380 + 381 + lines.append("}") 382 + 383 + return "\n".join(lines) 384 + 385 + 222 386 if __name__ == "__main__": 223 387 from .solver import BCDTo7SegmentSolver 224 388 ··· 226 390 result = solver.solve() 227 391 228 392 print("=" * 60) 229 - print("VERILOG OUTPUT") 230 - print("=" * 60) 231 - print(to_verilog(result)) 232 - 233 - print("\n") 393 + print("DOT OUTPUT (save as .dot, render with Graphviz)") 234 394 print("=" * 60) 235 - print("C CODE OUTPUT") 236 - print("=" * 60) 237 - print(to_c_code(result)) 395 + print(to_dot(result))