Contents¶
PandABlocks-FPGA¶
PandABlocks-FPGA contains the firmware that runs on the FPGA inside a Zynq module that is the heart of a PandABlocks Device like PandABox.
What can PandABlocks do?¶
PandABlocks is a framework enabling a number of functional Block instances to be written and loaded to an FPGA, with their parameters (including their connections to other Blocks) changed at runtime. It allows flexible triggering and processing systems to be created, by users who are unfamiliar with writing FPGA firmware.
<insert diagram of blocks, wrappers and system architecture here>
How is the documentation structured?¶
The documentation is structured into a series of Tutorials and some general Reference documentation. End users and developers need different documentation, so links for various categories of user are listed below:
Generating a new set of Blocks for a PandABlocks device¶
Read Available Blocks to find out what already exists, then read Assembling Blocks into an App to see how to make an App of these Blocks that can be loaded to a PandABlocks device.
Extending the functionality of a PandABlocks device¶
Read Available Blocks to see if you need to create a new Block or add to an existing one. Read Writing a Block to find out how to specify the interface to a Block, VHDL entity, timing tests and docs.
Working on the core autogeneration framework¶
Read Writing a Block to find out how the process works, then Autogeneration framework architecture for more details on specific parts of the autogeneration framework
Blinking LEDs Tutorial¶
This tutorial will introduce you to the basics of PandABlocks, how to wire Blocks together to make different LEDs flash at different rates
Opening the GUI¶
Point your web browser at the ip address or hostname of the PandA and you will be greeted with a welcome page. At the bottom of this page will be links for Docs, Control and Admin. You can use the Control link to open the Web Control page that we will use in these tutorials. For more information on the Web Control, see its entry in the Docs section.
Loading the tutorial design¶
The Design dropdown box allows you to select from saved designs stored on the PandA. Selecting an item from this list will load the saved design over the current Block settings. You can use the Save method to save your current design if you wish to keep it.
Select “tutorial1_blinking_leds” from the box and you will be greeted with a layout of Blocks:

If you now look at the front panel of the PandA you should see the first 4 TTL output LEDs blinking sequentially.
How the design works¶
The CLOCKS Block is creating a 50% duty cycle wave with a period of 0.5s. PULSE1..4 are taking this as an input trigger, and producing a 0.1s width pulse with a different delay for each PULSE Block. These PULSE Blocks work as a delay line, queuing a series of pulses up to be sent out when the delay expires.
If you click on one of them you can modify its settings:

If you increase the delay you will notice that the Queued input will increase, but the PULSE Block will still continue outputting pulses after the desired delay. However if you increase the width beyond the pulse period the Block will drop the pulse to avoid running them together.
You can also try clicking on the CLOCKS Block to modify the period of the input pulse train.
You can also try wiring these outputs to different TTLOUT Blocks by clicking the Palette icon, dragging a TTLOUT Block onto the canvas, and connecting it up by dragging the PULSE out port to the TTLOUT val port.
Note
All the ports on these Blocks are blue. They represent bits, single on/off values that can propagate through the system by connecting Blocks together.
Conclusion¶
This tutorial has shown how to load a saved design and modify some parameters. It has also introduced the PULSE delay block that is useful for delaying and stretching trigger signals. It has introduced bit outputs and shown how they can be connected to the outside world using the TTLOUT Blocks. In the next tutorial we will read about position outputs, how they can be set and how they can be captured.
Position Capture Tutorial¶
This tutorial will introduce you to the Position Capture interface of PandABlocks, how to provide trigger signals to control when these capture points are taken and visualize the data.
Position Compare Tutorial¶
This tutorial will introduce you to the concept of Position Compare. It will show a one dimensional scan of an encoder, how to create trigger pulses at regularly spaced positional intervals, and capture time information.
Snake Scan Tutorial¶
This tutorial will introduce the concept of table based position compare using the SEQ block to do a two dimensional ‘snake’ scan. This is where the X dimension scans forward over the range, Y steps forward, then X scans backwards, repeated until the scan is complete.
Available Blocks¶
These are the Block types that may be built into an App. Some are soft blocks, and some are tied to particular hardware, so not all Blocks will be included in every PandABlocks Device.
BITS - Soft inputs and constant bits¶
The BITS block contains 4 soft values A..D. Each of these soft values can be set to 0 or 1 by using the SET_A..SET_D parameters.
Fields¶
Name | Type | Description |
---|---|---|
A | param bit | The value that output A should take |
B | param bit | The value that output B should take |
C | param bit | The value that output C should take |
D | param bit | The value that output D should take |
OUTA | bit_out | The value of A on the bit bus |
OUTB | bit_out | The value of B on the bit bus |
OUTC | bit_out | The value of C on the bit bus |
OUTD | bit_out | The value of D on the bit bus |
Outputs follow parameters¶
This example shows how the values on the bit bus follow the parameter values after a 1 clock tick propagation delay
(Source code, png, hires.png, pdf)

CALC - Position Calc¶
The position calc block has an output which is the sum of the position inputs
Fields¶
Name | Type | Description |
---|---|---|
INPA | pos_mux | Position input A |
INPB | pos_mux | Position input B |
INPC | pos_mux | Position input C |
INPD | pos_mux | Position input D |
TYPEA | param enum | Source of the value of A for calculation
0 Value
1 -Value
|
TYPEB | param enum | Source of the value of B for calculation
0 Value
1 -Value
|
TYPEC | param enum | Source of the value of B for calculation
0 Value
1 -Value
|
TYPED | param enum | Source of the value of B for calculation
0 Value
1 -Value
|
FUNC | param enum | Scale divisor after add
0 A+B+C+D
1 (A+B+C+D)/2
2 (A+B+C+D)/4
|
OUT | pos_out | Position output |
Scaling¶
The scale factor is a bit shift and is applied after the sum.
(Source code, png, hires.png, pdf)

CLOCKS - Configurable clocks¶
The CLOCKS block contains 4 user-settable 50% duty cycle clocks. The period can be set for each clock separately. When any clock period is set, all clocks restart from a common synchronous point.
Fields¶
Name | Type | Description |
---|---|---|
A_PERIOD | param time | Period of clock A output |
B_PERIOD | param time | Period of clock B output |
C_PERIOD | param time | Period of clock C output |
D_PERIOD | param time | Period of clock D output |
OUTA | bit_out | Clock A output |
OUTB | bit_out | Clock B output |
OUTC | bit_out | Clock C output |
OUTD | bit_out | Clock D output |
Setting clock period parameters¶
Each time a clock parameter is set, the clock restarts from that point with the new period value.
(Source code, png, hires.png, pdf)

All clocks have the same starting point¶
When any period parameter is set, all clocks restart from that point.
(Source code, png, hires.png, pdf)

COUNTER - Up/Down pulse counter¶
Each counter block, when enabled, can count up/down with user-defined step value on the rising edge on input trigger. The counters can also be initialised to a user-defined START value.
Fields¶
Name | Type | Description |
---|---|---|
START | param int | Counter start value |
STEP | param | Up/Down step value |
MAX | param int | Rollover value |
MIN | param int | Value to which counter should rollover to |
ENABLE | bit_mux | Halt on falling edge, reset amd enable on rising |
TRIG | bit_mux | Rising edge ticks the counter up/down by STEP |
DIR | bit_mux | Up/Down direction (0 = Up, 1 = Down) |
CARRY | bit_out | Internal counter overflow status |
OUT | pos_out | Current counter value |
Counting pulses¶
The most common use of a counter block is when you would like to track the number of rising edges received while enabled:
(Source code, png, hires.png, pdf)

You can also set the start value to be loaded on enable, and step up by a number other than one:
(Source code, png, hires.png, pdf)

You can also set the direction that a pulse should apply step, so it becomes an up/down counter. The direction is sampled on the same clock tick as the pulse rising edge:
(Source code, png, hires.png, pdf)

