| src | ||
| LICENSE | ||
| nim.cfg | ||
| nimcdl.nimble | ||
| README.es.md | ||
| README.md | ||
NimCDL: Nim Circuit Design Language
This package is a small DSL (Domain Specific Language) for Nim that allows to design electronic circuits, make a small evaluation (composer.nim) and generate a netlist for Fritzing or Kicad.
It is still in the experimental phase and has many things to finish (See TODO).
Installation
Having installed nim and nimble, it is installed with just a simple command:
nimble install nimcdl
Use
This library defines a DSL to firstly define the circuit components, then the circuit itself and finally compose and export it.
Clearly the first thing we will have to do in our Nim script is to import this library:
import nimcdl
Defining a component
In the future, components may be imported and/or this library will have standard components. For the moment we will have to define each one of the components that we are going to use.
The first thing will be to assign the component to a variable and start a definition:
let led = component("led"):
add pins
Each electronic component has a series of pins that can be inputs or outputs (they can also be both, but this is not yet supported), to define these pins, we simply add the following to the previous code:
let led = component("led"):
pins:
P1 -> PinIn((1.0,5.0), (0.1,0.3))
P2..P3 -> PinOut()
Here we see pin 1(P1), which is assigned as an input pin (PinIn), then we assign a minimum and maximum voltage supported by the component ((1.0,5.0)) and do the same with the amperage it requires ((0.1,0.3)).
Then, we see how you can assign a range of pin (PX..PY), in this case from pin 2 to pin 3 is assigned as an output pin (PinOut), in this case these default pins do not provide any electrical current (Voltage 0 and Amperage 0).
Logic of pins conections
The pins of our component may not work identical when certain pins are connected, so we can define the internal connections and their logics as follows:
let led = component("led"):
pins:
P1 -> PinIn((1.0,5.0), (0.1,0.3))
P2..P3 -> PinOut()
pinsLogic:
logic L1:
p2.p.maxVoltage = p1.p.maxVoltage
P1 <-> P2
P1<L1>P3
Let's go in parts, first connect pin 1 to pin 2, that is, if current enters through this, it leaves without being altered by pin 2 (P1 <-> P2). Then, between pin 1 and 3 we want to assign a logic, either resistance or any other alteration to the output pin or to the input current(net). For this we define the following:
logic L1:
p2.p.maxVoltage = p1.p.maxVoltage
Que gracias a macros se convertira en:
proc (netIn: var Net, p1, p2: var tuple[n: uint, p: Pin]) =
p2.p.maxVoltage = p1.p.maxVoltage
In the meantime, we specified to which pins this logic was assigned(P1P3)
De esta forma ya tendríamos nuestro componente listo.
Define a circuit
With our components already in variables, we are ready to initialize our circuit:
let ledCircuit = cirucit("ledTest"):
Creating a net
When defining nets, the first one we define will be our input network while the last one, the output network. At the moment, we can only name it but not assign the input current. Although after composing the circuit, we will be able to know the minimum and maximum voltage and amperage necessary for these.
let ledCircuit = cirucit("ledTest"):
nets:
N0 -> Net(name: "VDD")
N1 -> Net()
Adding components
Another fundamental part of any circuit, without a doubt, are its components. These are assigned in such a way:
let ledCircuit = cirucit("ledTest"):
nets:
N0 -> Net(name: "VDD")
N1 -> Net()
components:
CL0 -> f
CL1 -> f
As we can see, we can choose a reference for the component with this format CXY, where X is just a letter to identify the component and Y a number that can have as many digits as you want.
Conections
Now the final touch, the connections between networks and components or between components, for this we have several ways to express it thanks to DSL. The first is the connection of pin to pin or network to pin, ideal for making circuits in serial, which can be put in these ways:
N0 <-> P1.CL0.P2 <-> N1
N0 <-> CL0.P1
N1 <-> CL0.P2
N0 <-> P1.CL0.P2 <-> P1.CL1.P2 <-> N1
The second is a multi-pin network, perfect for parallel connections:
N0 || N1
|| P1.CL0.P2
|| P1.CL1.P2
| N0
| CL0.P1
| CL1.P1
So, our circuit could be something like this:
let ledCircuit = cirucit("ledTest"):
nets:
N0 -> Net(name: "VDD")
N1 -> Net()
components:
CL0 -> f
CL1 -> f
connections:
N0 || N1
|| P1.CL0.P2
|| P1.CL1.P2
Composer
Finally, we compose the circuit, where the composer transforms the network to be easily exported and make a slight evaluation of it (In the future it will be more exhaustive).
let final = ledCircuit.compose(2)
The 2 is the total number of networks that we have defined in the nets section.
This function returns a tuple with the final circuit (final.circuit) and a collection of errors found in the circuit (final.errors).
Export
With this circuit in Nim we will not be able to do much more (for the moment), therefore we have to export it to a netlist:
- Kicad: with the function
toKicadNetlist('D')(final.toKicadNetlist('D')) - Fritzing: with the function toFritzingNetlist("circuit name")` (final.toFritzingNetlist("ledTest"))
Cotribute
Pulls requests accepted and well valued!!. I'm going to start another project and I don't have time to keep progressing in this one. Besides the TODOs in the code, here is a list of things to do:
- Composer: Make a more correct ERC check and optimize.
- types: add more important information.
- DSL: add the buses (and document them) and allow to add logics when you get the necessary current for a network.
- Create an importer to be able to import components from outside.
- make a general code improvement
- Other