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:

Using an existing PandABlocks device

Work through the Tutorials.

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:

_images/tutorial1_layout.png

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:

_images/tutorial1_pulse.png

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)

_images/bits_doc-1.png

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

Adding inputs

The output is the sum of the inputs

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

_images/calc_doc-1.png

Scaling

The scale factor is a bit shift and is applied after the sum.

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

_images/calc_doc-2.png

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)

_images/clocks_doc-1.png

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)

_images/clocks_doc-2.png

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)

_images/counter_doc-1.png

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)

_images/counter_doc-2.png

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)

_images/counter_doc-3.png

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)

_images/counter_doc-4.png

A similar thing happens for a negative overflow:

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

_images/counter_doc-5.png

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)

_images/counter_doc-6.png

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)

_images/counter_doc-7.png

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)

_images/div_doc-1.png

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

_images/div_doc-2.png

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)

_images/div_doc-3.png

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)

_images/filter_doc-1.png

After the operation, the latched value is updated to be the current value on the input.

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

_images/filter_doc-2.png

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)

_images/filter_doc-3.png

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)

_images/filter_doc-4.png

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

_images/filter_doc-5.png

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

_images/filter_doc-6.png

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)

_images/filter_doc-7.png

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

_images/filter_doc-8.png

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)

_images/lut_doc-1.png

~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)

_images/lut_doc-2.png

A (FUNC= 0xffff0000). The output should only be 1 if A is 1 irrespective of any other input.

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

_images/lut_doc-3.png

A&B|C&~D (FUNC= 0xff303030)

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

_images/lut_doc-4.png

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)

_images/lut_doc-5.png

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)

_images/lut_doc-6.png

We could also use this for generating pulses on every transition of A:

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

_images/lut_doc-7.png

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)

_images/pcomp_doc-1.png

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)

_images/pcomp_doc-2.png

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)

_images/pcomp_doc-3.png

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:

digraph pcomp_sm { WAIT_ENABLE [label="State 0\nWAIT_ENABLE",] WAIT_DIR [label="State 1\nWAIT_DIR"] WAIT_PRE_START [label="State 2\nWAIT_PRE_START"] WAIT_RISING [label="State 3\nWAIT_RISING"] WAIT_FALLING [label="State 4\nWAIT_FALLING"] WAIT_ENABLE -> WAIT_DIR [label="rising ENABLE\n & DIR=EITHER ",fontsize=13] WAIT_ENABLE -> WAIT_PRE_START [label=" rising\n ENABLE ",fontsize=13] WAIT_DIR -> WAIT_ENABLE [label=" Can't guess\n DIR \n or Disabled "] [fontsize=13] WAIT_DIR -> WAIT_PRE_START [label=" DIR\n calculated ",fontsize=13] WAIT_DIR -> WAIT_FALLING [label=" DIR calculated \n & \n no PRE_START"] [fontsize=13] WAIT_PRE_START -> WAIT_ENABLE [label=" Disabled "][fontsize=13] WAIT_PRE_START -> WAIT_RISING [label=" < PRE_START > "][fontsize=13] WAIT_RISING -> WAIT_ENABLE [label=" jump > WIDTH + STEP \n or Disabled "] [fontsize=13] WAIT_RISING -> WAIT_FALLING [label=" >= pulse ",fontsize=13] WAIT_FALLING -> WAIT_ENABLE [label=" jump > WIDTH + STEP \n or Finished \nor Disabled",fontsize=13] WAIT_FALLING -> WAIT_RISING [label=" >= pulse \n + WIDTH "] [fontsize=13] }

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)

_images/pcomp_doc-4.png

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)

_images/pcomp_doc-5.png

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)

_images/pcomp_doc-6.png

Guessing the direction

We can also ask to the Block to calculate direction for us:

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

_images/pcomp_doc-7.png

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)

_images/pcomp_doc-8.png

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)

_images/pcomp_doc-9.png

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

_images/pcomp_doc-10.png

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)

_images/pcomp_doc-11.png

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)

_images/pcomp_doc-12.png

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)

_images/pcomp_doc-13.png

And if we skipped a larger number of points we get the same error:

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

_images/pcomp_doc-14.png

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)

_images/pcomp_doc-15.png

We can also guess the direction in relative mode:

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

_images/pcomp_doc-16.png

This works when going negative too:

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

_images/pcomp_doc-17.png

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)

_images/pcomp_doc-18.png

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)

_images/pcomp_doc-19.png

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)

_images/pcomp_doc-20.png

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)

_images/pcomp_doc-21.png

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)

_images/pgen_doc-1.png
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)

_images/posenc_doc-1.png

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

_images/posenc_doc-2.png

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)

_images/posenc_doc-3.png

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

_images/posenc_doc-4.png

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)

_images/pulse_doc-1.png

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

_images/pulse_doc-2.png

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)

_images/pulse_doc-3.png

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

_images/pulse_doc-4.png

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)

_images/pulse_doc-5.png

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

_images/pulse_doc-6.png

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

_images/pulse_doc-7.png

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)

_images/pulse_doc-8.png

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

_images/pulse_doc-9.png

Pulse period error

The following example shows what happens when the period between pulses is too short.

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

_images/pulse_doc-10.png

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)

_images/qdec_doc-1.png

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

_images/qdec_doc-2.png

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)

_images/qdec_doc-3.png

Limitations

The block can continue to count when there is not a constant period between the pulses.

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

_images/qdec_doc-4.png

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)

_images/qdec_doc-5.png

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)

_images/seq_doc-1.png
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)

_images/seq_doc-2.png
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)

_images/seq_doc-3.png
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)

_images/seq_doc-4.png
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.