Rollover¶
If the count goes higher than the max value for an int32 (2147483647) the CARRY output gets set high and the counter rolls. The CARRY output stays high for as long as the trigger input stays high.
(Source code, png, hires.png, pdf)

A similar thing happens for a negative overflow:
(Source code, png, hires.png, pdf)

Edge cases¶
If the Enable input goes low at the same time as a trigger, there will be no output value on the next clock tick.
(Source code, png, hires.png, pdf)

If the step size is changed at the same time as a trigger input rising edge, the output value for that trigger will be the new step size.
(Source code, png, hires.png, pdf)

DIV - Pulse divider¶
A DIV block is a 32-bit pulse divider that can divide a pulse train between two outputs. It has an internal counter that counts from 0 to DIVISOR-1. On each rising edge of INP, if counter = DIVISOR-1, then it is set to 0 and the pulse is sent to OUTD, otherwise it is sent to OUTN. Change in any parameter causes the block to be reset.
Fields¶
Name | Type | Description |
---|---|---|
ENABLE | bit_mux | Reset on falling edge, enable on rising |
INP | bit_mux | Input pulse train |
DIVISOR | param | Divisor value |
FIRST_PULSE | param enum | Where to send first pulse
0 OutN
1 OutD
|
OUTD | bit_out | Divided pulse output |
OUTN | bit_out | Non-divided pulse output |
COUNT | read | Internal counter value |
Which output do pulses go to¶
With a DIVISOR of 3, the block will send 1 of 3 INP pulses to OUTD and 2 of 3 INP pulses to OUTN. The following two examples illustrate how the FIRST_PULSE parameter controls the initial value of OUT, which controls whether OUTD or OUTN gets the next pulse.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Reset conditions¶
If an ENABLE falling edge is received at the same time as an INP rising edge, the input signal is ignored and the block reset.
(Source code, png, hires.png, pdf)

FILTER - Filter¶
The filter block has two different modes of operation: Difference and Average. They both work by latching the values on the input and performing an operation comparing to the current value.
Fields¶
Name | Type | Description |
---|---|---|
MODE | param enum | Select operation mode
0 difference
1 average
|
ENABLE | bit_mux | Enable event |
TRIG | bit_mux | Trigger event |
INP | pos_mux | Input data |
OUT | pos_out | Output data |
READY | bit_out | Output Ready |
HEALTH | read enum | Error
0 OK
1 Accumulator overflow
2 Divider retrigger
|
Difference¶
The difference operation works by latching the value on the input on the rising edge of the Enable signal. On a rising edge of the trigger signal the output is given as the the current input value minus the latched value.
(Source code, png, hires.png, pdf)

After the operation, the latched value is updated to be the current value on the input.
(Source code, png, hires.png, pdf)

The operation continues to work if the current value is less than the latched value: a negative result is outputted
(Source code, png, hires.png, pdf)

Average¶
The average function appends a sum value on each clock pulse. When a trigger signal is received it divides the summed value by the number of clock pulses that have passed.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

If a calculation is triggered before the calculation is ready, the system will show an error on the HEALTH output and will then need to be re-enabled before another calculation can be sent.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

LUT - 5 Input lookup table¶
An LUT block produces an output that is determined by a user-programmable 5-input logic function, set with the FUNC register.
Fields¶
Name | Type | Description |
---|---|---|
INPA | bit_mux | Input A |
INPB | bit_mux | Input B |
INPC | bit_mux | Input C |
INPD | bit_mux | Input D |
INPE | bit_mux | Input E |
TYPEA | param enum | Source of the value of A for calculation
0 Input-Level
1 Pulse-On-Rising-Edge
2 Pulse-On-Falling-Edge
3 Pulse-On-Either-Edge
|
TYPEB | param enum | Source of the value of B for calculation
0 Input-Level
1 Pulse-On-Rising-Edge
2 Pulse-On-Falling-Edge
3 Pulse-On-Either-Edge
|
TYPEC | param enum | Source of the value of C for calculation
0 Input-Level
1 Pulse-On-Rising-Edge
2 Pulse-On-Falling-Edge
3 Pulse-On-Either-Edge
|
TYPED | param enum | Source of the value of D for calculation
0 Input-Level
1 Pulse-On-Rising-Edge
2 Pulse-On-Falling-Edge
3 Pulse-On-Either-Edge
|
TYPEE | param enum | Source of the value of E for calculation
0 Input-Level
1 Pulse-On-Rising-Edge
2 Pulse-On-Falling-Edge
3 Pulse-On-Either-Edge
|
FUNC | param lut | Input func |
OUT | bit_out | Lookup table output |
Testing Function Output¶
This set of tests sets the function value and checks whether the output is as expected
The value of FUNC is a 32-bit unsigned int representing the truth table output of the 5 inputs. The mapping of the string to an integer is done by the PandABlocks TCP server.
A&B&C&D&E (FUNC= 0x80000000). Setting all inputs to 1 results in an output of 1, and changing any inputs produces an output of 0
(Source code, png, hires.png, pdf)

~A&~B&~C&~D&~E (FUNC= 0x00000001). Setting all inputs to 0 results in an output of 1, and changing any inputs produces an output of 0
(Source code, png, hires.png, pdf)

A (FUNC= 0xffff0000). The output should only be 1 if A is 1 irrespective of any other input.
(Source code, png, hires.png, pdf)

A&B|C&~D (FUNC= 0xff303030)
(Source code, png, hires.png, pdf)

Changing the function in a test¶
If a function is changed, the output will take effect on the next clock tick
(Source code, png, hires.png, pdf)

Edge triggered inputs¶
We can also use the LUT to convert edges into levels by changing A..E to be one clock tick wide pulses based on edges rather than the current level of INPA..INPE.
If we wanted to produce a pulse only if INPA had a rising edge on the same clock tick as INPB had a falling edge we could set FUNC=0xff000000 (A&B) and A=1 (rising edge of INPA) and B=2 (falling edge of INPB):
(Source code, png, hires.png, pdf)

We could also use this for generating pulses on every transition of A:
(Source code, png, hires.png, pdf)

PCOMP - Position Compare¶
The position compare block takes a position input and allows a regular number of threshold comparisons to take place on a position input. The normal order of operations is something like this:
- If PRE_START > 0 then wait until position has passed START - PRE_START
- If START > 0 then wait until position has passed START and set OUT=1
- Wait until position has passed START + WIDTH and set OUT=0
- Wait until position has passed START + STEP and set OUT=1
- Wait until position has passed START + STEP + WIDTH and set OUT=0
- Continue until PULSES have been produced
It can be used to generate a position based pulse train against an input encoder or analogue system, or to work as repeating comparator.
Fields¶
Name | Type | Description |
---|---|---|
PRE_START | param int | INP must be this far from START before waiting for START |
START | param int | Pulse absolute/relative start position value |
WIDTH | param int | The relative distance between a rising and falling edge |
STEP | param int | The relative distance between successive rising edges |
PULSES | param | The number of pulses to produce, 0 means infinite |
RELATIVE | param enum | If 1 then START is relative to the position of INP at enable
0 Absolute
1 Relative
|
DIR | param enum | Direction to apply all relative offsets to
0 Positive
1 Negative
2 Either
|
ENABLE | bit_mux | Stop on falling edge, reset and enable on rising edge |
INP | pos_mux | Position data from position-data bus |
ACTIVE | bit_out | Active output is high while block is in operation |
OUT | bit_out | Output pulse train |
HEALTH | read enum | Error details if anything goes wrong
0 OK
1 Position jumped by more than STEP
2 Can’t guess DIR when RELATIVE and PRE_START=0 and START=0
|
PRODUCED | read | The number of pulses produced |
STATE | read enum | The internal statemachine state
0 WAIT_ENABLE
1 WAIT_DIR
2 WAIT_PRE_START
3 WAIT_RISING
4 WAIT_FALLING
|
Position compare is directional¶
A typical example would setup the parameters, enable the block, then start moving a motor to trigger a series of pulses:
(Source code, png, hires.png, pdf)

