Coder Social home page Coder Social logo

pcbdl's Introduction

PCB Design Language

A programming way to design schematics.

Sphinx Documentation

Installing

PyPI version

sudo apt-get install python3 python3-pip python3-pygments

sudo pip3 install pcbdl

Interactive terminal

A good way to try various features without having to write a file separately.

python3 -i -c "from pcbdl import *"

Language

PCBDL's goal is to allow designing schematics via Code. Similar to how VHDL or Verilog are ways to represent Logic Diagrams in code form, PCBDL the analogous of that but for EDA schematics.

To start one should define a couple of nets:

>>> vcc, gnd = Net("vcc"), Net("gnd")
>>> vin = Net("vin")
>>> base = Net("base")

We then can connect various components between those nets with the << operator and the to= argument (for the other side):

>>> base << C("1000u", to=vin)
>>> base << (
    R("1k", to=vcc),
    R("1k", to=gnd),
)

2 pin devices (aka JellyBean in pcbdl) like capacitors and resistors are one of the easiest things to connect. Internally they have a primary (connected with >>) and secondary, other side, pin (connected with to=).

Let's try to get more complicated by defining a transistor and connect some of its pins.

>>> class Transistor(Part):
    REFDES_PREFIX = "Q"
    PINS = ["B", "C", "E"]
...
>>> q = Transistor()
>>> base << q.B

Nets can also be created anonymously if started from a part's pins:

>>> q.E << (
    R("100", to=gnd),
    C("1u", to=gnd),
)

Let's finish our class A amplifier (note how we created the "vout" net in place, and how we gave a name ("Rc") to one of our resistors):

>>> q.C << (
    C("100u", to=Net("vout")),
    R("100", "Rc", to=vcc),
)

Note: One can find a completed version of this amplifier in examples/class_a.py:

python3 -i examples/class_a.py

One can now give automatic consecutive reference designators to components that haven't been named manually already:

>>> global_context.autoname()

Then one can explore the circuit:

>>> global_context.parts_list
[R1, R2, R3, Rc, C10, C11, Q1, C12]

>>> global_context.parts_list[0].refdes
'R1'
>>> global_context.parts_list[0].value
'1kΩ'
>>> global_context.parts_list[0].pins
(R1.P1, R1.P2)
>>> global_context.parts_list[0].pins[1].net
VCC(connected to R1.P2, R2.P2)

>>> nets
OrderedDict([('VCC', VCC(connected to R1.P2, R2.P2)), ('GND', GND(connected to R3.P2, Rc.P2, C10.-)), ('VIN', VIN(connected to C11.-)), ('VOUT', VOUT(connected to C12.-))])

>>> nets["GND"]
GND(connected to R3.P2, Rc.P2, C10.-)

Examples

Found in the examples/ folder. Another way to make sure the environment is sane. One can just "run" any example schematic with python, add -i to do more analysis operations on the schematic.

  • voltage_divider.py: Very simple voltage divider
  • class_a.py: Class A transistor amplifier, a good example to how a complicatedish analog circuit would look like.
  • servo_micro.py: Servo micro schematics, reimplementation in pcbdl, originally an 8 page pdf schematic.

Exporting

Netlists

The main goal of this language is to aid in creating PCBs. The intermediate file format that layout programs (where one designs physical boards) is called a netlist. We must support outputting to that (or several of such formats).

We have a an exporter for the Cadence Allegro Third Party netlists (found in netlist.py):

>>> generate_netlist("/tmp/some_export_location")

HTML

This produces a standalone html page with everything cross-linked:

  • List of nets with links to the parts and pins on each
  • List of parts with the part properties and a list of pins linking to the nets connected
  • Highlighted source code, with every variable and object linked to the previous 2 lists

Here's an example of such a html output for servo micro.

Schematics / Graphical representation of the circuit

In order for schematics to be more easily parsable, we want to graphically display them. The netlistsvg project has been proven to be an excellent tool to solve the hard problems of this. See pcbdl/netlistsvg.py for the implementation. To use the following commands you'll have to install netlistsvg.

  1. Convert a pcbdl schematic back into a traditional schematic

    generate_svg('svg_filename')

    Here's the svg output for the servo_micro example.

  2. Isolated schematics for how a particular thing is hooked up:

    generate_svg('svg_filename', net_regex='.*(SDA|SCL).*', airwires=0)
    generate_svg('svg_filename', net_regex='.*(PP|GND|VIN|VBUS).*')
  3. Block diagrams of the overall system

    • This depends on how the schematics is declared, if it's not hierarchical enough, it won't have many "blocks" to display
    • This task is dependent on allowing hierarchies in pcbdl

