BOM generation
Bill-of-materials generation creates a list of the components used in the design in a text format (CSV). The components in the list are often grouped together if they share certain parameters, for example, the same manufacturer part number.
To generate the BOM, pass the -b [output-file-path] flag to the circuitscript CLI. The flag itself is required to enable BOM generation; the output path is optional. When the path is omitted, the BOM is written to <input>.bom.csv — the full input filename plus the .bom.csv suffix, so foo.cst produces foo.cst.bom.csv. When reading from stdin or --input, it defaults to output.bom.csv instead. The BOM is always generated as a .csv file.
BOM configuration
Two properties determine how the BOM is generated:
document.bom.columnssets the component parameters that are displayed as columns in the BOM.document.bom.group_bysets how components are grouped. Components whose values for everygroup_bykey are equal collapse into a single row, and theirrefdescells are concatenated (for example"C1, C2").
Both properties have defaults defined in std, so setting them is optional:
document.bom.columns = ["refdes", "mpn", "manufacturer", "footprint"]
document.bom.group_by = ["mpn", "manufacturer"]
Two constraints apply:
- Every
group_bykey must also appear incolumns. Otherwise the build fails with error"Invalid group_by keys". columnsis open-ended — any parameter name works as a column, includingrefdes,value,size,footprint,description,mpn,manufacturer, or any custom parameter.refdesis always available even though it is not a stored parameter. Any parameter that is unknown or unset on a component renders as an empty cell.
Example:
from "std" import *
document.bom.columns = ["refdes", "mpn", "manufacturer", "footprint", "description"]
document.bom.group_by = ["mpn", "manufacturer"]
v5 = supply("5V")
gnd = dgnd()
at v5
wire down 100
add cap(100n)
..mpn = "CL05B104KP5VPNC"
..manufacturer = "Samsung Electro-Mechanics"
wire down 100
to gnd
at v5
wire down 100
add cap(100n)
..mpn = "CL05B104KP5VPNC"
..manufacturer = "Samsung Electro-Mechanics"
wire down 100
to gnd
at v5
wire down 100
add res(10k)
..mpn = "RC0402JR-0710KL"
..manufacturer = "Yageo"
wire down 100
to gnd
The ..<param> = <value> prefix sets a parameter on the most recently added or referenced component (it resolves against the last object reference in scope). It is order-sensitive — an intervening component reference changes what .. binds to, so place these assignments immediately after the component they target. See this page for more info about the double dot operator.
The output BOM csv file:
Refdes,Mpn,Manufacturer,Footprint,Description
R1,RC0402JR-0710KL,Yageo,Resistor_SMD:R_0402_1005Metric,RES 10k OHM 0402
"C1, C2",CL05B104KP5VPNC,Samsung Electro-Mechanics,Capacitor_SMD:C_0402_1005Metric,
The resistor sorts before the capacitors because res has a lower type-sort order than cap (see Row order, grouping, and column values). The res() component defines a description parameter ("RES {value} OHM {size}"), so R1's Description cell renders RES 10k OHM 0402. The cap() component has no description parameter at all, so the capacitor's Description cell is empty — this demonstrates the "unset parameter renders as an empty cell" behavior described above.
Row order, grouping, and column values
A few behaviors of the BOM output are worth calling out:
- Row ordering is by component type first (
res,cap,ind,diode,ic,conn, then everything else), and within a type by refdes in natural order — the refdes prefix alphabetically, then the number numerically. SoR2sorts beforeR10(not the string orderR10,R2). - Grouped rows merge each column across all members. When members share a value it appears once; when they differ, the distinct values are concatenated into a comma-separated cell (for example two capacitors with different
mpnvalues render as"PART_A, PART_B"). - Unset columns produce empty CSV cells; no error is raised.
- CSV headers capitalize only the first letter of each column key (
mpnbecomesMpn,lcscbecomesLcsc).
Do not install (place)
Every component carries a boolean place parameter that defaults to installed. Setting ..place = false marks a component as not installed (a "do not install" part).
document.bom.only_placed (default true) controls whether not-placed components appear in the BOM. When it is true, not-placed components are excluded from the BOM entirely. Set it to false to keep them in the list.
from "std" import *
document.bom.columns = ["refdes", "value", "footprint"]
document.bom.group_by = ["value"]
v5 = supply("5V")
gnd = dgnd()
at v5
wire down 100
add res(10k)
wire down 100
to gnd
at v5
wire down 100
add res(1k)
..place = false
wire down 100
to gnd
With the default only_placed = true, the not-placed resistor is omitted:
Refdes,Value,Footprint
R1,10k,Resistor_SMD:R_0402_1005Metric
Adding document.bom.only_placed = false keeps it in the list:
Refdes,Value,Footprint
R1,10k,Resistor_SMD:R_0402_1005Metric
R2,1k,Resistor_SMD:R_0402_1005Metric
Parameter assignment rules
Component parameters can be directly assigned for each component as shown in the example above. However, in large designs with many components, this is tedious to maintain and change when MPN/manufacturer changes are needed.
Instead of manually setting parameters for each component, rules can be used to assign parameters.
from "std" import *
v5 = supply("5V")
gnd = dgnd()
at v5
wire down 100
add cap(100n)
wire down 100
to gnd
at v5
wire down 100
branch:
wire down 100
add cap(100n)
wire down 100
to gnd
branch:
wire right 400 down 100
add cap(100n)
wire down 100
to gnd
branch:
wire right 800 down 100
add res(10k)
wire down 100
to gnd
# Parameter assignment rules
set: "mpn", "manufacturer":
type: "res", size: "0402", value:
10k: "RC0402JR-0710KL", "Yageo"
type: "cap", size: "0402", value:
100n: "CL05B104KP5VPNC", "Samsung Electro-Mechanics"
In the example above, the following rules are defined:
- If component is type
res(resistor), has asizeparameter of value0402and avalueparameter of value10k:- assign
mpnto beRC0402JR-0710KL - assign
manufacturerto beYageo
- assign
- If component is type
cap(capacitor), has asizeparameter of value0402and avalueparameter of value100n:- assign
mpnto beCL05B104KP5VPNC - assign
manufacturerto beSamsung Electro-Mechanics
- assign
Because no columns are set here, the default columns apply. The output BOM csv file (note that R1 sorts before the grouped capacitors, which share one row as "C1, C2, C3"):
Refdes,Mpn,Manufacturer,Footprint
R1,RC0402JR-0710KL,Yageo,Resistor_SMD:R_0402_1005Metric
"C1, C2, C3",CL05B104KP5VPNC,Samsung Electro-Mechanics,Capacitor_SMD:C_0402_1005Metric
How matching works
A rule assigns its parameters to a component only when all of the rule's conditions match (a logical AND). The matching semantics have several details worth knowing:
type:is special — it matches the component's type (res,cap, …), not a stored parameter.- Condition keys are bare identifiers (
type,size,value), while the parameter names in theset:header are quoted strings ("mpn","manufacturer"). Quoting a condition key silently prevents it from matching. - String values are compared exactly and case-sensitively (
size: "0402"). Numeric values are normalized before comparison, sovalue: 100nalso matches a component whose value is0.1u, and10kmatches10000. - Condition parameters such as
size/valuemust exist on the component.res()andcap()default tosize "0402", which is why size-keyed rules work; parts of other sizes silently do not match. - No match leaves the parameters unset and raises no error — the corresponding BOM cells are simply empty. Running the CLI with
--verbosereports which values were missing. - Execution order matters. A
set:block only affects components that already exist when the statement runs, so placeset:blocks after the components they target. Within one block, the first matching branch wins; across blocks, a laterset:overrides an earlier one (last write wins) for any component that exists when each block runs.
Creating parameter rules
The set command starts with the name of the parameters that should be assigned. Multiple set commands may be defined at any point in the code, and later set commands override earlier ones — but this override only applies to components that already exist when each set command runs.
The following example creates a rule that assigns the mpn and manufacturer parameter of components:
set: "mpn", "manufacturer"
Rules are defined in <parameterName>: <targetValue> pairs; these can be merged with commas to reduce the indentation needed.
The parameter value(s) to be assigned follow the last rule, separated by a colon. If there are multiple parameters to be assigned, each value is separated by commas.
The following parameter rules are all equivalent and perform the same parameter assignment.
# 2 parameters to be assigned
set: "mpn", "manufacturer":
# rules
type: "res", size: "0402", value:
10k: "RC0402JR-0710KL", "Yageo" # set first parameter, then second parameter
# 2 parameters to be assigned
set: "mpn", "manufacturer":
# rules
type: "res":
size: "0402":
# final rule and values to be assigned
value: 10k: "RC0402JR-0710KL", "Yageo"
# 2 parameters to be assigned
set: "mpn", "manufacturer":
# rules
type: "res", size: "0402":
value: 10k: "RC0402JR-0710KL", "Yageo"