But if we get the direction wrong, we won’t get the first pulse until we cross START in the correct direction:
(Source code, png, hires.png, pdf)

Moving in a negative direction works in a similar way. Note that WIDTH and PULSE still have positive values:
(Source code, png, hires.png, pdf)

Internal statemachine¶
The Block has an internal statemachine that is exposed as a parameter, allowing the user to see what the Block is currently doing:
Not generating a pulse more than once¶
A key part of position compare is not generating a pulse at a position more than once. This is to deal with noisy encoders:
(Source code, png, hires.png, pdf)

This means that care is needed if using direction sensing or relying on the directionality of the encoder when passing the start position. For example, if we approach START from the negative direction while doing a positive position compare, then jitter back over the start position, we will generate start at the wrong place. If you look carefully at the statemachine you will see that the Block crossed into WAIT_START when INP < 4 (START), which is too soon for this amount of jitter:
(Source code, png, hires.png, pdf)

We can fix this by adding to the PRE_START deadband which the encoder has to cross in order to advance to the WAIT_START state. Now INP < 2 (START-PRE_START) is used for the condition of crossing into WAIT_START:
(Source code, png, hires.png, pdf)

Guessing the direction¶
We can also ask to the Block to calculate direction for us:
(Source code, png, hires.png, pdf)

This is a one time calculation of direction at the start of operation, once the encoder has been moved enough to guess the direction then it is fixed until the Block has finished producing pulses:
(Source code, png, hires.png, pdf)

Interrupting a scan¶
When the ENABLE input is set low the output will cease. This will happen even if the ENABLE is set low when there are still cycles of the output pulse to generate, or if the ENABLE = 0 is set at the same time as a position match.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Position compare on absolute values¶
Doing position compare on an absolute value adds additional challenges, as we are not guaranteed to see every transition. It works in much the same way as the previous examples, but we trigger on greater than or equal rather than just greater than:
(Source code, png, hires.png, pdf)

But what should the Block do if the output is 0 and the position jumps by enough to trigger a transition to 1 and then back to 0? We handle this by setting HEALTH=”Error: Position jumped by more than STEP” and aborting the compare:
(Source code, png, hires.png, pdf)

Likewise if the output is 1 and the position causes us to need to produce a 0 then 1:
(Source code, png, hires.png, pdf)

And if we skipped a larger number of points we get the same error:
(Source code, png, hires.png, pdf)

Relative position compare¶
We may want to nest position compare blocks, or respond to some external event. In which case, we expose the option to a position compare relative to the latched position at the start:
(Source code, png, hires.png, pdf)

We can also guess the direction in relative mode:
(Source code, png, hires.png, pdf)

This works when going negative too:
(Source code, png, hires.png, pdf)

And with a PRE_START value we guess the direction to be the opposite to the direction the motor is travelling when it exceeds PRE_START:
(Source code, png, hires.png, pdf)

We cannot guess the direction when RELATIVE mode is set with no START or PRE_START though, the Block will error in this case:
(Source code, png, hires.png, pdf)

Use as a Schmitt trigger¶
We can also make use of a special case with STEP=0 and a negative WIDTH to create a Schmitt trigger that will always trigger at START, and turn off when INP has dipped WIDTH below START:
(Source code, png, hires.png, pdf)

We can use this same special case with a positive width to make a similar comparator that turns on at START and off at START+WIDTH, triggering again when INP <= START:
(Source code, png, hires.png, pdf)

PGEN - Position Generator¶
The position generator block produces an output position which is pre-defined in a table
Fields¶
Name | Type | Description |
---|---|---|
CYCLES | param | Number of cycles |
ENABLE | bit_mux | Halt on falling edge, reset and enable on rising |
TRIG | bit_mux | Trigger a sample to be produced |
OUT | pos_out | Current sample |
TABLE | table | Table of positions to be output |
HEALTH | read enum | Table status
0 OK
1 Table not ready
2 Table end reached
3 DMA overrun
|
Normal operation¶
The output pulse will be generated regardless of the direction of the INP data
(Source code, png, hires.png, pdf)

T1 |
---|
POS |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
111 |
152 |
132 |
POSENC - Quadrature and step/direction encoder¶
The POSENC block handles the Quadrature and step/direction encoding
Fields¶
Name | Type | Description |
---|---|---|
INP | pos_mux | Output position |
PERIOD | param time | Minimum time between Quadrature transitions of step pulses |
ENABLE | bit_mux | Halt on falling edge, reset and enable on rising |
PROTOCOL | param enum | Quadrature or step/direction
0 Quadrature
1 Step/Direction
|
A | bit_out | Quadrature A/Step output |
B | bit_out | Quadrature B/Direction output |
STATE | read enum | State of quadrature output
0 Disabled
1 At position
2 Slewing
|
Quadrature¶
When in the quadrature mode, the module will output signals A and B in different states as it counts up or down. When counting up B will follow A and when counting down A will follow B. The period is the time between an edge on one signal to the next edge of the other signal.
The input is initially set as the value of the INP line when ENABLE goes high. The system will then count to the current value on the INP line, and when it reaches this value the output signals will stay as they are.
The state output is ‘0’ while ENABLE is low, ‘1’ when the count is equal to the signal on the INP line and ‘2’ while it is counting towards the INP value.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Step/Direction¶
In the Step/Direction mode the A output becomes a step output. This goes high on every period for one clock cycle and is low for the remainder of the period. The B output becomes the direction output, it is ‘0’ when the internal counter is lower than the inputted target value (it is counting up), and ‘1’ when it is greater or equal to.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

PULSE - One-shot pulse delay and stretch¶
A PULSE block produces configurable width output pulses with an optional delay based on its parameters. If WIDTH is non-zero, the output pulse width will be the specified amount. If DELAY is non-zero, the pulse train will be delayed by that amount. If both are non-zero, the pulses are stretched and delayed as long as the resulting output would still contain the same number of distinct pulses. If this is not the case, then the PERR signal is raised, and the MISSED_CNT counter is incremented. Change of any parameter causes the block to be reset.
Fields¶
Name | Type | Description |
---|---|---|
DELAY | time | Output pulse delay (0 for no delay) |
WIDTH | time | Output pulse width (0 for input pulse width) |
ENABLE | bit_mux | Reset on falling edge, enable on rising |
TRIG | bit_mux | Input pulse train |
OUT | bit_out | Output pulse train |
QUEUED | read uint 1023 | Length of the delay queue |
DROPPED | read | Number of pulses not produced because of an ERR condition |
TRIG_EDGE | param enum | INP trigger edge
0 Rising
1 Falling
2 Either
|
Zero Delay¶
If DELAY=0, then the INP pulse will be stretched with only the propagation delay of the block (1 clock tick). WIDTH must be at least 4, and any value given below is defaulted to four.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Zero Width¶
If WIDTH=0, then the INP pulse width will be used. DELAY must be >3 clock ticks, any lower inputted values will be defaulted to four.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Width and Delay¶
In this mode, pulses are placed onto an output queue, so a number of restrictions apply:
- There must not be more than 1023 pulses on the output queue
- WIDTH must be >3 clock ticks
- There must be >3 clock ticks where output is 0 between pulses. This means that WIDTH < T - 3 where T is the minimum INP pulse period
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Different Edge Activation¶
When there is a width specified, it is possible to also specify which edge of the input pulse activates the output.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Pulse period error¶
The following example shows what happens when the period between pulses is too short.
(Source code, png, hires.png, pdf)