digraph pcomp_sm { WAIT_ENABLE [label="State 0\nWAIT_ENABLE"] LOAD_TABLE [label="State 1\nLOAD_TABLE"] WAIT_TRIGGER [label="State 2\nWAIT_TRIGGER"] PHASE1 [label="State 3\nPHASE1"] PHASE2 [label="State 4\nPHASE2"] WAIT_ENABLE -> LOAD_TABLE [label=" TABLE load started "] WAIT_ENABLE -> WAIT_TRIGGER [label=" rising ENABLE and trigger not met "] WAIT_ENABLE -> PHASE1 [label=" rising ENABLE and trigger met "] WAIT_ENABLE -> PHASE2 [label=" rising ENABLE and trigger met and no phase1 "] LOAD_TABLE -> WAIT_ENABLE [label=" TABLE load complete "] WAIT_TRIGGER -> LOAD_TABLE [label=" TABLE load started "] WAIT_TRIGGER -> PHASE1 [label=" trigger met "] WAIT_TRIGGER -> PHASE2 [label=" trigger met and no phase1 "] PHASE1 -> LOAD_TABLE [label=" TABLE load started "] PHASE1 -> PHASE2 [label=" time1 elapsed "] PHASE2 -> LOAD_TABLE [label=" TABLE load started "] PHASE2 -> WAIT_TRIGGER [label=" next trigger not met "] PHASE2 -> PHASE1 [label=" next trigger met "] PHASE2 -> PHASE2 [label=" next trigger met and no phase1 "] }

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)

_images/seq_doc-5.png
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)

_images/seq_doc-6.png
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)

_images/seq_doc-7.png
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)

_images/seq_doc-8.png
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)

_images/seq_doc-9.png
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)

_images/seq_doc-10.png
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)

_images/seq_doc-11.png
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)

_images/srgate_doc-1.png

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

_images/srgate_doc-2.png

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

_images/srgate_doc-3.png

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

_images/srgate_doc-4.png

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

_images/srgate_doc-5.png

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

_images/srgate_doc-6.png

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)

_images/srgate_doc-7.png

The disabled value can also be set high:

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

_images/srgate_doc-8.png

Or left at its current value:

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

_images/srgate_doc-9.png

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)

_images/srgate_doc-10.png

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)

_images/srgate_doc-11.png

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

_images/srgate_doc-12.png

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)

_images/srgate_doc-13.png

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)

_images/pcap_doc-1.png

Or on the falling edge of ENABLE:

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

_images/pcap_doc-2.png

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)

_images/pcap_doc-3.png
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)

_images/pcap_doc-4.png
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)

_images/pcap_doc-5.png
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)

_images/pcap_doc-6.png
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)

_images/pcap_doc-7.png
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)

_images/pcap_doc-8.png
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)

_images/pcap_doc-9.png
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)

_images/pcap_doc-10.png
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)

_images/pcap_doc-11.png
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)

_images/pcap_doc-12.png
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)

_images/pcap_doc-13.png
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)

_images/pcap_doc-14.png
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)

_images/pcap_doc-15.png
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)

_images/pcap_doc-16.png
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)

_images/pcap_doc-17.png
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

TTLIN - TTL Input

The TTLIN block handles the signals from the TTL Input connectors

Fields

Name Type Description
TERM param enum
Select TTL input termination
0 High-Z
1 50-Ohm
VAL bit_out TTL input value

TTLOUT - TTL Output

The TTLOUT block handles the signals to the TTL Output connectors

Fields

Name Type Description
VAL bit_mux TTL output value

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

App build process

Run:

make

And it will make a Zpkg for each App that can be loaded onto the PandABlocks Device. You can specify a subset of Apps to be built in the top level CONFIG file by specifying something like:

APPS = PandABox-no-fmc

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()

classmethod bits_to_int(bits)[source]

Convert 32 element bit array into an int number

on_changes(ts, changes)[source]

Handle field changes at a particular timestamp

Parameters:
  • 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

Next we read the block ini file:

NAMES, PROPERTIES = properties_from_ini(__file__, "myblock.block.ini")

This generates two objects:

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

common.python.configs.all_subclasses(cls)[source]

Recursively find all the subclasses of cls

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

register_addresses(block_counters)[source]

Register this block in the address space

filter_fields(regex, matching=True)[source]

Filter our child fields by typ. If not matching return those that don’t match

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

parse_extra_config(extra_config)[source]

Produce any extra config lines from self.kwargs

register_addresses(counters)[source]

Create registers using the FieldCounter object

address_line()[source]

Produce the line that should go in the registers file after name

config_line()[source]

Produce the line that should go in the config file after name

numbered_registers()[source]

Filter self.registers, only producing registers with a number (not those that are purely extension registers)

notify_changed(v)[source]

Will be overwritten by simulation

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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?

register_addresses(counters)[source]

Create registers using the FieldCounter object

config_line()[source]

Produce the line that should go in the config file after name

parse_extra_config(extra_config)[source]

Produce any extra config lines from self.kwargs

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

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?

parse_extra_config(extra_config)[source]

Produce any extra config lines from self.kwargs

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

register_addresses(counters)[source]

Create registers using the FieldCounter object

class common.python.configs.PosMuxFieldConfig(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]

The fields represent a position input multiplexer selection

register_addresses(counters)[source]

Create registers using the FieldCounter object

class common.python.configs.TimeFieldConfig(name, number, type, description, wstb=False, extension=None, extension_reg=None, **extra_config)[source]

The fields represent a configurable timer parameter

register_addresses(counters)[source]

Create registers using the FieldCounter object

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.

Field

An input, output or parameter of a 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