hdl-module-design — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited hdl-module-design (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A hardware module is an interface plus an implementation. Get the interface and its configuration right and the implementation stays swappable, testable, and reusable across an FPGA and an ASIC.
Core principle: A module should declare exactly what it needs as typed configuration, validate it at build time, and be exhaustively testable in isolation. If you can't construct and test it without the rest of the SoC, the boundary is wrong.
int/String/bool constructor argsSkip for throwaway testbench glue or a one-line wire rename.
Hardware bugs are expensive, so push errors as early as possible: ideally a type error, otherwise a build-time assertion, never a silent miscompile.
BusKind.axi4 not "axi4". A typo becomes a compile error instead of a wrong build.addrWidth is a function of depth, compute it. Don't make the caller pass both and hope they agree.// ROHD-flavored, but the shape is language-neutral.
class FifoConfig {
final int depth;
final int width;
const FifoConfig({required this.depth, required this.width});
// build-time validation, names the bad field
void validate() {
if (depth <= 0 || (depth & (depth - 1)) != 0) {
throw ArgumentError('FifoConfig.depth must be a power of two, got $depth');
}
if (width <= 0) {
throw ArgumentError('FifoConfig.width must be positive, got $width');
}
}
int get addrWidth => depth.bitLength - 1; // derived, not passed in
}The module and its elaboration logic live in the library. The CLI, build script, or generator is a thin wrapper that parses args and calls library methods. This keeps the module usable by other code (other generators, tests, third parties) and keeps the surface testable without spawning a process.
If you find yourself reaching into a module's internals from the CLI, lift that into a library method.
This is going to silicon. A miss is a respin.
lib/components/fifo.dart -> test/components/fifo_test.dart, lib/config/... -> test/config/.... Anyone can find the test for a unit.ignore/skip/ERROR_ON_X=false knob to make a violating design pass. Fix the design. See [[failures-should-fail]] in the silicon-grade-discipline skill.| Smell | Do instead |
|---|---|
8 positional int args | One typed config object |
mode == "fast" | mode == Mode.fast (enum) |
| Width mismatch caught in sim | Assert it at config construction |
| Logic in the CLI command | Library method, CLI calls it |
| Only top-level tests | One test file per component, mirrored layout |
skipDrc = true to get a pass | Fix the design |
const constructors with final fields.rohd-rtl-gotchas. See silicon-grade-discipline for the failures-should-fail and no-over-engineering rules this leans on.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.