QDEC - Quadrature Decoder¶
The QDEC block handles the encoder Decoding
Fields¶
Name | Type | Description |
---|---|---|
RST_ON_Z | param bit | Zero position on Z rising edge |
SETP | write int | Set point |
A | bit_mux | Quadrature A |
B | bit_mux | Quadrature B |
Z | bit_mux | Z index channel |
OUT | pos_out | Output position |
Counting¶
The quadrature decoder counts, incrementing at each rising or falling edge of the sequence. If the sequence is reversed the count will decrease at each edge. The initial value is set to the value of the SETP input.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Resetting¶
Whilst counting, it can be reset to ‘0’ on while the Z input is high, provided that this functionality is enabled by setting the RST_ON_Z input to ‘1’. If the SETP input is changed the count value changes to the new value.
(Source code, png, hires.png, pdf)

Limitations¶
The block can continue to count when there is not a constant period between the pulses.
(Source code, png, hires.png, pdf)

The output takes three clock pulses to update. If the inputs are changing faster than this, inputs can be lost.
(Source code, png, hires.png, pdf)

SEQ - Sequencer¶
The sequencer block performs automatic execution of sequenced lines to produce timing signals. Each line optionally waits for an external trigger condition and runs for an optional phase1, then a mandatory phase2 before moving to the next line. Each line sets the block outputs during phase1 and phase2 as defined by user-configured mask. Individual lines can be repeated, and the whole table can be repeated, with a value of 0 meaning repeat forever.
Fields¶
Name | Type | Description |
---|---|---|
TABLE | table short | Sequencer table of lines
TIME2 The time the mandatory phase 2 should take
REPEATS Number of times the line will repeat
TRIGGER The trigger condition to start the phases
OUTA1 Output A value during phase 1
OUTB1 Output B value during phase 1
OUTC1 Output C value during phase 1
OUTD1 Output D value during phase 1
OUTE1 Output E value during phase 1
OUTF1 Output F value during phase 1
OUTA2 Output A value during phase 2
OUTB2 Output B value during phase 2
OUTC2 Output C value during phase 2
OUTD2 Output D value during phase 2
OUTE2 Output E value during phase 2
OUTF2 Output F value during phase 2
POSITION The position that can be used in trigger condition
TIME1 The time the optional phase 1 should take
127:96 TIME2
15:0 REPEATS
19:16 TRIGGER enum
0 Immediate
1 BITA=0
2 BITA=1
3 BITB=0
4 BITB=1
5 BITC=0
6 BITC=1
7 POSA>=POSITION
8 POSA<=POSITION
9 POSB>=POSITION
10 POSB<=POSITION
11 POSC>=POSITION
12 POSC<=POSITION
20:20 OUTA1
21:21 OUTB1
22:22 OUTC1
23:23 OUTD1
24:24 OUTE1
25:25 OUTF1
26:26 OUTA2
27:27 OUTB2
28:28 OUTC2
29:29 OUTD2
30:30 OUTE2
31:31 OUTF2
63:32 POSITION int
95:64 TIME1
|
PRESCALE | param time | Prescalar for sequencer table times |
REPEATS | param | Number of times the table will repeat |
ENABLE | bit_mux | Stop on falling edge, reset and enable on rising edge |
BITA | bit_mux | BITA for optional trigger condition |
BITB | bit_mux | BITB for optional trigger condition |
BITC | bit_mux | BITC for optional trigger condition |
POSA | pos_mux | POSA for optional trigger condition |
POSB | pos_mux | POSB for optional trigger condition |
POSC | pos_mux | POSC for optional trigger condition |
ACTIVE | bit_out | Sequencer active flag |
OUTA | bit_out | Output A for phase outputs |
OUTB | bit_out | Output B for phase outputs |
OUTC | bit_out | Output C for phase outputs |
OUTD | bit_out | Output D for phase outputs |
OUTE | bit_out | Output E for phase outputs |
OUTF | bit_out | Output F for phase outputs |
TABLE_REPEAT | read | Current iteration through the entire table |
TABLE_LINE | read | Current line in the table that is active |
LINE_REPEAT | read | Current iteration of the active table line |
STATE | read enum | Internal statemachine state
0 WAIT_ENABLE
1 LOAD_TABLE
2 WAIT_TRIGGER
3 PHASE1
4 PHASE2
|
Sequencer Table Line Composition¶
Bit Field | Name | Description |
---|---|---|
[15:0] | REPEATS | Number of times the line will repeat |
[19:16] | TRIGGER | The trigger condition to start the phases
0: Immediate
1: BITA=0
2: BITA=1
3: BITB=0
4: BITB=1
5: BITC=0
6: BITC=1
7: POSA>=POSITION
8: POSA<=POSITION
9: POSB>=POSITION
10: POSB<=POSITION
11: POSC>=POSITION
12: POSC<=POSITION
|
[63:32] | POSITION | The position that can be used in trigger condition |
[95:64] | TIME1 | The time the optional phase 1 should take |
[20:20] | OUTA1 | Output A value during phase 1 |
[21:21] | OUTB1 | Output B value during phase 1 |
[22:22] | OUTC1 | Output C value during phase 1 |
[23:23] | OUTD1 | Output D value during phase 1 |
[24:24] | OUTE1 | Output E value during phase 1 |
[25:25] | OUTF1 | Output F value during phase 1 |
[127:96] | TIME2 | The time the mandatory phase 2 should take |
[26:26] | OUTA2 | Output A value during phase 2 |
[27:27] | OUTB2 | Output B value during phase 2 |
[28:28] | OUTC2 | Output C value during phase 2 |
[29:29] | OUTD2 | Output D value during phase 2 |
[30:30] | OUTE2 | Output E value during phase 2 |
[31:31] | OUTF2 | Output F value during phase 2 |
Generating fixed pulse trains¶
The basic use case is for generating fixed pulse trains when enabled. For example we can ask for 3x 50% duty cycle pulses by writing a single line table that is repeated 3 times. When enabled it will become active and immediately start producing pulses, remaining active until the pulses have been produced:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
3 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
We can also use it to generate irregular streams of pulses on different outputs by adding more lines to the table. Note that OUTB which was high at the end of Phase2 of the first line remains high in Phase1 of the second line:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
2 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 1 | 0 | 0 | 0 | 0 |
3 | Immediate | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
And we can set repeats on the entire table too. Note that in the second line of this table we have suppressed phase1 by setting its time to 0:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
2 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | Immediate | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 0 | 1 | 0 | 0 | 0 | 0 |
There are 6 outputs which allow for complex patterns to be generated:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | Immediate | 0 | 3 | 1 | 0 | 0 | 0 | 0 | 0 | 4 | 1 | 1 | 0 | 0 | 0 | 0 |
1 | Immediate | 0 | 5 | 1 | 1 | 1 | 0 | 0 | 0 | 6 | 1 | 1 | 1 | 1 | 0 | 0 |
1 | Immediate | 0 | 7 | 1 | 1 | 1 | 1 | 1 | 0 | 8 | 1 | 1 | 1 | 1 | 1 | 1 |
Statemachine¶
There is an internal statemachine that controls which phase is currently being output. It has a number of transitions that allow it to skip PHASE1 if there is none, or skip WAIT_TRIGGER if there is no trigger condition.
External trigger sources¶
The trigger column in the table allows an optional trigger condition to be waited on before the phased times are started. The trigger condition is checked on each repeat of the line, but not checked during phase1 and phase2. You can see when the Block is waiting for a trigger signal as it will enter the WAIT_TRIGGER(2) state:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
3 | BITA=1 | 0 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | BITB=1 | 0 | 3 | 0 | 1 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
You can also use a position field as a trigger condition in the same way, this is useful to do a table based position compare:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | POSA>=POSITION | 20 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 1 | 0 | 0 | 0 | 0 |
3 | Immediate | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 3 | 0 | 1 | 0 | 0 | 0 | 0 |
2 | POSA<=POSITION | 10 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
Prescaler¶
Each row of the table gives a time value for the phases. This value can be scaled with a block wide prescaler to allow a frame to be longer than 2**32 * 8e-9 = about 34 seconds. For example:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
2 | Immediate | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Interrupting a sequence¶
Setting the repeats on a table row to 0 will cause it to iterate until interrupted by a falling ENABLE signal:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
0 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
In a similar way, REPEATS=0 on a table will cause the whole table to be iterated until interrupted by a falling ENABLE signal:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | Immediate | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 |
2 | Immediate | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
And a rising edge of the ENABLE will re-run the same table from the start:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
Table rewriting¶
If a table is written while enabled, the outputs and table state are reset and operation begins again from the first repeat of the first line of the table:
(Source code, png, hires.png, pdf)