BOM

Bill of Materials would be a trivial thing to implement in pcbdl.

ERC

Electrical Rule Checking. How to unit test a schematic?

This is a big TODO item. The basic idea is that the pins will get annotated more heavily than normal EDA software.

Pins will have beyond just simple input/output/open drain/power properties, but will go into detail with things like:

  • Power well for both inputs and outputs
  • ViH, ViL
  • Output voltages

With this information it should be possible to make isolated spice circuits to check for current leaks. For every net, for every combination of output pins on that net, are all the input pins receiving proper voltages?

Importing from traditional EDA schematics

Given that graphical exporting might be impossible, and in lieu of the language being slightly more unreadable than normal schematics, perhaps we should just use pcbdl as an intermediate data format or a library.

The way one would use it would be to import a kicad schematic, annotate it with a few more classes (for BOM and ERC purposes, unless we can find a way to put all metadata in the kicad schematics). Then all exporting and analysis features of pcbdl can still be used.

A kicad importer should be pretty trivial to implement. TODO

Support

This is not an officially supported Google product.

The language itself is still in flux, things might change. A lot of the syntax was added as a demo of what could be possible, might still need polishing. Please don't use this language without expecting some tinkering to keep your schematics up to date to the language.

Credits / Thanks

  • CBOLD for the idea
  • netlistsvg for svg output support
  • Chrome OS Hardware Design Team for feedback

pcbdl's People

Contributors

amstan avatar kasbah avatar kiavash-at-work avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pcbdl's Issues

view is not defined

In the example class_a.py, line 38:

q.BASE << (
    C(ac_coupling_value, to=Net("vin")),
    R("1k", to=vcc),
    R("1k", to=gnd),
)
view(R)

an error is thrown:
NameError: name 'view' is not defined

After searching the source for view it's not found anywhere.

EDIT: Mistakenly submitted

Eagle input module

It would help the migration out of Eagle into PCBDL significantly if there were an Eagle plugin that exports the schematic to pcbdl language.

Not populated in HTML

When the part is not populated, the generated HTML should mention "Do Not Populate!"

--- a/pcbdl/html.py
+++ b/pcbdl/html.py
@@ -63,9 +63,13 @@ class HTMLPart(Plugin):
 
				if part.__doc__:
						yield "<pre>%s</pre>" % textwrap.dedent(part.__doc__.rstrip())

				yield "<p>Value: %s</p>" % part.value
-               yield "<p>Part Number: %s</p>" % part.part_number
+
+               if part.populated:
+                       yield "<p>Part Number: %s</p>" % part.part_number
+               else:
+                       yield "<p>Do Not Populate!</p>"

Use of LED leads to for-pcbdl netlistsvg crash

Thanks Alan for finding this!

For the following test file:

from pcbdl import *

pp3300 = Net("PP3300")

led = LED()
pp3300 << led.P1
Net("LED_K") << led.P2

You might get an error with the current https://github.com/amstan/netlistsvg/tree/for-pcbdl:

TypeError: Cannot read property '1' of undefined
    at Cell.renderAirwire (/home/amstan/Projects/netlistsvg/built/Cell.js:443:22)
    at /home/amstan/Projects/netlistsvg/built/Cell.js:338:23
    at Array.forEach (<anonymous>)
    at Cell.render (/home/amstan/Projects/netlistsvg/built/Cell.js:327:30)
    at /home/amstan/Projects/netlistsvg/built/drawModule.js:25:18
    at Array.map (<anonymous>)
    at Object.drawModule [as default] (/home/amstan/Projects/netlistsvg/built/drawModule.js:23:30)
    at /home/amstan/Projects/netlistsvg/built/index.js:57:61

This is due to this line: https://github.com/amstan/netlistsvg/blob/for-pcbdl/lib/Cell.ts#L464

Problems don't end there:

Looks like netlistsvg.py also outputs the json wrong for LEDs, even though netlistsvg will render it as a generic, we still output it as if it puts a skinned JellyBean (with pins named A and B). My netlistsvg modifications aren't reliable enough and it crashes when it looks a pin name and pin number syntax and doesn't find any because it's a generic.

Have a version for design files

So if a design is done for a specific version and the future versions have changed the normaclaiture, the design file still compiles correctly until the new release.

Net names + airwires

Nets should be able to get labeled with the net name.

If a net doesn't go anywhere but one pin, it should make a little stub, long enough to fit its name.

Duplicate of nturley/netlistsvg#36

Right now we can simulate this by uncommenting

pcbdl/pcbdl/netlistsvg.py

Lines 203 to 204 in 0c02c3d

