config.json, Field by Field
The flow you ran in the previous article did not configure itself. Every decision it made, which RTL to read, what clock to target, how densely to pack cells, came from one file, config.json. This article reads it the way you would on day one of a project, field by field, without yet needing to know how each value is used deep inside a stage.
The config for the counter
A representative config.json for the counter:
{
"DESIGN_NAME": "my_first_design",
"VERILOG_FILES": "dir::src/*.v",
"CLOCK_PORT": "clk",
"CLOCK_PERIOD": 10,
"FP_CORE_UTIL": 40,
"PL_TARGET_DENSITY": 0.45
}
Field by field:
DESIGN_NAME: the top-level module OpenLane treats as the design’s root. It has to match the top module name in the RTL, or the flow does not know which module is being built.VERILOG_FILES: which RTL to read.dir::src/*.vtells OpenLane to pick up every.vin the design’ssrc/folder, the folder you built two articles ago.CLOCK_PORT: the port carrying the clock (clkhere). Every timing-aware stage from synthesis on needs to know which port this is.CLOCK_PERIOD: the target clock period in nanoseconds. This one number is the timing constraint every stage from synthesis to signoff is trying to satisfy. A 10 ns period targets 100 MHz.FP_CORE_UTIL: the floorplan core utilisation target as a percentage, the same utilisation from the die-area estimate in Section 1.1, now a real field. You tune it in Module 3.PL_TARGET_DENSITY: a related density target for global placement. You tune it in Module 4.
You do not need the mechanics of the last two yet. Recognise them as named knobs the course will return to. Of all the fields, CLOCK_PERIOD is the one every later stage reads, and getting it right, matching the design’s real required frequency, before running anything is one of the highest-leverage decisions in the file.
Change one field, predict the effect
Edit CLOCK_PERIOD from 10 to 5, halving the period and doubling the target frequency:
$$ f = \frac{1}{T} \implies \frac{1}{5\text{ ns}} = 200\text{ MHz}, \quad \text{versus } \frac{1}{10\text{ ns}} = 100\text{ MHz} $$
Before rerunning, predict what should change. A smaller period is a harder target: every path between flip-flops has less time to settle. For this tiny counter the logic is simple enough that 200 MHz is very likely still achievable with no other change. The prediction to verify, once you rerun ./flow.tcl -design my_first_design, is in the timing report at the end: is the worst slack still positive at the tighter period? That is the kind of report you learn to read in full in Module 7.
This workflow, tighten a constraint, rerun, check whether it still closes, is the daily rhythm of timing closure. Nobody picks a final clock period once; it is iterated against signoff results, as you just did on a toy design with one edit. And if a design starts failing timing after a config change, the clock period field is the first thing to check.
Practice Exercise
A config correctly lists CLOCK_PORT: "clk", but the RTL names its clock input clock. What kind of problem would you expect, and at which stage would it first surface?
Solution
Every timing-aware stage depends on identifying the clock to reason about setup and hold and to build the clock tree. If CLOCK_PORT names a port that does not exist, the tool has no clock to constrain against. This most likely surfaces at synthesis, when the clock definition (an .sdc constraint referencing CLOCK_PORT, covered in Module 2) fails to find a matching port, either with an explicit error or, worse, by silently treating the design as unconstrained, which would let a badly timed design pass synthesis without complaint. A mismatch between a config field and the RTL’s port name tends to fail loudly when the tool can detect the missing reference and dangerously quietly when it cannot, which is why Module 2 treats constraint correctness as seriously as it does.
In the next article we list every stage OpenLane runs, in order, one sentence each and the module that covers it, the reference map for the rest of the course.