T1 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | Immediate | 0 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
T2 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | Trigger | Phase1 | Phase1 Outputs | Phase2 | Phase2 Outputs | |||||||||||
Repeats | Condition | Position | Time | A | B | C | D | E | F | Time | A | B | C | D | E | F |
1 | Immediate | 0 | 8 | 1 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
SRGATE - Set Reset Gate¶
An SRGATE block produces either a high (SET) or low (RST) output. It has configurable inputs and an option to force its output independently. Both Set and Rst inputs can be selected from bit bus, and the active-edge of its inputs is configurable. An enable signal allows the block to ignore its inputs.
Fields¶
Name | Type | Description |
---|---|---|
WHEN_DISABLED | param enum | What to do with the output when Enable is low
0 Set output low
1 Set output high
2 Keep current output
|
SET_EDGE | param enum | Output set edge
0 Rising
1 Falling
2 Either
|
RST_EDGE | param enum | Output reset edge
0 Rising
1 Falling
2 Either
|
FORCE_SET | write action | Set output to 1 |
FORCE_RST | write action | Reset output to 0 |
ENABLE | bit_mux | Whether to listen to SET/RST events |
SET | bit_mux | A falling/rising edge sets the output to 1 |
RST | bit_mux | a falling/rising edge resets the output to 0 |
OUT | bit_out | output value |
Normal conditions¶
The normal behaviour is to set the output OUT on the configured edge of the SET or RESET input.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Disabling the block¶
The default behaviour is to force the block output low when disabled, ignoring any SET/RST events:
(Source code, png, hires.png, pdf)

The disabled value can also be set high:
(Source code, png, hires.png, pdf)

Or left at its current value:
(Source code, png, hires.png, pdf)

Active edge configure conditions¶
if the active edge is ‘rising’ then reset to ‘falling’ at the same time as a rising edge on the SET input, the block will ignore the rising edge and set the output OUT on the falling edge of the SET input.
(Source code, png, hires.png, pdf)

If the active edge changes to ‘falling’ at the same time as a falling edge on the SET input, the output OUT will be set following this.
(Source code, png, hires.png, pdf)

(Source code, png, hires.png, pdf)

Set-reset conditions¶
When determining the output if two values are set simultaneously, FORCE_SET and FORCE_RESET registers take priority over the input bus, and reset takes priority over set.
(Source code, png, hires.png, pdf)

INENC - Input encoder¶
The INENC block handles the encoder input signals
Fields¶
Name | Type | Description |
---|---|---|
PROTOCOL | param enum | Type of absolute/incremental protocol
0 Quadrature
1 SSI
2 BISS
3 enDat
|
CLK_SRC | param enum | Bypass/Pass Through encoder signals
0 Internally Generated
1 From CLK
|
CLK_PERIOD | param time | Clock rate |
FRAME_PERIOD | param time | Frame rate |
BITS | param uint 63 | Number of bits |
LSB_DISCARD | param uint 31 | Number of LSB bits to discard |
MSB_DISCARD | param uint 31 | Number of MSB bits to discard |
SETP | write int | Set point |
RST_ON_Z | param bit | Zero position on Z rising edge |
DCARD_TYPE | read enum | Daughter card jumper mode
0 DCARD id 0
1 Encoder Control
2 DCARD id 2
3 Encoder Monitor
4 DCARD id 3
5 DCARD id 4
6 DCARD id 5
7 Unplugged
|
A | bit_out | Quadrature A if in incremental mode |
B | bit_out | Quadrature B if in incremental mode |
Z | bit_out | Z index channel if in incremental mode |
DATA | bit_out | Data input from slave encoder |
CLK | bit_mux | Clock output to slave encoder |
CONN | bit_out | Signal detected |
VAL | pos_out | Current position |
LVDSIN - LVDS Input¶
The LVDSIN block handles the signals from the LVDS Input connectors
Fields¶
Name | Type | Description |
---|---|---|
VAL | bit_out | LVDS input value |
LVDSOUT - LVDS Output¶
The LVDSOUT block handles the signals to the LVDS Output connectors
Fields¶
Name | Type | Description |
---|---|---|
VAL | bit_mux | LVDS output value |
OUTENC - Output encoder¶
The OUTENC block handles the encoder output signals
Fields¶
Name | Type | Description |
---|---|---|
ENABLE | bit_mux | Halt of falling edge, reset and enable on rising |
PROTOCOL | param enum | Type of absolute/incremental protocol
0 Quadrature
1 SSI
2 BISS
3 enDat
4 ABZ Passthrough
5 DATA Passthrough
|
BITS | param uint 32 | Number of bits |
QPERIOD | param time | Quadrature prescaler |
A | bit_mux | Input for A (only straight through) |
B | bit_mux | Input for B (only straight through) |
Z | bit_mux | Input for Z (only straight through) |
DATA | bit_mux | Data output to master encoder |
CLK | bit_out | Clock input from master encoder |
VAL | pos_mux | Input for position (all other protocols) |
CONN | bit_mux | Input for connected |
DCARD_TYPE | read enum | Daughter card jumper mode
0 DCARD id 0
1 Encoder Control
2 DCARD id 2
3 Encoder Monitor
4 DCARD id 3
5 DCARD id 4
6 DCARD id 5
7 Unplugged
|
QSTATE | read enum | Quadrature state
0 Disabled
1 At position
2 Slewing
|
PCAP - Position Capture¶
Position capture has the capability to capture anything that is happening on the pos_bus or bit_bus. It listens to ENABLE, GATE and CAPTURE signals, and can capture the value at capture, sum, min and max.
Fields¶
Name | Type | Description |
---|---|---|
ENABLE | bit_mux | After arm, when high start capture, when low disarm |
GATE | bit_mux | After enable, only process gated values if high |
TRIG | bit_mux | On selected edge capture current value and gated data |
TRIG_EDGE | param enum | Which edge of capture input signal triggers capture
0 Rising
1 Falling
2 Either
|
SHIFT_SUM | param uint 8 | Shift sum/samples data, use if > 2**32 samples required in sum/average |
HEALTH | read enum | Was last capture successful?
0 OK
1 Capture events too close together
2 Samples overflow
|
ACTIVE | bit_out | Data capture in progress |
TS_START | ext_out timestamp | Timestamp of first gate high in current capture relative to enable |
TS_END | ext_out timestamp | Timestamp of last gate high +1 in current capture relative to enable |
TS_TRIG | ext_out timestamp | Timestamp of capture event relative to enable |
SAMPLES | ext_out samples | Number of gated samples in the current capture |
BITS0 | ext_out bits 0 | Quadrant 0 of bit_bus |
BITS1 | ext_out bits 1 | Quadrant 1 of bit_bus |
BITS2 | ext_out bits 2 | Quadrant 2 of bit_bus |
BITS3 | ext_out bits 3 | Quadrant 3 of bit_bus |
Arming¶
To start off the block an arm signal is required with a write to *PCAP.ARM=
.
The active signal is raised immediately on ARM, and dropped either on
*PCAP.DISARM
:
(Source code, png, hires.png, pdf)

Or on the falling edge of ENABLE:
(Source code, png, hires.png, pdf)