##if len(pin_net_helper.grouped_connections) > 1:
#self.attach_net_name_port(pin.net, net_node_number, port_directions[name])

Add tests for connection lines starting from pins

In d53d183 I added to the functionality that allows a Pin to be used instead of a Net for a lot of cases.

Should write tests for that.

Perhaps make a Connectable protocol: something that implements .connect, .__lshift__ and .__rshift__. Pin and Net would both support it.

Multiple GND symbols

Generated SVG treats all nets with GND in it the same GND symbol, however it would be better to have different symbols for AGND and GND.

servo_micro netlist output should match cadence netlist output

Given that servo_micro is a reimplementation of an older design which already has a netlist output, we should put that old output in a test/ folder and do a diff compared to what we ouput.

This output being correct would be a huge benefit as an integration test for a big portion of the project.

Barring small differences (normalization, ANON_NETS, header at the top) there shouldn't be much of a difference.

saved_net = Net() ^ R() ^ Net() on one line

Sometimes one wants to write something like this near an MCU:

saved_net_variable = Net("MORE_HERE") ^ R("100") ^ Net("NEARBY_PIN") << mcu.PIN

The idea is that saved_net_variable(aka Net("MORE_HERE")) is used in other places in the circuit later on.

The problem is that the ^ operator makes the expression lose the reference to Net("MORE_HERE")`

Then in needs to be rewritten in separate lines:

saved_net_variable = Net("MORE_HERE")
saved_net_variable ^ R("100") ^ Net("NEARBY_PIN") << mcu.PIN

But that's kind of ugly, and both me and Jim don't like it (especially if there's many of them).

The to= argument could have done this on one line, but it's even uglier:

saved_net_variable = Net("MORE_HERE") << R("100", to=(Net("NEARBY_PIN") << mcu.PIN))

Here's a bug to dump thoughts about improving this.

Part.refdes should have a setter with more checks

While it's not really possible to define a part with the same refdes as another par normally.

Part(refdes="C1")
C1
Part(refdes="C1")
Traceback (most recent call last):
File "", line 1, in
File "/home/amstan/Projects/pcbdl/pcbdl/base.py", line 532, in init
Plugin.init(self)
File "/home/amstan/Projects/pcbdl/pcbdl/base.py", line 54, in init
instance.plugins = {plugin: plugin(instance) for plugin in factories}
File "/home/amstan/Projects/pcbdl/pcbdl/base.py", line 54, in
instance.plugins = {plugin: plugin(instance) for plugin in factories}
File "/home/amstan/Projects/pcbdl/pcbdl/context.py", line 137, in init
global_context.new_part(instance)
File "/home/amstan/Projects/pcbdl/pcbdl/context.py", line 36, in new_part
raise Exception("Cannot have more than one part with the refdes %s in %s" % (part.refdes, self))
Exception: Cannot have more than one part with the refdes C1 in <pcbdl.context.Context object at 0x7fdd3eb02190>

The check gets bypassed if one were to do part.refdes="something_already_in_use", like the autoname system. This really ought to be implemented differently instead. Perhaps with a setter that checks if there's duplicates instead.

Not populated in SVG

Similar to #2, there should be something special in the SVG if a part is not populated. Jim suggested a crossed out shape on top of the part. Not sure how this will work with netlistsvg (hopefully I won't have to parse the SVG output and bypass netlistsvg).

Reorganize modules

  • "context" => "schematic" or something like that. This is really the thing that will be added upon to do hierarchies.

  • "defined_at" => "inspect"

  • Some of the code should be moved out of base, context and defined_at that deal with consistent naming.

  • base should perhaps be split up in each of the big classes.

Better part fields framework

We probably want to have a richer way to define fields on parts. Something that's easy to handle and inherit from.

Potential fields:

  • value
  • part_number
  • populated
  • datasheet
  • manufacturer
  • custom ones? some parts want more fields: resistors have tolerances

Wire Bundles/Ports

Sometimes parts have pins that belong to groups (ex: SPI, SDIO). Those pins shouldn't really force the user to connect each pin individually, but there should be a way to handle the connections in a group.

Example:

NetBundle("SPI_FLASH_") << mcu.SPI1 >> flash.spi

Instead of:

Net("SPI_FLASH_MOSI") << mcu.SPI1_MOSI >> flash.MOSI
Net("SPI_FLASH_MISO") >> mcu.SPI1_MISO << flash.MISO
Net("SPI_FLASH_CLK") << mcu.SPI1_CLK >> flash.CK
Net("SPI_FLASH_CS") << mcu.SPI1_CS >> flash.CS_L

More mockups to follow.

global_context.autoname() causes every wire to be labeled ANON_NET in SVG

Running the following causes an SVG to be generated which looks like
test

import pcbdl as dl

ac_coupling_value = "1000u"

vcc, gnd = dl.Net("vcc"), dl.Net("gnd")

q = dl.BJT("2n3904")

C = dl.C_POL

q.BASE << (
    C(ac_coupling_value, to=dl.Net("vin")),
    dl.R("1k", to=vcc),
    dl.R("1k", to=gnd),
)

q.COLLECTOR << (
    dl.C(ac_coupling_value, to=dl.Net("vout")),
    dl.R("100", to=vcc),
)

q.EMITTER << (
    dl.R("100", "Rc", to=gnd),
    dl.C("1u", "C10", to=gnd),
)

dl.global_context.autoname()

svg_arr = []
for svg in dl.generate_svg('test'):
    svg_arr.append(svg)

f = open("test.svg", "a")
f.write(svg_arr[0])
f.close()

Commenting out dl.global_context.autoname() produces a much more readable schematic, however now components are not named sensibly:
test(1)

Proposed change is that autonameing doesn't cause anonymous nets to be displayed.

generate_svg does nothing

After following the instructions here, and running the following code from the root directory of pcbdl:

import pcbdl as dl

vcc, gnd = dl.Net("vcc"), dl.Net("gnd")
vin = dl.Net("vin")
base = dl.Net("base")

base << dl.C("1000u", to=vin)
base << (
    dl.R("1k", to=vcc),
    dl.R("1k", to=gnd),
)

class Transistor(dl.Part):
    REFDES_PREFIX = "Q"
    PINS = ["B", "C", "E"]

q = Transistor()

q.E << (
    dl.R("100", to=gnd),
    dl.C("100u", to=gnd),
)

q.C << (
    dl.C("100u", to=dl.Net("vout")),
    dl.R("100", "Rc", to=vcc),
)

dl.global_context.autoname()


dl.generate_svg('class_svg'):

Nothing happens. After investigating the generate_svg function, it appears that the function doesn't do anything since it is a generator with a try/except block which yields svg_contents if they exist. However, SVGPage.PageEmpty exception is always raised by n.generate(). After investigating further, it seems that the page is always empty because for some reason in add_parts(), self.should_draw_pin(pin) always returns None or False. I'm guessing that the None return behavior is from returning an empty regex match, but it seems that the return type should be uniform. After setting should_draw_pin to the following:

def should_draw_pin(self, pin):
    return True

then the following produces an SVG:

svg_arr = []
for svg in dl.generate_svg('class_svg'):
    svg_arr.append(svg)

However, it doesn't write the SVG file as documented, and instead seems to produce the raw SVG data.

RefdesRememberer broken due to capacitor value change

index d42fef2..2928e40 100755
--- a/examples/servo_micro.py
+++ b/examples/servo_micro.py
@@ -576,7 +576,7 @@ pp3300 << (
 Net("PP3300_PD_VDDA") << (
     ec.VDDA,
     L("600@100MHz", to=pp3300, package="INDC1005L", part_number="FERRITE_BEAD-185-00019-00"),
-    decoupling("1u"),
+    decoupling("10u"),
     decoupling("100p"),
 )
 pp3300 << (

This causes a lot of components to get renamed. Another reason to need #21.

Improve Consistent/Deterministic refdeses

I don't really want people to have to worry about refdeses inside the code. Part initializations should not be littered with refdes="Uwhatever".

Right now Context.autoname, beyond numbering parts automatically, also remembers what things were named (via the .refdes_mapping files). But it's hard to track parts via just the code, since there's nothing anchoring part instances across different runs to the place where they're defined.

Some traditional EDAs solve this problem by having an UUID for each part (which stays for the life of it, inside the source file), then layout tools don't even care if the refdeses change, as long as the UUID didn't. We don't have that luxury, unless we want things like R("100k", uuid="d4c1d842-705b-456d-9ee1-585463c11db2") everywhere.

The current implementation of Context.autoname uses the nearby lines contents around every part initialization to track a part. The problem with this is that a simple change to something maybe unrelated, or a simple change to the value of a part, causes the part's refdes to "pop" and get forgotten.

We need to track a few more things beyond just lines of code.

"Design doc"
https://github.com/google/pcbdl/wiki/Consistent-refdeses

Write tests for RefdesRememberer

Together with issue #4, we should write a few tests for making sure refdeses do stay consistent. Something like a copy of servo_micro, and a ton of patches on top of it (all parented to one servo_micro, not on a chain, more like a star). Apply each of those patches then see if refdeses are sticking.

git checkout c718fbc~1 examples/servo_micro.py from ba391e8 is a good idea.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.