Block Preview

Introduction

Principle of Operation

Detectors and preamplifiers come in both polarities, and most downstream processing (trapezoidal shapers, peak detectors, discriminators) expects positive-going pulses. Polarity Invert makes that a runtime choice: raise INVERT and the stream is flipped, lower it and the stream passes through untouched.

What “flipped” means depends on the Input sign property, and the two answers are genuinely different operations — not two spellings of the same one:

Input sign operation when INVERT = 1 in words
UNSIGNED $y = (2^{N_{\rm eff}} - 1) - x$ reflection about full scale — this is the bitwise complement, $y = \overline{x}$
SIGNED $y = -x \pmod{2^{N_{\rm eff}}}$ two’s-complement negation

$N_{\rm eff}$ is the Effective # bits property, not the port width. Both paths are pure combinational logic: the output changes in the same clock period as the input, with zero latency and no clock or reset pin.

Which mode do I want?

  • UNSIGNED is the natural choice for a raw ADC stream. A negative-going pulse sitting on a pedestal near full scale becomes a positive-going pulse sitting on a pedestal near zero, and the pedestal moves with it — the baseline restorer downstream will absorb the shift. Nothing can overflow, because the mapping $x \mapsto \overline{x}$ is a bijection of the $N_{\rm eff}$-bit range onto itself.
  • SIGNED is the choice once the pedestal has already been removed and the stream is a signed excursion about zero. It is a true negation, so it preserves the “distance from zero” of every sample — at the price of the one edge case described below.

Pin Description

a Input InputSize bit BIT VECTOR
Input word (IN on the canvas), Input bits wide. Only the low Effective # bits are used; anything above them is discarded, inverted or not.
Default: Must be connected
pol Input 1 bit BIT

Invert control (INVERT on the canvas), one bit, sampled combinationally.

  • 0 → output = input (truncated to Effective # bits)
  • 1 → output = inverted input, per Input sign

It has no default: drive it from a register or a constant.

b Output InputSize bit BIT VECTOR
Output word (OUT on the canvas), same width as the input. Valid in the same clock period as IN — there is no pipeline register. Note the zero-extension caveat when Effective # bits < Input bits in SIGNED mode.

Properties

Property window

Input bits InputSize

Set the number of bits of the input

Width in bits of IN and OUT — they always match. This is the width of the bus, not the resolution of the data; use Effective # bits for that.

Range: 2 to 2048. Changing it rebuilds the symbol.

Default: 16

Default: 16

Range: 2 – 2048

Effective # bits EffSize

Set the effective number of significative bits in the data. For example for 14 bit ADC board set this number to 14

Effective # bits — the number of significant bits actually carried in the low end of the word, and the width the inversion is computed in. On a 14-bit ADC carried in a 16-bit bus, set it to 14: the UNSIGNED reflection then mirrors about $2^{14}-1$ instead of $2^{16}-1$.

Must be less than or equal to Input bits: the core slices a(EffSize-1 downto 0), so a larger value is an out-of-range slice and the design will not elaborate. Bits above it are discarded.

Range: 2 to 2048.

Default: 16

Default: 16

Range: 2 – 2048

Input sign InputSign

Select the sign/unsign of the input

How the word is interpreted, and therefore what “invert” means.

value inversion overflow behaviour
UNSIGNED $y = (2^{N_{\rm eff}}-1) - x$, i.e. bitwise NOT cannot overflow
SIGNED $y = -x$ in two’s complement $-2^{N_{\rm eff}-1}$ wraps onto itself; no saturation

Fixed at synthesis (it becomes the SIGN generic) — it cannot be switched at runtime, only INVERT can.

Default: UNSIGNED

Default: UNSIGNED

Options: UNSIGNED SIGNED

⚙️ Detailed Operation

Datapath

The core (Resources/Code/polinvert.vhd) is three concurrent statements:

            A_SIZE bits                      AN_SIZE bits          A_SIZE bits
 IN ───►[ take the low AN_SIZE bits ]───► aa ───►[ invert ]───► bb ───►[ resize ]───► OUT
                                                     ▲
                                          INVERT ────┘
  

with the generics driven from the properties: A_SIZE = Input bits, AN_SIZE = Effective # bits, SIGN = Input sign.

  • INVERT = 0 → bb = aa, a straight copy.
  • INVERT = 1, SIGN = "UNSIGNED" → bb = A_MAX - aa, where A_MAX is AN_SIZE ones, i.e. $2^{N_{\rm eff}}-1$. Subtracting from an all-ones constant never borrows, so this is exactly the bitwise NOT of the word.
  • INVERT = 1, SIGN = "SIGNED" → bb = 0 - aa, evaluated in AN_SIZE-bit signed arithmetic.

The one thing people get wrong: the most negative value

In SIGNED mode the subtraction is performed modulo $2^{N_{\rm eff}}$ and is never clamped. The architecture does declare an A_MIN/A_MAX pair in the signed branch, but nothing ever reads them — there is no saturation logic anywhere in the file.

So for the most negative representable sample:

$$ -2^{N_{\rm eff}-1} ;\longmapsto; 0 - \left(-2^{N_{\rm eff}-1}\right) = 2^{N_{\rm eff}-1} \equiv -2^{N_{\rm eff}-1} $$

With Effective # bits = 16, input 0x8000 (−32768) comes out as 0x8000 (−32768) — the sample is not inverted, it wraps back onto itself. Every other value negates correctly.

Effective # bits = 8, SIGNED, INVERT = 1
IN OUT note
0x00 (0) 0x00 (0)
0x01 (1) 0xFF (−1)
0x7F (127) 0x81 (−127)
0x80 (−128) 0x80 (−128) wraps — not saturated to +127

Effective # bits truncates, in both directions

aa is taken as the low AN_SIZE bits of the input, and the result is resized back to A_SIZE. That has two consequences that hold even when INVERT = 0:

  • Any input bit above Effective # bits is discarded. The block is not a transparent pass-through when Effective # bits < Input bits.
  • The reflection point in UNSIGNED mode is $2^{N_{\rm eff}}-1$, not $2^{N_{\rm in}}-1$. This is the whole purpose of the property: on a board whose 14-bit ADC is carried in a 16-bit bus, set Effective # bits = 14 and the inversion mirrors about 16383, which is what the detector actually sees. Leave it at 16 and the pulses will be mirrored about 65535 and end up 49152 counts away from where you wanted them.

Configuration

Neither the size nor the sign can be changed at runtime: Input bits, Effective # bits and Input sign become VHDL generics and are fixed at synthesis. Only INVERT is a live control. The usual pattern is to drive INVERT from a Register block so the SciSDK can flip the polarity per detector without rebuilding the firmware:

  ADC ──────────────► IN  ┌─────────────┐
                        │ Polarity    │──► OUT ──► shaper / trigger chain
Register (1 bit) ─► INVERT  Invert    │
                        └─────────────┘
  

INVERT is a plain one-bit input with no defined default in the component, so wire it explicitly — to a register, or to a constant if the polarity is fixed in your design.

Timing

quantity value
latency 0 clocks (combinational)
throughput one sample per clock of the surrounding logic
clock / reset none — the block has no sequential elements

Because it is combinational, the inverter sits inside the timing path of whatever drives and consumes it; at high clock rates on a wide bus, place a register block after it if timing closure becomes tight.

Resources & Timing

  • Latency: 0 clocks — purely combinational, no clock or reset pin

  • Throughput: 1 sample per clock of the surrounding pipeline

  • Cost is one Effective # bits-wide subtractor plus a multiplexer; in UNSIGNED mode the subtractor degenerates to an XOR with all-ones, which synthesis reduces to inverters.
  • No memory-mapped registers are generated.
  • The INVERT pin is shared logic for the whole word; use the TM twin, Polarity Invert (TM), for time-multiplexed buses.