Skip to content

PCIe · Module 5

PCIe Gen1 — What 2.5 GT/s Actually Means

Deriving encoding-adjusted capacity from first principles: why GT/s is not Gb/s, how 8b/10b costs 20%, what 250 MB/s per lane per direction does and does not describe, and how to instrument RTL to measure the difference.

Module 4 answered where things sit and what shares what. Every conclusion was relative — this segment carries more, those devices interfere. Nothing attached a number to a Link.

Module 5 does, and it opens with the number most often misread in the entire protocol:

What does "PCIe Gen1 at 2.5 GT/s" actually mean, and how do encoding and protocol overhead transform that figure into useful data movement?

1. GT/s Means Giga-Transfers Per Second

Start with what the unit is, because the whole chapter depends on it.

GT/s = giga-transfers per second. It counts signalling events on the wire — how many times per second the transmitter presents a new symbol. It counts transfers, not application bits, not bytes, and not anything the software sees.

For Gen1 the figure is 2.5 GT/s, meaning 2.5 × 10⁹ transfers per second, per lane, in each direction. Three qualifiers, all load-bearing:

  • Per lane — a Link may have several lanes. This chapter uses one; the architectural meaning of Link width belongs to Module 6.
  • Per direction — a Link carries traffic both ways using separate directional resources, so this figure describes one direction (§5).
  • Signalling — the count is of transmitted symbols, which is not the same as data bits (§2).

2. 8b/10b: 8 Bits of Data Per 10 Transmitted

Gen1 uses 8b/10b encoding. The mechanism relevant here is one sentence:

Eight bits of source data are represented on the wire as ten transmitted encoded bits.

That gives an efficiency directly:

8 / 10 = 0.8 → 80%

Twenty percent of everything transmitted is encoding overhead. It is not wasted — it buys properties the physical layer needs from a serial stream with no accompanying clock, as Chapter 3.3 described. But from a data-rate standpoint it is 20% of the wire that does not carry your data.

What this chapter deliberately does not cover: how the mapping is constructed, running disparity, control symbols, or symbol tables. Those are physical-layer mechanism, and this chapter needs only the ratio. Where 8b/10b's internals matter, they belong to Module 17.

3. The Derivation

Work it step by step, keeping units visible at every stage.

The Gen1 rate derivation in four steps: 2.5 giga-transfers per second per lane per direction, multiplied by the 8 over 10 encoding efficiency to give 2.0 gigabits per second, divided by 8 bits per byte to give 0.25 gigabytes per second, which equals 250 megabytes per second theoretical encoding-adjusted capacity.12.5 GT/ssignalling transfers per second, per lane, per direction2× 8/10 → 2.0 Gb/s8b/10b: 8 data bits per 10 transmitted bits3÷ 8 → 0.25 GB/s8 bits per byte4250 MB/stheoretical, encoding-adjusted, per lane, per direction
Figure 1 — the Gen1 rate derivation. Each step changes the unit, and each unit change is where a shortcut goes wrong. The final figure is theoretical, encoding-adjusted, per lane, per direction — and still sits above what any application observes, because protocol and workload overhead have not yet been applied.

Written out:

Step 1 — the signalling rate. 2.5 × 10⁹ transfers/second per lane, per direction.

Step 2 — apply encoding efficiency. 2.5 GT/s × (8 data bits / 10 transmitted bits) = 2.0 × 10⁹ data bits/second = 2.0 Gb/s

The unit changed from transfers to data bits. That conversion is the entire content of the encoding step.

Step 3 — convert to bytes. 2.0 Gb/s ÷ 8 bits/byte = 0.25 × 10⁹ bytes/second = 0.25 GB/s

Step 4 — express conventionally. 0.25 GB/s = 250 MB/s

Units used here are decimal — 1 GB/s = 10⁹ bytes/second — matching the convention in which signalling rates are expressed. If you ever compare against a figure quoted in binary units (MiB/s, GiB/s), convert deliberately; mixing the two silently produces a ~7% discrepancy that is easy to misattribute to protocol overhead.

The result: 250 MB/s is the theoretical, encoding-adjusted capacity per lane, per direction. Every word in that phrase is doing work.

4. What 250 MB/s Is Not

The derivation gives a ceiling. Several things sit between it and what an application observes, and each subtracts.

Packet overhead. Transactions are packets carrying routing and identity information alongside payload, as Chapter 3.1 established. Those bytes traverse the Link and are not application data. The proportion depends on how much payload each packet carries — a large transfer amortises the overhead; a small one does not, and its efficiency can be dramatically worse.

Link-level traffic. The hop-local delivery mechanism of Chapter 3.2 exchanges control information with the neighbour and, when delivery fails, retransmits. That traffic occupies the Link and carries no new application data.

Utilisation. The Link only carries data when something is presenting data. A device that is idle, stalled by backpressure, waiting on completions, or limited by its own logic leaves the Link empty — and an empty Link transfers nothing regardless of its rate.

Workload and system. Transaction sizes, access patterns, host memory behaviour, and software all shape what is achievable. A device issuing many small operations will not approach line rate no matter how capable the Link.

250 MB/s is the ceiling the encoding permits. Everything above the physical layer, plus the workload, determines how close you get.

This chapter does not quote a typical efficiency percentage, because there isn't one — it depends on transaction size, traffic pattern, and implementation, and any single number would be misleading. What matters is knowing the ceiling exists, that it is well below the naive figure, and that measuring the gap requires instrumentation (§6).

5. Both Directions, Carefully

A Link carries traffic in both directions using separate directional resources (Chapter 2.8). So the 250 MB/s figure applies to each direction independently — a device can be receiving while it transmits.

6. Computing Rates in SystemVerilog

Two pieces of code follow. The first computes the theoretical figure at compile time; the second measures what actually happened. The pair is the point: theory and measurement are different quantities, and you need both.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Compile-time only (elaboration constants and pure functions).
// Integer/rational arithmetic throughout — no `real`, no synthesis of division
// at runtime. Units are carried in the names to make misuse visible.
package pcie_rate_pkg;
 
  // Transfer rate expressed in MEGA-transfers per second so Gen1's 2.5 GT/s
  // is representable as an integer (2500 MT/s) without fractions.
  //
  //   encoded_mbps = mtps * enc_num / enc_den        [megabits/s of data]
  //   mbytes_per_s = encoded_mbps / 8                [megabytes/s, decimal]
  //
  // Multiplication precedes division so integer truncation cannot discard
  // significance. All results are per lane, per direction.
 
  function automatic longint unsigned encoded_mbits_per_s(
    input longint unsigned mtps,     // mega-transfers/s, e.g. 2500 for Gen1
    input int unsigned     enc_num,  // data bits per encoded group, e.g. 8
    input int unsigned     enc_den   // transmitted bits per group, e.g. 10
  );
    // Guard against a nonsensical configuration producing a silent result.
    if (enc_den == 0 || enc_num > enc_den) return 0;
    return (mtps * enc_num) / enc_den;
  endfunction
 
  function automatic longint unsigned mbytes_per_s(
    input longint unsigned mtps,
    input int unsigned     enc_num,
    input int unsigned     enc_den
  );
    return encoded_mbits_per_s(mtps, enc_num, enc_den) / 8;
  endfunction
 
  // Gen1: 2500 MT/s with 8b/10b, per lane, per direction.
  localparam longint unsigned GEN1_MTPS        = 2500;
  localparam int unsigned     ENC8B10B_NUM     = 8;
  localparam int unsigned     ENC8B10B_DEN     = 10;
 
  localparam longint unsigned GEN1_ENC_MBPS    =
      encoded_mbits_per_s(GEN1_MTPS, ENC8B10B_NUM, ENC8B10B_DEN);   // 2000
  localparam longint unsigned GEN1_MBYTES_PER_S =
      mbytes_per_s(GEN1_MTPS, ENC8B10B_NUM, ENC8B10B_DEN);          // 250
 
endpackage

Classification: compile-time only (functions evaluated during elaboration).

What it teaches: the derivation as executable arithmetic, with multiplication before division so integer truncation cannot silently lose precision, and with units embedded in the identifiers so a misuse is visible at the call site.

Deliberately simplified: no lane-count parameter — lane-width scaling is Module 6's subject and folding it in here would invite exactly the bandwidth tables this chapter avoids. No representation of protocol overhead, because that is workload-dependent and not a constant.

What to notice: GEN1_MBYTES_PER_S evaluates to 250, and the name says megabytes per second. It does not say "throughput," because it is not throughput. Naming discipline in code is the same discipline as naming discipline in prose.

7. Measuring What Actually Happened

The theoretical figure tells you the ceiling. Only measurement tells you the gap — and the gap is where the engineering is.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative synthesizable RTL — throughput and utilisation instrumentation.
// NOT a PCIe controller. Classifies every cycle in a measurement window and
// counts payload bytes actually transferred.
module link_utilisation #(
  parameter int unsigned BYTES_PER_BEAT = 16,
  parameter int unsigned CNT_W          = 48
) (
  input  logic              clk,
  input  logic              rst_n,
 
  // Observed interface. This module only watches — it never drives valid or
  // ready, so it cannot perturb the behaviour it is measuring.
  input  logic              obs_valid,
  input  logic              obs_ready,
  input  logic [15:0]       obs_bytes,      // valid payload bytes this beat
 
  // Measurement window control
  input  logic              win_start,      // begin a new window
  input  logic              win_stop,       // freeze counters
 
  output logic [CNT_W-1:0]  cyc_total,      // cycles in the window
  output logic [CNT_W-1:0]  cyc_transfer,   // valid && ready
  output logic [CNT_W-1:0]  cyc_stalled,    // valid && !ready  (sink blocking)
  output logic [CNT_W-1:0]  cyc_idle,       // !valid           (source has nothing)
  output logic [CNT_W-1:0]  bytes_moved,    // payload bytes actually transferred
  output logic              running
);
 
  logic running_q;
  assign running = running_q;
 
  wire transfer = obs_valid &&  obs_ready;
  wire stalled  = obs_valid && !obs_ready;
  wire idle     = !obs_valid;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      running_q    <= 1'b0;
      cyc_total    <= '0;
      cyc_transfer <= '0;
      cyc_stalled  <= '0;
      cyc_idle     <= '0;
      bytes_moved  <= '0;
    end else begin
      if (win_start) begin
        // Clear on start so a window is a clean measurement, not cumulative.
        running_q    <= 1'b1;
        cyc_total    <= '0;
        cyc_transfer <= '0;
        cyc_stalled  <= '0;
        cyc_idle     <= '0;
        bytes_moved  <= '0;
      end else if (win_stop) begin
        running_q <= 1'b0;
      end else if (running_q) begin
        cyc_total <= cyc_total + 1'b1;
 
        // Exactly one classification per cycle — the three are mutually
        // exclusive and exhaustive by construction.
        if (transfer)     cyc_transfer <= cyc_transfer + 1'b1;
        else if (stalled) cyc_stalled  <= cyc_stalled  + 1'b1;
        else              cyc_idle     <= cyc_idle     + 1'b1;
 
        // Payload bytes accrue only on an accepted beat.
        if (transfer) bytes_moved <= bytes_moved + obs_bytes;
      end
    end
  end
endmodule

Classification: synthesizable.

What it teaches: that "the Link is slow" decomposes into three measurable causes — transferring, stalled (the sink is blocking), and idle (the source has nothing to send) — and that only the first moves data.

Deliberately simplified: one interface observed rather than both directions; a single flat window rather than rolling statistics; counters that could overflow on an extremely long window, bounded here by CNT_W; and obs_bytes assumed already validated by whatever produces it.

What to notice:

  • The module never drives valid or ready. Instrumentation that perturbs the interface it measures is worse than none, because the measurement then describes a system that does not exist in production.
  • The three cycle classes are mutually exclusive and exhaustive, so they sum to cyc_total. That invariant is assertable (§8) and catches classification bugs immediately.
  • Bytes accrue only on valid && ready. Counting on valid alone would inflate the figure by every stalled cycle — a very common instrumentation bug that produces impossibly good numbers.

Why this matters: with these counters, "PCIe is slow" becomes something like "the source was idle for most of the window, so the Link was never the constraint." That is a different problem with a different fix, and no amount of reasoning about encoding efficiency would have found it.

8. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SVA over the illustrative instrumentation. Implementation invariants for
// THIS design — not PCIe protocol requirements.
 
// P1 — CLASSIFICATION IS EXHAUSTIVE. The three cycle classes must account for
// every counted cycle. A mismatch means a cycle was classified twice or not
// at all, which silently corrupts every derived percentage.
property p_cycles_sum;
  @(posedge clk) disable iff (!rst_n)
  cyc_total == (cyc_transfer + cyc_stalled + cyc_idle);
endproperty
a_cycles_sum : assert property (p_cycles_sum);
 
// P2 — BYTES ONLY ON ACCEPTED BEATS. Catches the classic inflation bug of
// counting on valid alone.
property p_bytes_only_on_transfer;
  @(posedge clk) disable iff (!rst_n)
  (bytes_moved != $past(bytes_moved)) |-> $past(obs_valid && obs_ready);
endproperty
a_bytes_on_transfer : assert property (p_bytes_only_on_transfer);
 
// P3 — COUNTERS FROZEN WHEN NOT RUNNING. A window that keeps accruing after
// it stopped reports a measurement that never happened.
property p_frozen_when_stopped;
  @(posedge clk) disable iff (!rst_n)
  (!running && !win_start) |=> $stable(cyc_total) && $stable(bytes_moved);
endproperty
a_frozen : assert property (p_frozen_when_stopped);
 
// P4 — CONFIGURATION LEGALITY. An encoding claiming more data bits than
// transmitted bits would produce an efficiency above 100%.
//   (elaboration-time check, shown here for completeness)
// initial assert (ENC8B10B_NUM <= ENC8B10B_DEN);

P1 is the one that matters most. Every utilisation percentage is derived from these three counters, so a classification bug does not produce an obviously wrong answer — it produces a plausible wrong answer, which is far more damaging. Simulation will not flag it, because nothing else checks the relationship.

P2 catches counting on valid instead of valid && ready. The symptom is measured throughput exceeding what the interface could possibly carry, which is at least obvious once noticed — but only if someone compares against the theoretical ceiling, which is exactly why §3 exists.

P3 catches windows that do not stop, producing measurements spanning more than the interval intended.

9. Verification

What monitors should observe. The interface handshake, the byte count per accepted beat, and the window boundaries. The monitor must derive throughput independently of the design's own counters.

What to generate:

  • Sustained saturation — source always has data, sink always accepts. Utilisation should approach 100% transferring, and measured byte rate should approach the theoretical ceiling minus whatever overhead the model includes.
  • Sustained backpressure — sink accepts rarely. Should show high cyc_stalled, low bytes.
  • Idle source — source rarely offers. Should show high cyc_idle, and this is the case that most often surprises people in real systems.
  • Bursty traffic — alternating saturation and idle. Verifies the counters attribute cycles correctly across transitions, and demonstrates why short bursts never reach theoretical rates.
  • Window boundaries — start and stop asserted at various points, including mid-burst, confirming counters clear and freeze correctly.
  • Beat-size variation — minimum and maximum obs_bytes, confirming byte accounting scales rather than assuming a fixed size.

Coverage worth defining: each cycle class dominant in at least one window; windows containing all three classes; window start and stop during transfer, stall, and idle; byte counts at extremes.

10. Debugging Rate Questions

A checklist for "why is this slower than expected," ordered by how often each is the answer.

Was GT/s read as GB/s? The first thing to check, because it is the most common and it makes an expectation impossible to meet. If someone expected 2.5 GB/s from a Gen1 lane, the system is behaving correctly and the expectation was wrong by an order of magnitude.

Was encoding overhead applied? Expecting 312.5 MB/s instead of 250 MB/s per lane per direction means 8b/10b was skipped. A 25% shortfall against a naive expectation is exactly this.

Are decimal and binary units being mixed? Comparing a decimal MB/s figure against a tool reporting MiB/s produces a ~7% discrepancy easily misattributed to protocol inefficiency.

Were both directions summed? Comparing a one-direction measurement against a both-directions figure produces an apparent 2× shortfall that is purely an accounting error.

Is the Link width what you assume? Per-lane figures scale with width, and assuming a width the topology does not actually provide is a common source of confusion. Verify the actual configuration — and note that the architectural meaning of width is Module 6.

Is the source actually generating demand? Check cyc_idle. A largely idle Link is not a Link problem, and no amount of protocol analysis will help. This is where instrumentation earns its cost.

Is the sink applying backpressure? Check cyc_stalled. High stall means something downstream is the constraint — which, per Module 4, may be several hops away.

Is the transfer size too small? Packet overhead is amortised over payload. A workload of many small operations will sit well below line rate as a matter of arithmetic, not malfunction.

Was the measurement long enough? Short bursts include ramp-up and never reach steady state. A window that captures a burst and its surrounding idle reports a low average that describes the measurement, not the Link.

11. Common Misconceptions

12. Understanding Check

13. Summary

GT/s counts signalling transfers, per lane, per direction — not bits of data and not bytes. For Gen1 the figure is 2.5 GT/s.

8b/10b encoding is 80% efficient: eight data bits are represented as ten transmitted bits, so 20% of the wire carries encoding rather than data.

The derivation, with units visible at every step:

2.5 GT/s × 8/10 = 2.0 Gb/s2.0 Gb/s ÷ 8 = 0.25 GB/s250 MB/s

That result is the theoretical, encoding-adjusted capacity per lane, per direction, in decimal units. It is a ceiling, not a throughput: packet overhead, link-level control traffic, utilisation, transaction size, and system behaviour all sit between it and what an application observes. Both directions are independently usable, but summing them describes a capability most workloads do not exercise.

Because the gap between ceiling and observed is where the engineering lives, instrument to measure it: classify every cycle as transferring, stalled, or idle, and count payload bytes only on accepted beats. That turns "PCIe is slow" into a specific and actionable statement.

Hold the model: 2.5 GT/s is a signalling rate; 250 MB/s per lane per direction is what the encoding permits; what your application sees is neither.

14. What Comes Next

Chapter 5.2 — PCIe Gen2 doubles the signalling rate while keeping 8b/10b unchanged — a clean controlled comparison that isolates the effect of rate alone. It also takes up the question this chapter's framing makes unavoidable: when doubling the Link's capability does not double what the application achieves, and how to work out why.

Later chapters in Module 5 cover the generations where more than the rate changes, including a different encoding scheme with different efficiency. Module 6 takes up Link width and the architectural meaning of lanes, which this chapter deliberately held at one.

Revisit Physical Layer for where encoding sits in the stack, or Real System Examples for the topology reasoning that determines whether a Link is even the constraint. Browse the full path on the PCIe tutorials index.