Capturing fields¶
Capturing fields is done by specifying a series of WRITE addresses. These are made up of a mode in the bottom 4 bits, and an index in the 6 bits above them. Indexes < 32 refer to entries on the pos_bus, while indexes >= 32 are extra entries specific to PCAP, like timestamps and number of gated samples. The values sent via the WRITE register are written from the TCP server, so will not be visible to end users.
Data is ticked out one at a time from the DATA attribute, then sent to the TCP server over DMA, before being sent to the user. It is reconstructed into a table in each of the examples below for ease of reading.
The following example shows PCAP being configured to capture the timestamp when CAPTURE goes high (0x24 is the bottom 32-bits of TS_CAPTURE).
(Source code, png, hires.png, pdf)

Row | 0x240 |
---|---|
0 | 2 |
1 | 6 |
Pos bus capture¶
As well as general fields like the timestamp, any pos_bus index can be captured. Pos bus fields have multiple modes that they can capture in.
Mode 0 - Value¶
This gives an instantaneous capture of value no matter what the state of GATE:
(Source code, png, hires.png, pdf)

Row | 0x50 |
---|---|
0 | 20 |
1 | 100 |
2 | 6 |
Mode 1 - Difference¶
This is mainly used for something like an incrementing counter value. It will only count the differences while GATE was high:
(Source code, png, hires.png, pdf)

Row | 0xB1 |
---|---|
0 | 10 |
1 | -5 |
Mode 2/3 - Sum Lo/Hi¶
Mode 2 is the lower 32-bits of the sum of all samples while GATE was high:
(Source code, png, hires.png, pdf)

Row | 0x32 |
---|---|
0 | 6 |
1 | 21 |
2 | 206 |
Mode 2 and 3 together gives the full 64-bits of sum, needed for any sizeable values on the pos_bus:
(Source code, png, hires.png, pdf)

Row | 0x22 | 0x23 |
---|---|---|
0 | 1073741824 | 0 |
1 | -1073741824 | 0 |
2 | -2147483648 | 2 |
3 | -1073741824 | -1 |
4 | -1073741824 | -2 |
If long frame times (> 2**32 SAMPLES, > 30s), are to be used, then SHIFT_SUM can be used to shift both the sum and SAMPLES field by up to 8-bits to accomodate up to 125 hour frames. This example demonstrates the effect with smaller numbers:
(Source code, png, hires.png, pdf)

Row | 0x92 | 0x260 |
---|---|---|
0 | 40 | 1 |
1 | 36 | 1 |
2 | -13 | 1 |
3 | 0 | 0 |
Mode 4/5 - Min/Max¶
Both of these modes calculate statistics on the value while GATE is high.
Mode 4 produces the min of all values or zero if the gate was low for all of the current capture:
(Source code, png, hires.png, pdf)

Row | 0x84 |
---|---|
0 | 10 |
1 | 20 |
2 | 21 |
3 | 2147483647 |
Mode 5 produces the max of all values in a similar way:
(Source code, png, hires.png, pdf)

Row | 0x45 |
---|---|
0 | 20 |
1 | 20 |
2 | 22 |
3 | -2147483648 |
Number of samples¶
There is a SAMPLES field that can be captured that will give the number of clock ticks that GATE was high during a single CAPTURE. This field allows the TCP server to offer “Mean” as a capture option, dividing “Sum” by SAMPLES to get the mean value of the field during the capture period. It can also be captured separately to give the gate length:
(Source code, png, hires.png, pdf)

Row | 0x260 |
---|---|
0 | 4 |
1 | 3 |
2 | 2 |
3 | 0 |
Timestamps¶
As well as the timestamp of the capture signal, timestamps can also be generated for the start of each capture period (first gate high signal) and end (the tick after the last gate high). These are again split into two 32-bit segments so only the lower bits need to be captured for short captures. In the following example we capture TS_START (0x20), TS_END (0x22) and TS_CAPTURE (0x24) lower bits:
(Source code, png, hires.png, pdf)

Row | 0x200 | 0x220 | 0x240 |
---|---|---|---|
0 | 0 | 4 | 4 |
1 | 4 | 8 | 9 |
2 | 11 | 13 | 13 |
3 | -1 | -1 | 16 |
Bit bus capture¶
The state of the bit bus at capture can also be captured. It is split into 4 quadrants of 32-bits each. For example, to capture signals 0..31 on the bit bus we would use BITS0 (0x27):
(Source code, png, hires.png, pdf)

Row | 0x270 |
---|---|
0 | 0 |
1 | 4 |
2 | 20 |
3 | 16 |
By capturing all 4 quadrants (0x27..0x2A) we get the whole bit bus:
(Source code, png, hires.png, pdf)

Row | 0x270 | 0x280 | 0x290 | 0x2A0 |
---|---|---|---|---|
0 | 4 | 0 | 0 | 0 |
1 | 4 | 67108864 | 0 | 0 |
2 | 4 | 67108864 | 0 | 32 |
3 | 1028 | 67108864 | 0 | 32 |
Triggering options¶
ENABLE and GATE are level triggered, with ENABLE used for marking the start and end of the entire acquisition, and GATE used to accept or reject samples within a single capture from the acquisition. CAPTURE is edge triggered with an option to trigger on rising, falling or both edges.
Triggering on rising is the default, explored in the preceding examples. Triggering on falling edge would be used if you have a gate signal that marks the capture boundaries and want sum or difference data within. For example, to capture the amount POS[1] changes in each capture gate we could connect GATE and CAPTURE to the same signal:
(Source code, png, hires.png, pdf)

Row | 0x11 |
---|---|
0 | 10 |
1 | -9 |
Another option would be a gap-less acquisition of sum while gate is high with capture boundaries marked with a toggle of CAPTURE:
(Source code, png, hires.png, pdf)

Row | 0x12 |
---|---|
0 | 30 |
1 | 178 |
2 | 39 |
Error conditions¶
The distance between capture signals must be at least the number of 32-bit capture fields. If 2 capture signals are too close together HEALTH will be set to 1 (Capture events too close together).
In this example there are 3 fields captured (TS_CAPTURE_L, TS_CAPTURE_H, SAMPLES), but only 2 clock ticks between the 2nd and 3rd capture signals:
(Source code, png, hires.png, pdf)

Row | 0x240 | 0x250 | 0x260 |
---|---|---|---|
0 | 1 | 0 | 0 |
1 | 5 | 0 | 0 |
SYSTEM - System control FPGA¶
Fields¶
Name | Type | Description |
---|---|---|
TEMP_PSU | read int | On-board temperature [Power Supply] |
TEMP_SFP | read int | On-board temperature [SFP] |
TEMP_ENC_L | read int | On-board temperature [Left Encoder] |
TEMP_PICO | read int | On-board temperature [Picozed] |
TEMP_ENC_R | read int | On-board temperature [Right Encoder] |
TEMP_ZYNQ | read scalar 0.001 0 deg | On-board zynq temperature |
ALIM_12V0 | read scalar 0.001486252 | On-board voltage sensor values |
PICO_5V0 | read scalar 0.000611546 | On-board voltage sensor values |
IO_5V0 | read scalar 0.000609385 | On-board voltage sensor values |
SFP_3V3 | read scalar 0.000384078 | On-board voltage sensor values |
FMC_15VN | read scalar 0.003533569 | On-board voltage sensor values |
FMC_15VP | read scalar 0.001847291 | On-board voltage sensor values |
ENC_24V | read scalar 0.002854764 | On-board voltage sensor values |
FMC_12V | read scalar 0.001494582 | On-board voltage sensor values |
VCCINT | read scalar 0.001 0 deg | On-board voltage sensor |
Contributing¶
Contributions and issues are most welcome! All issues and pull requests are handled through github on the PandABlocks repository. Also, please check for any existing issues before filing a new one. If you have a great idea but it involves big changes, please file a ticket before making a pull request! We want to make sure you don’t spend your time coding something that might not fit the scope of the project.
Running the tests¶
To get the source source code and run the unit tests, run:
$ git clone git://github.com/PandABlocks/PandABlocks-FPGA.git
$ cd PandABlocks-FPGA
$ virtualenv venv
$ source venv/bin/activate
$ pip install --upgrade pip
$ pip install -r tests/requirements.txt
$ cp CONFIG.example CONFIG
$ make test_python
$ make sim_timing
Writing VHDL¶
Code styling here…
Writing Python¶
Please arrange imports with the following style
# Standard library imports
import os
# Third party package imports
from mock import patch
# Local package imports
from common.python.configs import BlockConfig
Please follow Google’s python style guide wherever possible.
Documentation¶
There are some conventions:
- First usage of a term in a page should link to an entry in the Glossary
- Glossary entries should define a reference with a trailing underscore
You can build the docs When in the project directory:
$ source venv/bin/activate
$ pip install -r docs/requirements.txt
$ make docs
$ firefox docs/index.html
Release Checklist¶
Before a new release, please go through the following checklist:
- Add a release note in CHANGELOG.rst
- Git tag the version
Assembling Blocks into an App¶
A collections of Block instances that can be loaded to a PandABlocks Device is called an App. This section details how to create and build a new App.
App ini file¶
An ini file is used to specify the Blocks that make up an App. It lives in the
apps/
directory and has the extension .app.ini
. It consists of a top
level section with information about the App, then a section for every Block
in the App.
The [.] section¶
The first section contains app wide information. It looks like this:
[.]
description: Short description of what this app will do
target: device_type
The description
value is a human readable description of what the app
contains and why it should be used.
The target
value must correspond to a directory name in targets/
that
will be used to wrap the blocks in a top level entity that is loadable on the
given PandABlocks device.
[BLOCK] sections¶
All other sections specify Block instance information. They look like this:
[MYBLOCK]
number: 4
module: mymodule
ini: myblock.block.ini
The section name is used to determine the name of the Block in the resulting App. It should be made of upper case letters and underscores with no numbers.
The number
value gives the number of blocks that will be instantiated in the
App. If not specified it will default to 1.
The module
value gives the directory in modules/
that the
Block ini file lives in. If not specified it is the lowercase
version of the section name.
The ini
value gives the Block ini filename relative to the
module directory. If not specified it is the lowercase version of the section
name + .block.ini
Writing a Block¶
If you have checked the list of Available Blocks and need a feature that is not there you can extend an existing Block or create a new one. If the feature fits with the behaviour of an existing Block and can be added without breaking backwards compatibility it is preferable to add it there. If there is a new type of behaviour it may be better to make a new one.
This page lists all of the framework features that are involved in making a Block, finding a Module for it, defining the interface, writing the simulation, writing the timing tests, documenting the behaviour, and finally writing the logic.
Modules¶
Modules are subdirectories in modules/
that contain Block definitions. If
you are writing a soft Block then you will typically create a new Module for it.
If you are writing a Block with hardware connections it will live in a Module
for that hardware (e.g. for the FMC card, or for that Target Platform).
To create a new module, simply create a new directory in modules/
Block ini¶
The first thing that should be defined when creating a new Block is the
interface to the rest of the framework. This consists of an ini file that
contains all the information that the framework needs to integrate some VHDL
logic into the system. It lives in the Module directory and has the extension
.block.ini
. It consists of a top level section with information
about the Block, then a section for every Field in the Block.
The [.] section¶
The first entry to the ini file describes the block as a whole. It looks like this:
[.]
description: Short description of the Block
entity: vhdl_entity
type: dma or sfp or fmc
constraints:
ip:
otherconst:
extension:
The description
should be a short (a few words) description that will be
visible as a Block label to users of the PandABlocks Device when it runs.
The entity
should be the name of the VHDL entity that will be created to
hold the logic. It is typically the lowercase version of the Block name.
The type
field will identify if the block is an SFP, FMC or DMA. These are
special cases and need to be handled differently. This field is automatically
set to soft for soft blocks or carrier for carrier blocks.
The constraints
is used to identify the location of any xdc constraints
files, relative to the module’s directory.
The ip
field holds the name of any ip blocks used in the module’s vhdl code.
otherconst
is used to locate a tcl script if the block needs any further
configuration.
If the extension
field is present then the extensions
directory in the
module must exist and contain a python server extension file.
[FIELD] sections¶
All other sections specify the Field that will be present in the Block. They look like this:
[MYFIELD]
type: type subtype options
description: Short description of the Field
extension: extension-parameter
extension_reg:
wstb:
The section name is used to determine the name of the Field in the resulting Block. It should be made of upper case letters, numbers and underscores.
The type
value gives information about the type which specifies the
purpose and connections of the Field to the system. It is passed straight
through to the field specific line in the config file for the TCP server so
should be written according to type documentation. Subsequent indented lines
in the config file are supplied according to the type
value and are
documented in Extra Field Keys.
The description
value gives a short (single sentence) description about
what the Field does, visible as a tooltip to users.
If extension
is specified then this field is configured as an extension
field. If the extension_reg
field is also specified then this field is also
a hardware register.
If a signal uses a write strobe wstb
should be set to True.
Extra Field Keys¶
Some field types accept extra numeric keys in the Field section to allow extra information to be passed to the TCP server via its config file.
Enum fields would contain numeric keys to translate specific numbers into user readable strings. Strings should be lowercase letters and numbers with underscores and no spaces. A typical field might look like this:
[ENUM_FIELD]
type: param enum # or read enum or write enum
description: Short description of the Field
0: first_value
1: next_value
2: another_value
8: gappy_value
Tables will be defined here too
Block Simulation¶
The Block simulation framework allows the behaviour to be specified in Python and timing tests to be written against it without writing any VHDL. This is beneficial as it allows the behaviour of the Block to be tied down and documented while the logic is relatively easy to change. It also gives an accurate simulation of the Block that can be used to simulate an entire PandABlocks Device.
The first step in making a Block Simulation is to define the imports:
from common.python.simulations import BlockSimulation, properties_from_ini, \
TYPE_CHECKING
if TYPE_CHECKING:
from typing import Dict
The typing
imports allow IDEs like PyCharm to infer the types of the
variables, increasing the chance of finding bugs at edit time.
The BlockSimulation is a baseclass that our simulation should inherit from:
-
class
common.python.simulations.
BlockSimulation
[source]¶ -
changes
= None¶ This will be dictionary with changes pushed by any properties created with properties_from_ini()
-
Next we read the block ini file:
NAMES, PROPERTIES = properties_from_ini(__file__, "myblock.block.ini")
This generates two objects:
NAMES
: Acollections.namedtuple
with a string attribute for every field, for comparing field names with.PROPERTIES
: Aproperty
for each Field of the Block that can be attached to theBlockSimulation
class
Now we are ready to create our simulation class:
class MyBlockSimulation(BlockSimulation):
INP, ANOTHER_FIELD, OUT = PROPERTIES
def on_changes(self, ts, changes):
"""Handle field changes at a particular timestamp
Args:
ts (int): The timestamp the changes occurred at
changes (Dict[str, int]): Fields that changed with their value
Returns:
If the Block needs to be called back at a particular ts then return
that int, otherwise return None and it will be called when a field
next changes
"""
# Set attributes
super(MyBlockSimulation, self).on_changes(ts, changes)
if NAMES.INP in changes:
# If our input changed then set our output high
self.OUT = 1
# Need to be called back next clock tick to set it back
return ts + 1
else:
# The next clock tick set it back low
self.OUT = 0
This is a very simple Block, when INP
changes, it outputs a 1 clock tick
pulse on OUT
. It checks the changes dict to see if INP
is in it, and
if it is then sets OUT
to 1. The framework only calls on_changes()
when there are changes unless informed when the Block needs to be called next.
In this case we need to be called back the next clock tick to set OUT
back
to zero, so we do this by returning ts + 1
. When we are called back next
clock tick then there is nothing in the changes dict, so OUT
is set back
to 0 and return None so the framework won’t call us back until something
changes.
Note
If you need to use a field name in code, use an attribute of NAMES
. This
avoids mistakes due to typos like:
if "INPP" in changes:
code_that_will_never_execute
While if we use NAMES
:
if NAMES.INPP in changes: # Fails with AttributeError
Timing ini¶
The purpose of the .timing.ini file is to provide expected data for comparison in the testing of the modules. Data should be calculated as to how and when the module will behave with a range of inputs.
The [.] section¶
The first entry to the ini file describes the timing tests as a whole. It looks like this:
[.]
description: Timing tests for Block
scope: block.ini file
[TEST] sections¶
The other sections will display the tests inputs and outputs. It looks like this:
[NAME_OF_TEST]
1: inputA=1, inputB=2 -> output=3
5: inputC=4 -> output=7
6: inputD=-10 -> output=0, Error=1
The numbers at the left indicate the timestamp at which a change occurs, followed by a colon. Any assignments before the -> symbol indicate a change in an input and assignments after the -> symbol indicate a change in an output.
Target ini¶
A target.ini is written for the blocks which are specific to the target. This ini file declares the blocks and their number similar to the app.ini file.
The [.] section¶
The first entry to the ini file defines information for the SFP sites for the target:
[.]
sfp_sites:
sfp_constraints:
The sfp_sites
type is the number of available SFP sites on the target, and
the sfp_sites
type is the name of the constraints file for each SFP site,
located in the target/const directory.
[BLOCK] sections¶
The block sections are handled in the same manner as those within the app.ini file, however the type, unless overwritten in the block.ini files for these blocks is set to carrier, rather than soft.
Writing docs¶
Two RST directives, how to structure
Block VHDL entity¶
How to structure the VHDL entity
Autogeneration framework architecture¶
Wrappers¶
How wrapper, config, desc, vhdl entities, test benches are generated
Config_d entries¶
-
common.python.configs.
pad
(name, spaces=19)[source]¶ Pad the right of a name with spaces until it is at least spaces long
-
class
common.python.configs.
BlockConfig
(name, type, number, ini, module_name)[source]¶ The config for a single Block
-
name
= None¶ The name of the Block, like LUT
-
number
= None¶ The number of instances Blocks that will be created, like 8
-
block_address
= None¶ The Block section of the register address space
-
module_name
= None¶ The module name (can be different to block name)
-
entity
= None¶ The VHDL entity name, like lut
-
type
= None¶ Is the block soft, sfp, fmc or dma?
-
constraints
= None¶ Any constraints?
-
ip
= None¶ Does the block require IP?
-
description
= None¶ The description, like “Lookup table”
-
fields
= None¶ All the child fields
-
-
class
common.python.configs.
RegisterConfig
(name, number=-1, prefix='', extension='')[source]¶ A low level register name and number backing this field
-
name
= None¶ The name of the register, like INPA_DLY
-
number
= None¶ The register number relative to Block, like 9
-
extension
= None¶ For an extension field, the register path
-
-
class
common.python.configs.
BusEntryConfig
(bus, index)[source]¶ A bus entry belonging to a field
-
bus
= None¶ The name of the register, like bit
-
index
= None¶ The bus index, like 5
-
-
class
common.python.configs.
FieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ The config for a single Field of a Block
-
type_regex
= None¶ Regex for matching a type string to this field
-
name
= None¶ The name of the field relative to it’s Block, like INPA
-
number
= None¶ The number of instances Blocks that will be created, like 8
-
type
= None¶ The complete type string, like param lut
-
description
= None¶ The long description of the field
-
registers
= None¶ The list of registers this field uses
-
bus_entries
= None¶ The list of bus entries this field has
-
wstb
= None¶ If a write strobe is required, set wstb to 1
-
extension
= None¶ Store the extension register info
-
value
= None¶ The current value of this field for simulation
-
extra_config_lines
= None¶ All the other extra config items
-
-
class
common.python.configs.
BitOutFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a single entry on the bit bus
-
class
common.python.configs.
PosOutFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a position output
-
class
common.python.configs.
ExtOutFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a ext output
-
class
common.python.configs.
ExtOutTimeFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a ext output timestamp, which requires two registers
-
class
common.python.configs.
TableFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a table field
-
words
= None¶ How many 32-bit words per line?
-
-
class
common.python.configs.
TableShortFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a table field
-
class
common.python.configs.
ParamFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent all other set/get parameters backed with a single register
-
class
common.python.configs.
EnumParamFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ A special These fields represent all other set/get parameters backed with a single register
-
enumlength
= 0¶ If there is an enum, how long is it?
-
-
class
common.python.configs.
BitMuxFieldConfig
(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]¶ These fields represent a single entry on the pos bus
Test benches¶
A generic outline is common across the testbenches for the different blocks. There are four main areas of required functionality: Assigning signals, reading expected data, assigning inputs to the UUT and reading the outputs and comparing the outputs.
A template can therefore be used to autogenerate the testbench, with this common functionality, with the modifications required for use with the different blocks.
Required signals in the block¶
Python code used has extracted the different signals which are required from the
.block.ini
file for each block. Using this information, register signals are
produced in the testbench for each signal, using the names from the INI file.
However, not all signals are used in the same manner. Therefore the field type
of each signal is also read to determine the size of the required register for
each signals. This is also used to determine whether the signal is an input or
an output signal. Each output signal requires a register signal similar to the
the inputs, however they also require wire signals for use with the UUT, this is
differentiated by the suffix “_UUT” and an error register which is
differentiated by the suffix “_error”.
Integer signals are also declared for holding the file identifier, the
$fscanf
return value and the timestamp.
Read expected.csv¶
From the .timing.ini
file within the block, a CSV file is generated which
describes how the UUT should behave under certain inputs at different times. The
first line of the file contains strings with the names of each of the signals,
the first column being the timestamp data. All other lines contain numeric data
for the timestamp, inputs and corresponding outputs.
The file is opened in the testbench and read line by line. The first line, containing the names of the signals is discarded. The numeric data is then read, when the timestamp value is equal to that in the file the values are assigned to the corresponding registers in the testbench. The data in the file is ordered in the same way as the .block.ini file so iterating through the signals in order, will assign the data to the correct registers.
set the input values of the VHDL entity to that from the file, and read the outputs
The inputs to the entity for the block will have the same name as for those used in the testbench. It is therefore straightforward to connect the signals. The registers with the same name as the outputs are being used for holding the expected values, therefore the wire signals with the suffix “_uut” are used to read the output signals.
Compare output signals¶
To verify the correct functionality of the block, the outputted values will need to be compared to the expected values. A simple comparison is implemented, if the two signals are not equal, set that output’s error signal to one and display an error message to the user.
Change Log¶
All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.
Unreleased¶
Added:
- Started a changelog
Changed:
- Interface to the server, require 1.0 release of the server package
Glossary¶
This section defines some commonly used PandABlocks terms.
App¶
An ini file that contains the type and number of Blocks that should be built together to form an FPGA image (loadable on a PandABlocks device as a Zpkg).
Block¶
A piece of FPGA logic that has a number of Field instances and does some specified calculations on each FPGA clock tick. It may be a soft Block like a SEQ, or have hardware connections like a TTLIN Block.
Module¶
A directory containing Block definitions, logic, simulations and timing. Modules will typically contain a single soft Block definition, or a number of hardware Blocks tied to a particular Target Platform, SFP or FMC card.
PandABox¶
A PandABlocks Device manufactured by Diamond Light Source and SOLEIL. Schematics on Open Hardware
PandABlocks Device¶
A Zynq 7030 based device loaded with PandABlocks rootfs so that it runs the PandABlocks framework.
Target Platform¶
The physical Zynq based hardware that will be loaded with firmware to become a PandABlocks Device like a PandABox or a Picozed Carrier
Zpkg¶
A specially formatted tar file of built files that can be deployed to a PandABlocks device