Restrict¶
restrict
can be used in coordination with require
to add specific constraints on which pins are valid. We can specify all pins in a bundle to come from a specific IO bank, or that a timer has some advanced feature, or has a specific electrical property. The concept is that while supports statements are additive - the restrict
statements are subtractive.
The other useful feature for restrict
is that it is a constraint that can be added at point of use. This allows the user to construct a general purpose component that can be used in many circuits, and then customize the pin assignment problem for a particular board design.
Signature¶
restrict(<REQ-PORT>, <FUNC>)
<REQ-PORT>
- Reference to aSinglePin
abstract port
defined by the require statement.<FUNC>
- Reference to a function that takes aPort
argument and returns a boolean (ie,True|False
)- This function must return
true
if the passedPort
should be an option in the solution to the pin assignment problem. - This function must return
false
if thisPort
should be rejected from the solution to the pin assignment problem.
Usage¶
When we use the restrict
statement, we are typically evaluating the properties
of the pins of a component or module. Here is a trivial example:
pcb-bundle gpio:
port single
pcb-module restrict-a-pin-set :
port p : pin[2]
property(p[0].active) = true
property(p[1].active) = false
for i in 0 to length(p) do:
supports gpio :
gpio.single => p[i]
require g:gpio from self
restrict(g.single,
fn (mypin) :
property(mypin.active))
In this example, the module has a port array p
with two single pins. We then apply an active
property to both of these single pins. This active
property is what our restrict
statement is going to check. Later the restrict
statement checks whether the active
property is true
and only allows p[0]
to be selected in the pin assignment solution.
Notice that the restrict
statement is referring one of the SinglePin
ports of the abstract port g
of type gpio
bundle. The restrict
statement must refer to an abstract port
(ie, a port defined by a restrict statement). If the restrict
were to refer to a module or component port, this would be an error.
Here we are using an anonymous function (kind of like a lambda in python) to implement the restriction function:
fn (mypin) :
property(mypin.active)
If you squint - this kind of looks like a function definition:
defn my-restrict-func (mypin) :
property(mypin.active)
In fact, there is no reason you couldn't use that style of definition, you would just need to define it ahead of time:
require g:gpio from self
defn is-active? (mypin) :
if has-property?(x.active):
property(x.active)
else:
false
restrict(g.single, is-active?)
NOTE - It is a good practice to make sure that all of the pins that are going to participate in this
restrict
functionality define theproperty
that will be examined in the restrict function. In this example, theactive
property is applied to all of the pins inp
- not just the pins inp
that areactive=true
.In the above example, the
has-property?
function is a useful tool to detect if/when a pin is missing a property and take a reasonable default action.
Why use a restrict
?¶
For the most part, any restrict
statement could also be implemented with a supports
statement. The restrict
is kind of like the DeMorgan's equivalent version.
So what is the purpose of the restrict
statement if we already have supports
statements?
- Sometimes the
restrict
statement version is less verbose than the equivalentsupports
statements. - Often times the
supports
statement that satisfies a particularrequire
statement are not co-located.- Placing a
restrict
statement in the same scope as therequire
statement can often make the code more readable. - It can be easier to document the reason for a particular
restrict
when it is co-located with therequire
- Placing a
- The
supports
statements are often broad constraints that apply generally to a particular component.- The
restrict
statement is often a refinement of those broad constraints for your particular application. - This also means that we can define a general component that gets used in many circuits, and then add application specific constraints at point of use with a
restrict
- The
Examples¶
Gigabit Transceivers¶
In many FPGAs, there are often one or more classes of serializers/transceivers for high speed communications. These typically are differential pair transceivers and result in the use of a diff-pair
bundle for all of them.
To differentiate between these different differential pairs, we can set a property
on those differential pairs and use a restrict
statement. Full listing of the example code.
defn is-gigabit-transceiver (x) -> True|False :
if has-property?(x.gigabit-transceiver):
property(x.gigabit-transceiver)
else:
false
public pcb-module gigabit-restrict-example:
inst fpga : Xilinx-FPGA
inst conn : VPX-conn
require h-dp:diff-pair from fpga
require c-dp:diff-pair from conn
for dp in [h-dp, c-dp] do:
restrict(dp.P, is-gigabit-transceiver)
; Connect the two high-speed capable differential pairs.
net (h-dp, c-dp)
This is the top-level of the example demonstrating the use of the restrict
statement with the helper function is-gigabit-transceiver
.
Notice that the restrict
statement can be placed anywhere within the scope of this pcb-module
. The only requirement is that it can uniquely identify which abstract port
that the statement applies to.
In the restrict
statement, we apply the restriction only to the dp.P
, or the positive side of the differential pair. We don't have to add the dp.N
restriction because it is basically implied by the supports diff-pair
constraint. It wouldn't hurt anything to add it, but it would make the pin assignment problem more computationally complex.
The property gigabit-transceiver
is applied in the pcb-component
definition for the FPGA and the connector. Here is a slimmed down example from the FPGA:
public pcb-component Xilinx-FPGA :
name = "Xilinx"
description = "Excerpt of an IO Bank & High Speed Transceiver"
manufacturer = "Xilinx"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Int]
[IO_L2N | C[3] | Right | 0 ]
[IO_L2P | C[2] | Right | 0 ]
[MGTHRXN0 | D[3] | Right | 0 ]
[MGTHRXP0 | D[4] | Right | 0 ]
pin-properties :
[pin:Ref | clock-capable:(True|False) | gigabit-transceiver:(True|False)]
[IO_L2N | false | false]
[IO_L2P | false | false]
[MGTHRXN0 | false | true]
[MGTHRXP0 | false | true]
make-box-symbol()
assign-landpattern(FPGA-pkg)
supports diff-pair:
diff-pair.P => self.IO_L2P
diff-pair.N => self.IO_L2N
supports diff-pair:
diff-pair.P => self.MGTHRXP0
diff-pair.N => self.MGTHRXN0
Here we use two pin-properties
tables. The first pin-properties
table defines the pins, pads, and bank associations. The second pin-properties
table defines other properties related to each of those pins. In particular - it defines the gigabit-transceiver
property as a boolean value in the 3rd column. Notice that these properties are on a per-pin basis - not on the bundle.
At the end of the component, we define two diff-pair
supports. Neither of these supports
statements care about the gigabit-transceiver
properties. In absence of the restrict
statements, a require
statement for a diff-pair
bundle might select either of these two diff-pair
bundles.
Example rendering in the board view:
Notice that the differential pair rat's nest only connects to the MGTHRXN0/MGTHRXP0
pair on the FPGA.