Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

Working with the tool: act_seq_dag.py

act_seq_dag.py walks an ACT design from an entry .act file, follows every import (path and namespace) into the stdlib by default, parses each chp {} / hse {} body, and emits one Graphviz .dot DAG per body where:

  • nodes = statements / parallel-comma clusters / control structures (loops & selections), inlined so the ; spine of one loop iteration is visible;
  • edges = the ; operators, each labeled with why it's necessary:
    • RAW (black), WAR (darkred), WAW (purple), CHANNEL (blue) — necessary, keep;
    • SPURIOUS (orange dotted) — no hazard, the ; could be a ,. These are your optimization targets.

Quick start examples

# one file
python3 act_seq_dag.py actnow/tests/wfi_test.act -o wfi.dot --rank

# just one body by name (substring match, case-insensitive)
python3 act_seq_dag.py actnow/soc.act --only soc --rank

# don't descend into std::/sim:: (faster, focus on your own code)
python3 act_seq_dag.py actnow/tests/wfi_test.act --no-stdlib --rank

# machine-readable summary for a dashboard/CI
python3 act_seq_dag.py actnow/tests/wfi_test.act --json wfi.json

# CI gate: fail if any spurious ; remains
python3 act_seq_dag.py actnow/tests/wfi_test.act --fail-on-spurious

# also expand , branches as separate nodes (denser graph)
python3 act_seq_dag.py actnow/soc.act --show-parallel -o soc.dot

Render with graphviz:

dot -Tsvg wfi.dot -o wfi.svg      # or -Tpng
xdot wfi.dot                       # interactive

What the --rank table means

=== wfi_test.act: 17 files, 149 bodies, 205 semicolons (72 necessary, 133 spurious) ===
  soc        chp   55 semi ( 35 nec, 20 spur)  soc.act:84  <-- spurious ;'s
  mmu        chp   16 semi (  4 nec, 12 spur)  mmu.act:49  <-- spurious ;'s
  ...

Sort by the spur column descending — that's where the most layers can be removed. necessary is a lower bound on how many ;s you're stuck with (may be slightly inflated by struct-root matching; see §3).

How the tool knows (and its limits)

It mirrors the ACT library's own model: the grammar in act/lang.m4 (the chp_body/chp_comma_list rules make ;ACT_CHP_SEMI, ,ACT_CHP_COMMA) and the conflict checker act/lang_chp.cc:: _check_concurrent_conflicts + _compute_rw_sets. It is a surface parser, not the compiler:

  • It does not elaborate templates, resolve types, or expand ptype-passed processes. So a templated body appears once (not once per instantiation). That's fine for spotting the pattern, less fine for counting exact gate layers in a fully-expanded netlist.
  • It matches hazards on the root identifier of dotted names (struct fields collapse to the struct). This is conservative: it never misses a real dependency, but it may mark a ; necessary when two disjoint fields of one struct are touched. The tooltip lists the full field names so you can confirm.
  • It treats every channel action as a write to the channel name (for CHANNEL hazard purposes), matching the compiler's "two parallel branches can't both touch one channel" rule.

When the tool and your intuition disagree, trust the compiler: if actsim accepts s1, s2 (no conflict warning), the ; was spurious.

A worked example, end-to-end

Take actnow/mmu.act's main loop. The --rank table says mmu: 16 semi (4 nec, 12 spur). Open mmu.dot, jump to the mmu cluster. The orange dotted edges are the 12 spurious ;s. Reading them in source order:

addr_core?addr; mode_core?mode;     // <-- (1) SPURIOUS: two different channels
op := mode.op;                       // <-- (2) reads mode.op, writes op
size := mode.size;                   // <-- (3) SPURIOUS vs (2): disjoint field+var
[ is_write := ... ];                 // reads op → necessary after (2)
[ is_read := ... ];                  // reads op → spurious vs the is_write select
...

Apply the checklist (§5) to each orange edge:

edge S1 writes S2 reads S2 writes shared chan? verdict rewrite
(1) addr, addr_core mode, mode_core no (diff ch) SPURIOUS addr_core?addr, mode_core?mode;
(2)→(3) op mode.size size no SPURIOUS op := mode.op, size := mode.size;
is_write→is_read is_write op is_read no SPURIOUS [...is_write...], [...is_read...]; (see §9.2 caution)

The four necessary edges are the channel pairs (addr_mem!addr; rdata_mem?rdata etc.) and the final rdata := mask_data(rdata, size); rdata_core!rdata (RAW on rdata). Those stay ;.

After the rewrite, re-run the tool: the spur count for mmu should drop toward 0, and the latency of the MMU's per-transaction handshake shrinks by the removed layers.

Checklist before you commit

  • Ran python3 act_seq_dag.py <entry>.act --rank and read the table.
  • For each body with spur > 0, opened the .dot, found the orange edges.
  • For each orange edge, ran the 4-question checklist (§5) and confirmed no RAW/WAR/WAW/CHANNEL.
  • Rewrote the spurious ; to ,, parenthesizing for precedence (, binds tighter than ;).
  • Left necessary ;s (channel pairs, real data deps, loop-guard feeds) alone — verified by the tool's RAW/WAR/WAW/CHANNEL labels.
  • Re-ran the simulator (actsim) on the affected testbenches — semantics unchanged, only latency/area improved.
  • Re-ran the DAG tool: spur count went down, necessary count unchanged (no new hazards introduced).

Golden rule. If you can point at a ; and say specifically which variable read or which channel forces the ordering, keep it. If you can't, it's a candidate — and the DAG tool will already have colored it orange.

About

Tool to visualize sequential dependencies and mark potentially unnecessary ones, in an ACT project, from the ACT VLSI Design Tools.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages