Skip to content
VLSI Mentor

UART · Module 17

Bit Order, Parity and Framing Failure Signatures

Why 0xA5 cannot detect a reversed byte, why parity is structurally blind to bit order, why one frame can never separate a parity misconfiguration from noise, and a classifier in three HDLs that resolves all four faults.

From an application's point of view there is one symptom: the byte that arrived is not the byte that was sent. Underneath it are at least four distinct faults, with four distinct fixes, and a status register that reports "parity error" or "framing error" resolves only some of them — sometimes into the wrong one.

This chapter builds a classifier that separates them, and in doing so runs into two results that are worth the chapter on their own. One of the most widely used UART test bytes in the world cannot detect a reversed byte at all. And a parity misconfiguration is, from a single frame, mathematically indistinguishable from a single noise event.

1. Four Faults, One Symptom

FaultWhat is physically wrongThe fix
Bit order reversedthe byte is assembled MSB-first at one endone line of RTL, or a configuration bit
Parity configurationone end is even, the other odda configuration bit
Parity noisethe wire occasionally flips a bitcabling, termination, shielding, slew rate
Framing violationthe stop bit is not MARKbaud, or a genuinely malformed frame

The four are not distinguishable from a single corrupted byte, and two of them are not distinguishable from a single frame at all — no matter how carefully you look at it. That is not an engineering limitation to be designed around; it is a property of how much information one frame contains. The way out is to stop looking at frames and start looking at rates.

2. Bit Order, and the Bytes That Cannot Detect It

A UART transmits least-significant bit first. Assemble the incoming bits the other way round and every byte arrives bit-reversed — a completely systematic, completely repeatable remapping.

It is also, under the right test vector, completely invisible.

A byte is unchanged by reversal when bit 0 equals bit 7, bit 1 equals bit 6, bit 2 equals bit 5 and bit 3 equals bit 4. That is four free bits, so there are exactly sixteen such bytes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  0x00  0x18  0x24  0x3C  0x42  0x5A  0x66  0x7E  0x81  0x99  0xA5  0xBD  0xC3  0xDB  0xE7  0xFF

Several of those are among the most-reached-for test values in the field:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  byte    reversed   can it detect a reversal?
  ----    --------   -------------------------
  0x00      0x00       NO  - its own mirror
  0xFF      0xFF       NO  - its own mirror
  0x55      0xAA       yes
  0xAA      0x55       yes
  0xA5      0xA5       NO  - its own mirror
  0x5A      0x5A       NO  - its own mirror
  0x3C      0x3C       NO  - its own mirror
  0x0F      0xF0       yes
  0xF0      0x0F       yes
  0x81      0x81       NO  - its own mirror
  0x7E      0x7E       NO  - its own mirror
  0xDE      0x7B       yes

There is a second blindness here, and it is structural rather than accidental:

3. Parity: Configuration or Noise?

Suppose the receiver is configured for even parity and the transmitter is sending odd. Every frame arrives with a parity bit that is the complement of what the receiver computes, so every frame raises a parity error.

Now suppose instead the link is correctly configured and a noise event flips the parity bit on one frame. That frame arrives with a parity bit that is the complement of what the receiver computes, so it raises a parity error.

The two frames are bit-for-bit identical on the wire. There is no measurement that separates them, because there is nothing to separate — a single frame simply does not carry the information.

What does separate them is the rate:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  every frame wrong     ->   configuration. Change a config bit.
  some frames wrong     ->   noise. Change the physical layer.
  no frames wrong       ->   nothing to fix (or nothing detectable -- see §2)

This is why the classifier below accumulates across frames and refuses to answer until it has seen MINFRAMES of them. A debug instrument that reports a verdict from one frame is reporting a guess.

4. The Classifier

The block records six counters and derives a verdict from them. The counters matter as much as the verdict: they are the evidence, and they remain available when the verdict is unhelpful.

Verilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------------
// uart_fail_classifier -- decide WHICH failure a capture shows.
//
// From software, a reversed byte, a parity-configuration mismatch and a
// genuine framing violation all look the same: "the data is wrong". They are
// not the same fault and they do not have the same fix. This block separates
// them.
//
// The central design decision is that the verdict is accumulated over MANY
// frames, not formed from one. That is forced by the physics, not by taste:
//
//   * A parity CONFIGURATION mismatch (even vs odd) inverts the parity bit on
//     every single frame. Noise inverts it occasionally. From ONE frame the
//     two are literally indistinguishable -- the wire carries the same bits.
//     Only the RATE separates them.
//
//   * Bit-order reversal cannot be detected at all using a payload that is a
//     bit-palindrome (0x00, 0xFF, 0x81, 0x66 ...), because the reversed byte
//     equals the original. The block counts those frames separately and
//     reports them, so a clean result on useless stimulus is never mistaken
//     for evidence of correctness.
// ---------------------------------------------------------------------------
module uart_fail_classifier #(
    parameter MINFRAMES = 4          // evidence required before judging
)(
    input  wire       clk,
    input  wire       rst_n,
    input  wire       clr_i,         // begin a new investigation
    input  wire       go_i,          // strobe: one observed frame is presented
    input  wire [7:0] exp_i,         // what the transmitter sent
    input  wire [7:0] rx_i,          // what the receiver assembled
    input  wire       par_rx_i,      // the parity bit as it arrived
    input  wire       par_odd_i,     // receiver config: 0 = even, 1 = odd
    input  wire       stop_i,        // the stop bit as sampled

    output reg  [7:0] n_frames_o,
    output reg  [7:0] n_stop_bad_o,  // stop sampled as SPACE
    output reg  [7:0] n_rev_o,       // rx == bit-reverse(exp), and differs
    output reg  [7:0] n_data_bad_o,  // wrong, and not a clean reversal
    output reg  [7:0] n_par_bad_o,   // parity bit disagrees with the data
    output reg  [7:0] n_ambig_o,     // payload could not prove bit order
    output wire [2:0] verdict_o
);

    localparam [2:0] V_NEED      = 3'd0,   // not enough frames yet
                     V_OK        = 3'd1,
                     V_FRAMING   = 3'd2,
                     V_REVERSED  = 3'd3,
                     V_DATA      = 3'd4,
                     V_PAR_CFG   = 3'd5,   // every frame -- a configuration fault
                     V_PAR_NOISE = 3'd6;   // some frames -- a signal-integrity fault

    function [7:0] bitrev;
        input [7:0] b;
        integer i;
        begin
            for (i = 0; i < 8; i = i + 1) bitrev[i] = b[7-i];
        end
    endfunction

    wire [7:0] exp_rev  = bitrev(exp_i);
    wire       is_rev   = (rx_i == exp_rev) && (rx_i != exp_i);
    wire       is_ambig = (exp_rev == exp_i);              // palindrome: proves nothing
    wire       par_exp  = par_odd_i ? ~(^rx_i) : (^rx_i);
    wire       par_bad  = (par_rx_i != par_exp);

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            n_frames_o   <= 8'd0; n_stop_bad_o <= 8'd0; n_rev_o    <= 8'd0;
            n_data_bad_o <= 8'd0; n_par_bad_o  <= 8'd0; n_ambig_o  <= 8'd0;
        end else if (clr_i) begin
            n_frames_o   <= 8'd0; n_stop_bad_o <= 8'd0; n_rev_o    <= 8'd0;
            n_data_bad_o <= 8'd0; n_par_bad_o  <= 8'd0; n_ambig_o  <= 8'd0;
        end else if (go_i) begin
            n_frames_o <= n_frames_o + 8'd1;
            if (!stop_i)                       n_stop_bad_o <= n_stop_bad_o + 8'd1;
            if (is_rev)                        n_rev_o      <= n_rev_o      + 8'd1;
            if (is_ambig)                      n_ambig_o    <= n_ambig_o    + 8'd1;
            if ((rx_i != exp_i) && !is_rev)    n_data_bad_o <= n_data_bad_o + 8'd1;
            if (par_bad)                       n_par_bad_o  <= n_par_bad_o  + 8'd1;
        end
    end

    // ---- the verdict, and the order in which evidence is weighed ----------
    // Framing first: if the frame boundary is wrong, every field inside it was
    // sampled at an unknown offset and none of the other evidence is sound.
    // Reversal next, but only when EVERY frame is reversed -- an intermittent
    // "reversal" is not a wiring fault, it is corruption that happened to land
    // on a mirror image. Parity last, split by rate: always-wrong is a
    // configuration fault, sometimes-wrong is a signal-integrity fault.
    assign verdict_o =
        (n_frames_o < MINFRAMES)             ? V_NEED      :
        (n_stop_bad_o != 8'd0)               ? V_FRAMING   :
        (n_rev_o == n_frames_o)              ? V_REVERSED  :
        (n_data_bad_o != 8'd0)               ? V_DATA      :
        (n_par_bad_o == n_frames_o)          ? V_PAR_CFG   :
        (n_par_bad_o != 8'd0)                ? V_PAR_NOISE :
                                               V_OK;

endmodule

SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------------
// uart_fail_classifier -- decide WHICH failure a capture shows.
//
// From software, a reversed byte, a parity-configuration mismatch and a
// genuine framing violation all look the same: "the data is wrong". They are
// not the same fault and they do not have the same fix. This block separates
// them.
//
// The central design decision is that the verdict is accumulated over MANY
// frames, not formed from one. That is forced by the physics, not by taste:
//
//   * A parity CONFIGURATION mismatch (even vs odd) inverts the parity bit on
//     every single frame. Noise inverts it occasionally. From ONE frame the
//     two are literally indistinguishable -- the wire carries the same bits.
//     Only the RATE separates them.
//
//   * Bit-order reversal cannot be detected at all using a payload that is a
//     bit-palindrome (0x00, 0xFF, 0x81, 0x66 ...), because the reversed byte
//     equals the original. The block counts those frames separately and
//     reports them, so a clean result on useless stimulus is never mistaken
//     for evidence of correctness.
// ---------------------------------------------------------------------------
module uart_fail_classifier #(
    parameter int MINFRAMES = 4          // evidence required before judging
)(
    input  logic      clk,
    input  logic      rst_n,
    input  logic      clr_i,         // begin a new investigation
    input  logic      go_i,          // strobe: one observed frame is presented
    input  logic [7:0] exp_i,         // what the transmitter sent
    input  logic [7:0] rx_i,          // what the receiver assembled
    input  logic      par_rx_i,      // the parity bit as it arrived
    input  logic      par_odd_i,     // receiver config: 0 = even, 1 = odd
    input  logic      stop_i,        // the stop bit as sampled

    output logic [7:0] n_frames_o,
    output logic [7:0] n_stop_bad_o,  // stop sampled as SPACE
    output logic [7:0] n_rev_o,       // rx == bit-reverse(exp), and differs
    output logic [7:0] n_data_bad_o,  // wrong, and not a clean reversal
    output logic [7:0] n_par_bad_o,   // parity bit disagrees with the data
    output logic [7:0] n_ambig_o,     // payload could not prove bit order
    output logic [2:0] verdict_o
);

    localparam [2:0] V_NEED      = 3'd0,   // not enough frames yet
                     V_OK        = 3'd1,
                     V_FRAMING   = 3'd2,
                     V_REVERSED  = 3'd3,
                     V_DATA      = 3'd4,
                     V_PAR_CFG   = 3'd5,   // every frame -- a configuration fault
                     V_PAR_NOISE = 3'd6;   // some frames -- a signal-integrity fault

    function automatic logic [7:0] bitrev(input logic [7:0] b);
        int i;
        begin
            for (i = 0; i < 8; i = i + 1) bitrev[i] = b[7-i];
        end
    endfunction

    wire  [7:0] exp_rev  = bitrev(exp_i);
    wire       is_rev   = (rx_i == exp_rev) && (rx_i != exp_i);
    wire       is_ambig = (exp_rev == exp_i);              // palindrome: proves nothing
    wire       par_exp  = par_odd_i ? ~(^rx_i) : (^rx_i);
    wire       par_bad  = (par_rx_i != par_exp);

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            n_frames_o   <= 8'd0; n_stop_bad_o <= 8'd0; n_rev_o    <= 8'd0;
            n_data_bad_o <= 8'd0; n_par_bad_o  <= 8'd0; n_ambig_o  <= 8'd0;
        end else if (clr_i) begin
            n_frames_o   <= 8'd0; n_stop_bad_o <= 8'd0; n_rev_o    <= 8'd0;
            n_data_bad_o <= 8'd0; n_par_bad_o  <= 8'd0; n_ambig_o  <= 8'd0;
        end else if (go_i) begin
            n_frames_o <= n_frames_o + 8'd1;
            if (!stop_i)                       n_stop_bad_o <= n_stop_bad_o + 8'd1;
            if (is_rev)                        n_rev_o      <= n_rev_o      + 8'd1;
            if (is_ambig)                      n_ambig_o    <= n_ambig_o    + 8'd1;
            if ((rx_i != exp_i) && !is_rev)    n_data_bad_o <= n_data_bad_o + 8'd1;
            if (par_bad)                       n_par_bad_o  <= n_par_bad_o  + 8'd1;
        end
    end

    // ---- the verdict, and the order in which evidence is weighed ----------
    // Framing first: if the frame boundary is wrong, every field inside it was
    // sampled at an unknown offset and none of the other evidence is sound.
    // Reversal next, but only when EVERY frame is reversed -- an intermittent
    // "reversal" is not a wiring fault, it is corruption that happened to land
    // on a mirror image. Parity last, split by rate: always-wrong is a
    // configuration fault, sometimes-wrong is a signal-integrity fault.
    assign verdict_o =
        (n_frames_o < MINFRAMES)             ? V_NEED      :
        (n_stop_bad_o != 8'd0)               ? V_FRAMING   :
        (n_rev_o == n_frames_o)              ? V_REVERSED  :
        (n_data_bad_o != 8'd0)               ? V_DATA      :
        (n_par_bad_o == n_frames_o)          ? V_PAR_CFG   :
        (n_par_bad_o != 8'd0)                ? V_PAR_NOISE :
                                               V_OK;

endmodule

VHDL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- ---------------------------------------------------------------------------
-- uart_fail_classifier -- decide WHICH failure a capture shows.
--
-- From software, a reversed byte, a parity-configuration mismatch and a
-- genuine framing violation all look the same: "the data is wrong". They are
-- not the same fault and they do not have the same fix. This block separates
-- them.
--
-- The central design decision is that the verdict is accumulated over MANY
-- frames, not formed from one. That is forced by the physics, not by taste:
--
--   * A parity CONFIGURATION mismatch (even vs odd) inverts the parity bit on
--     every single frame. Noise inverts it occasionally. From ONE frame the
--     two are literally indistinguishable -- the wire carries the same bits.
--     Only the RATE separates them.
--
--   * Bit-order reversal cannot be detected at all using a payload that is a
--     bit-palindrome (0x00, 0xFF, 0x81, 0xA5 ...), because the reversed byte
--     equals the original. The block counts those frames separately and
--     reports them, so a clean result on useless stimulus is never mistaken
--     for evidence of correctness.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity uart_fail_classifier is
    generic (
        MINFRAMES : natural := 4               -- evidence required before judging
    );
    port (
        clk          : in  std_logic;
        rst_n        : in  std_logic;
        clr_i        : in  std_logic;                     -- begin a new investigation
        go_i         : in  std_logic;                     -- one observed frame presented
        exp_i        : in  std_logic_vector(7 downto 0);  -- what the transmitter sent
        rx_i         : in  std_logic_vector(7 downto 0);  -- what the receiver assembled
        par_rx_i     : in  std_logic;                     -- the parity bit as it arrived
        par_odd_i    : in  std_logic;                     -- config: '0' = even, '1' = odd
        stop_i       : in  std_logic;                     -- the stop bit as sampled

        n_frames_o   : out unsigned(7 downto 0);
        n_stop_bad_o : out unsigned(7 downto 0);          -- stop sampled as SPACE
        n_rev_o      : out unsigned(7 downto 0);          -- rx = mirror(exp), and differs
        n_data_bad_o : out unsigned(7 downto 0);          -- wrong, not a clean reversal
        n_par_bad_o  : out unsigned(7 downto 0);          -- parity disagrees with data
        n_ambig_o    : out unsigned(7 downto 0);          -- payload could not prove order
        verdict_o    : out std_logic_vector(2 downto 0)
    );
end entity uart_fail_classifier;

architecture rtl of uart_fail_classifier is

    constant V_NEED      : std_logic_vector(2 downto 0) := "000";  -- not enough frames
    constant V_OK        : std_logic_vector(2 downto 0) := "001";
    constant V_FRAMING   : std_logic_vector(2 downto 0) := "010";
    constant V_REVERSED  : std_logic_vector(2 downto 0) := "011";
    constant V_DATA      : std_logic_vector(2 downto 0) := "100";
    constant V_PAR_CFG   : std_logic_vector(2 downto 0) := "101";  -- every frame
    constant V_PAR_NOISE : std_logic_vector(2 downto 0) := "110";  -- some frames

    function bitrev (b : std_logic_vector(7 downto 0))
        return std_logic_vector is
        variable r : std_logic_vector(7 downto 0);
    begin
        for i in 0 to 7 loop
            r(i) := b(7-i);
        end loop;
        return r;
    end function;

    function xor_all (b : std_logic_vector(7 downto 0)) return std_logic is
        variable r : std_logic := '0';
    begin
        for i in 0 to 7 loop
            r := r xor b(i);
        end loop;
        return r;
    end function;

    signal exp_rev  : std_logic_vector(7 downto 0);
    signal is_rev   : std_logic;
    signal is_ambig : std_logic;
    signal par_exp  : std_logic;
    signal par_bad  : std_logic;

    -- outputs mirrored internally: an entity may not read its own outputs
    signal n_frames, n_stop_bad, n_rev, n_data_bad, n_par_bad, n_ambig
        : unsigned(7 downto 0) := (others => '0');

begin

    exp_rev  <= bitrev(exp_i);
    is_rev   <= '1' when (rx_i = exp_rev and rx_i /= exp_i) else '0';
    is_ambig <= '1' when exp_rev = exp_i else '0';        -- palindrome: proves nothing
    par_exp  <= not xor_all(rx_i) when par_odd_i = '1' else xor_all(rx_i);
    par_bad  <= '1' when par_rx_i /= par_exp else '0';

    n_frames_o   <= n_frames;
    n_stop_bad_o <= n_stop_bad;
    n_rev_o      <= n_rev;
    n_data_bad_o <= n_data_bad;
    n_par_bad_o  <= n_par_bad;
    n_ambig_o    <= n_ambig;

    process (clk, rst_n)
    begin
        if rst_n = '0' then
            n_frames   <= (others => '0'); n_stop_bad <= (others => '0');
            n_rev      <= (others => '0'); n_data_bad <= (others => '0');
            n_par_bad  <= (others => '0'); n_ambig    <= (others => '0');
        elsif rising_edge(clk) then
            if clr_i = '1' then
                n_frames   <= (others => '0'); n_stop_bad <= (others => '0');
                n_rev      <= (others => '0'); n_data_bad <= (others => '0');
                n_par_bad  <= (others => '0'); n_ambig    <= (others => '0');
            elsif go_i = '1' then
                n_frames <= n_frames + 1;
                if stop_i = '0'  then n_stop_bad <= n_stop_bad + 1; end if;
                if is_rev = '1'  then n_rev      <= n_rev      + 1; end if;
                if is_ambig = '1' then n_ambig   <= n_ambig    + 1; end if;
                if rx_i /= exp_i and is_rev = '0' then
                    n_data_bad <= n_data_bad + 1;
                end if;
                if par_bad = '1' then n_par_bad <= n_par_bad + 1; end if;
            end if;
        end if;
    end process;

    -- ---- the verdict, and the order in which evidence is weighed ----------
    -- Framing first: if the frame boundary is wrong, every field inside it was
    -- sampled at an unknown offset and none of the other evidence is sound.
    -- Reversal next, but only when EVERY frame is reversed -- an intermittent
    -- "reversal" is not a wiring fault, it is corruption that happened to land
    -- on a mirror image. Parity last, split by rate: always-wrong is a
    -- configuration fault, sometimes-wrong is a signal-integrity fault.
    verdict_o <= V_NEED      when n_frames < to_unsigned(MINFRAMES, 8) else
                 V_FRAMING   when n_stop_bad /= 0                      else
                 V_REVERSED  when n_rev = n_frames                     else
                 V_DATA      when n_data_bad /= 0                      else
                 V_PAR_CFG   when n_par_bad = n_frames                 else
                 V_PAR_NOISE when n_par_bad /= 0                       else
                 V_OK;

end architecture rtl;

The n_ambig_o counter is the §2 result made operational. It counts frames whose expected payload is its own mirror image — frames that, whatever else they prove, cannot prove anything about bit order. A clean run with n_ambig_o equal to n_frames_o is not evidence that the bit order is right; it is evidence that the stimulus was incapable of testing it.

The precedence order

The verdict weighs evidence in a fixed order, and the order encodes real reasoning rather than convenience:

  1. Framing first. If the stop bit is not where it should be, the frame boundary is wrong, every field inside it was sampled at an unknown offset, and no other observation from that frame is sound. A capture that is both framing-broken and apparently reversed must report framing, because the "reversal" may be an artefact of sampling the wrong bits.
  2. Reversal next, but only if every frame shows it. A wiring or configuration fault is deterministic. An intermittent "reversal" is corruption that happened to land on a mirror image, which is a different problem.
  3. Data corruption, when bytes are wrong in a way that is not a clean mirror.
  4. Parity last, split by rate — the §3 distinction.

5. What the Classifier Measured

Ten scenarios, eight frames each:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  scenario                          verdict        supporting counters
  -------------------------------   ------------   ------------------------
  T1   healthy traffic              OK             par_bad 0/8
  T2   parity wrong on every frame  PARITY_CFG     par_bad 8/8
  T3   parity wrong on one frame    PARITY_NOISE   par_bad 1/8
  T4   reversed, probed with 0x55   REVERSED       rev 8/8, par_bad 0
  T5   reversed, probed with 0xA5   OK             rev 0, ambiguous 8/8
  T6   one bad stop bit             FRAMING        stop_bad 1
  T7   thin evidence                NEED_MORE      then PARITY_CFG at frame 4
  T8   corruption, not a mirror     DATA           data_bad 8, rev 0
  T10  reversed AND bad stop bits   FRAMING        rev 8/8, stop_bad 8/8

T4 and T5 are the same physical fault. The wire is assembling bytes backwards in both. With 0x55 the classifier detects it on all eight frames; with 0xA5 it detects nothing and the capture is spotless — while flagging all eight frames as ambiguous, which is the only honest thing it can say.

The same reversed wire, probed with two different payloads

11 cycles
A comparison of two frames sent over a link whose receiver assembles bytes in the wrong bit order. In the upper case the transmitter sends fifty-five hexadecimal and the receiver assembles A A hexadecimal, a clearly different value, so the fault is detected. In the lower case the transmitter sends A five hexadecimal, and because A five is unchanged by bit reversal the receiver assembles A five as well. The received value matches the sent value exactly, so the identical fault produces no evidence at all. The verdict row shows reversed for the first case and clean for the second.0x55 mirrors to 0xAA — detected0x55 mirrors to 0xAA —detected0xA5 mirrors to itself — invisible0xA5 mirrors to itself —invisibleintervalstartd0d1d2d3d4d5d6d7stopidlesent 0x55got 0xAAsent 0xA5got 0xA5t0t1t2t3t4t5t6t7t8t9t10

T10 is the precedence rule under test. Every frame is reversed and every stop bit is SPACE. The reversal counter still records 8 of 8 — the evidence is not discarded — but the verdict is FRAMING, because until the frame boundary is trusted the contents cannot be.

That test exists because of a mutation. It was not in the original suite, and §7 explains how its absence was found.

6. The Testbench

Each test presents a stream describing one physical fault, rather than a single frame, because §3 established that a single frame cannot carry the answer.

Verilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
// ---------------------------------------------------------------------------
// Testbench for uart_fail_classifier.
//
// Each test presents a STREAM of frames describing one physical fault, and
// asserts the verdict the evidence supports. The interesting cases are the two
// where the honest answer is "this stimulus cannot tell you":
//
//   T5  bit-order reversal probed with 0xA5 -- a bit-palindrome. The reversed
//       byte equals the original, so the capture is clean and proves nothing.
//   T3  parity wrong on SOME frames, which is noise, versus T2 where it is
//       wrong on EVERY frame, which is a configuration fault. One frame cannot
//       separate these; a rate can.
// ---------------------------------------------------------------------------
module tb_uart_fail_classifier;

    localparam MINF = 4;

    localparam [2:0] V_NEED      = 3'd0, V_OK       = 3'd1, V_FRAMING   = 3'd2,
                     V_REVERSED  = 3'd3, V_DATA     = 3'd4, V_PAR_CFG   = 3'd5,
                     V_PAR_NOISE = 3'd6;

    reg        clk = 1'b0;
    reg        rst_n = 1'b0;
    reg        clr = 1'b0;
    reg        go = 1'b0;
    reg  [7:0] exp_b = 8'd0, rx_b = 8'd0;
    reg        par_rx = 1'b0, par_odd = 1'b0, stop_b = 1'b1;

    wire [7:0] n_frames, n_stop_bad, n_rev, n_data_bad, n_par_bad, n_ambig;
    wire [2:0] verdict;

    integer checks = 0;
    integer fails  = 0;

    always #5 clk = ~clk;

    uart_fail_classifier #(.MINFRAMES(MINF)) dut (
        .clk(clk), .rst_n(rst_n), .clr_i(clr), .go_i(go),
        .exp_i(exp_b), .rx_i(rx_b), .par_rx_i(par_rx),
        .par_odd_i(par_odd), .stop_i(stop_b),
        .n_frames_o(n_frames), .n_stop_bad_o(n_stop_bad), .n_rev_o(n_rev),
        .n_data_bad_o(n_data_bad), .n_par_bad_o(n_par_bad), .n_ambig_o(n_ambig),
        .verdict_o(verdict));

    function [7:0] bitrev;
        input [7:0] b;
        integer i;
        begin
            for (i = 0; i < 8; i = i + 1) bitrev[i] = b[7-i];
        end
    endfunction

    function [12*8:1] vname;
        input [2:0] v;
        begin
            case (v)
                V_NEED:      vname = "NEED_MORE";
                V_OK:        vname = "OK";
                V_FRAMING:   vname = "FRAMING";
                V_REVERSED:  vname = "REVERSED";
                V_DATA:      vname = "DATA";
                V_PAR_CFG:   vname = "PARITY_CFG";
                V_PAR_NOISE: vname = "PARITY_NOISE";
                default:     vname = "?";
            endcase
        end
    endfunction

    task chk;
        input [255:0] name;
        input integer got;
        input integer exp;
        begin
            checks = checks + 1;
            if (got !== exp) begin
                fails = fails + 1;
                $display("  FAIL %0s: got %0d expected %0d", name, got, exp);
            end
        end
    endtask

    task present;
        input [7:0] e;
        input [7:0] r;
        input       p;
        input       po;
        input       s;
        begin
            @(negedge clk);
            exp_b = e; rx_b = r; par_rx = p; par_odd = po; stop_b = s; go = 1'b1;
            @(posedge clk);
            @(negedge clk);
            go = 1'b0;
        end
    endtask

    task restart;
        begin
            @(negedge clk); clr = 1'b1;
            @(posedge clk);
            @(negedge clk); clr = 1'b0;
            @(posedge clk);
        end
    endtask

    integer i;

    initial begin
        rst_n = 1'b0;
        repeat (3) @(posedge clk);
        @(negedge clk); rst_n = 1'b1;
        repeat (2) @(posedge clk);

        // ---------------- T1: healthy traffic -----------------------------
        restart;
        for (i = 0; i < 8; i = i + 1) present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T1 healthy          : verdict=%0s frames=%0d", vname(verdict), n_frames);
        chk("T1 verdict OK", verdict, V_OK);
        chk("T1 no parity complaints", n_par_bad, 0);

        // ---------------- T2: parity wrong on EVERY frame -----------------
        // The transmitter is using odd parity; the receiver is configured even.
        restart;
        for (i = 0; i < 8; i = i + 1) present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        $display("T2 parity every fr. : verdict=%0s par_bad=%0d/%0d",
                 vname(verdict), n_par_bad, n_frames);
        chk("T2 verdict PARITY_CFG", verdict, V_PAR_CFG);
        chk("T2 every frame flagged", n_par_bad, n_frames);

        // ---------------- T3: parity wrong on ONE frame in eight ----------
        restart;
        for (i = 0; i < 8; i = i + 1) begin
            if (i == 3) present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
            else        present(8'h55, 8'h55,  (^(8'h55)), 1'b0, 1'b1);
        end
        #1;
        $display("T3 parity one frame : verdict=%0s par_bad=%0d/%0d",
                 vname(verdict), n_par_bad, n_frames);
        chk("T3 verdict PARITY_NOISE", verdict, V_PAR_NOISE);
        chk("T3 exactly one frame flagged", n_par_bad, 1);

        // ---------------- T4: every byte arrives bit-reversed -------------
        // Note the parity bit stays CONSISTENT: XOR is order-independent, so
        // parity cannot see a reversal at all.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, bitrev(8'h55), ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T4 reversed (0x55)  : verdict=%0s rev=%0d/%0d par_bad=%0d",
                 vname(verdict), n_rev, n_frames, n_par_bad);
        chk("T4 verdict REVERSED", verdict, V_REVERSED);
        chk("T4 every frame reversed", n_rev, n_frames);
        chk("T4 parity did NOT notice", n_par_bad, 0);

        // ---------------- T5: the same fault, probed with 0xA5 ------------
        // 0xA5 is a bit-palindrome. Reversed, it is still 0xA5. The wire is
        // just as broken as in T4 and the capture is spotless.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'hA5, bitrev(8'hA5), ^(8'hA5), 1'b0, 1'b1);
        #1;
        $display("T5 reversed (0xA5)  : verdict=%0s rev=%0d ambiguous=%0d/%0d",
                 vname(verdict), n_rev, n_ambig, n_frames);
        chk("T5 0xA5 is its own mirror", bitrev(8'hA5), 8'hA5);
        chk("T5 the broken wire looks clean", verdict, V_OK);
        chk("T5 no reversal was detectable", n_rev, 0);
        chk("T5 every frame flagged ambiguous", n_ambig, n_frames);

        // ---------------- T6: one framing error outranks everything -------
        restart;
        for (i = 0; i < 8; i = i + 1) begin
            if (i == 5) present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b0);   // stop = SPACE
            else        present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b1);
        end
        #1;
        $display("T6 one framing err  : verdict=%0s stop_bad=%0d",
                 vname(verdict), n_stop_bad);
        chk("T6 verdict FRAMING", verdict, V_FRAMING);
        chk("T6 one bad stop", n_stop_bad, 1);

        // ---------------- T7: refuse to judge on thin evidence ------------
        restart;
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        chk("T7 one frame is not evidence", verdict, V_NEED);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        chk("T7 three frames still not enough", verdict, V_NEED);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        $display("T7 fourth frame     : verdict=%0s", vname(verdict));
        chk("T7 the fourth frame decides it", verdict, V_PAR_CFG);

        // ---------------- T8: corruption that is not a clean mirror -------
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, 8'h5D, ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T8 plain corruption : verdict=%0s data_bad=%0d rev=%0d",
                 vname(verdict), n_data_bad, n_rev);
        chk("T8 verdict DATA", verdict, V_DATA);
        chk("T8 not mistaken for a reversal", n_rev, 0);

        // ---------------- T10: both faults at once ------------------------
        // Every frame is reversed AND every stop bit is SPACE. The evidence for
        // the reversal is still recorded, but the VERDICT must be FRAMING: if
        // the frame boundary is wrong, the bits inside it were sampled at an
        // unknown offset, so "it looks reversed" is not yet a sound conclusion.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, bitrev(8'h55), ^(8'h55), 1'b0, 1'b0);
        #1;
        $display("T10 reversed + framing: verdict=%0s rev=%0d/%0d stop_bad=%0d",
                 vname(verdict), n_rev, n_frames, n_stop_bad);
        chk("T10 framing outranks reversal", verdict, V_FRAMING);
        chk("T10 the reversal is still recorded", n_rev, n_frames);

        // ---------------- T9: clear really clears -------------------------
        restart;
        #1;
        chk("T9 frames cleared",   n_frames, 0);
        chk("T9 verdict reset",    verdict,  V_NEED);
        chk("T9 counters cleared", n_data_bad + n_par_bad + n_rev + n_stop_bad + n_ambig, 0);

        $display("");
        $display("== %0d checks, %0d failures ==", checks, fails);
        if (fails == 0) $display("   RESULT: ALL VERILOG FAIL-CLASSIFIER TESTS PASSED");
        else            $display("   RESULT: %0d FAILURE(S)", fails);
        $finish;
    end

endmodule

SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
// ---------------------------------------------------------------------------
// Testbench for uart_fail_classifier.
//
// Each test presents a STREAM of frames describing one physical fault, and
// asserts the verdict the evidence supports. The interesting cases are the two
// where the honest answer is "this stimulus cannot tell you":
//
//   T5  bit-order reversal probed with 0xA5 -- a bit-palindrome. The reversed
//       byte equals the original, so the capture is clean and proves nothing.
//   T3  parity wrong on SOME frames, which is noise, versus T2 where it is
//       wrong on EVERY frame, which is a configuration fault. One frame cannot
//       separate these; a rate can.
// ---------------------------------------------------------------------------
module tb_uart_fail_classifier;

    localparam MINF = 4;

    localparam [2:0] V_NEED      = 3'd0, V_OK       = 3'd1, V_FRAMING   = 3'd2,
                     V_REVERSED  = 3'd3, V_DATA     = 3'd4, V_PAR_CFG   = 3'd5,
                     V_PAR_NOISE = 3'd6;

    logic        clk = 1'b0;
    logic        rst_n = 1'b0;
    logic        clr = 1'b0;
    logic        go = 1'b0;
    logic  [7:0] exp_b = 8'd0, rx_b = 8'd0;
    logic        par_rx = 1'b0, par_odd = 1'b0, stop_b = 1'b1;

    logic [7:0] n_frames, n_stop_bad, n_rev, n_data_bad, n_par_bad, n_ambig;
    logic [2:0] verdict;

    integer checks = 0;
    integer fails  = 0;

    always #5 clk = ~clk;

    uart_fail_classifier #(.MINFRAMES(MINF)) dut (
        .clk(clk), .rst_n(rst_n), .clr_i(clr), .go_i(go),
        .exp_i(exp_b), .rx_i(rx_b), .par_rx_i(par_rx),
        .par_odd_i(par_odd), .stop_i(stop_b),
        .n_frames_o(n_frames), .n_stop_bad_o(n_stop_bad), .n_rev_o(n_rev),
        .n_data_bad_o(n_data_bad), .n_par_bad_o(n_par_bad), .n_ambig_o(n_ambig),
        .verdict_o(verdict));

    function automatic logic [7:0] bitrev(input logic [7:0] b);
        int i;
        begin
            for (i = 0; i < 8; i = i + 1) bitrev[i] = b[7-i];
        end
    endfunction

    function automatic string vname(input logic [2:0] v);
        begin
            case (v)
                V_NEED:      vname = "NEED_MORE";
                V_OK:        vname = "OK";
                V_FRAMING:   vname = "FRAMING";
                V_REVERSED:  vname = "REVERSED";
                V_DATA:      vname = "DATA";
                V_PAR_CFG:   vname = "PARITY_CFG";
                V_PAR_NOISE: vname = "PARITY_NOISE";
                default:     vname = "?";
            endcase
        end
    endfunction

    task automatic chk(input string name, input int got, input int exp);
        begin
            checks = checks + 1;
            if (got !== exp) begin
                fails = fails + 1;
                $display("  FAIL %0s: got %0d expected %0d", name, got, exp);
            end
        end
    endtask

    task automatic present(input logic [7:0] e, input logic [7:0] r,
                           input logic p, input logic po, input logic s);
        begin
            @(negedge clk);
            exp_b = e; rx_b = r; par_rx = p; par_odd = po; stop_b = s; go = 1'b1;
            @(posedge clk);
            @(negedge clk);
            go = 1'b0;
        end
    endtask

    task automatic restart();
        begin
            @(negedge clk); clr = 1'b1;
            @(posedge clk);
            @(negedge clk); clr = 1'b0;
            @(posedge clk);
        end
    endtask

    integer i;

    initial begin
        rst_n = 1'b0;
        repeat (3) @(posedge clk);
        @(negedge clk); rst_n = 1'b1;
        repeat (2) @(posedge clk);

        // ---------------- T1: healthy traffic -----------------------------
        restart;
        for (i = 0; i < 8; i = i + 1) present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T1 healthy          : verdict=%0s frames=%0d", vname(verdict), n_frames);
        chk("T1 verdict OK", verdict, V_OK);
        chk("T1 no parity complaints", n_par_bad, 0);

        // ---------------- T2: parity wrong on EVERY frame -----------------
        // The transmitter is using odd parity; the receiver is configured even.
        restart;
        for (i = 0; i < 8; i = i + 1) present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        $display("T2 parity every fr. : verdict=%0s par_bad=%0d/%0d",
                 vname(verdict), n_par_bad, n_frames);
        chk("T2 verdict PARITY_CFG", verdict, V_PAR_CFG);
        chk("T2 every frame flagged", n_par_bad, n_frames);

        // ---------------- T3: parity wrong on ONE frame in eight ----------
        restart;
        for (i = 0; i < 8; i = i + 1) begin
            if (i == 3) present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
            else        present(8'h55, 8'h55,  (^(8'h55)), 1'b0, 1'b1);
        end
        #1;
        $display("T3 parity one frame : verdict=%0s par_bad=%0d/%0d",
                 vname(verdict), n_par_bad, n_frames);
        chk("T3 verdict PARITY_NOISE", verdict, V_PAR_NOISE);
        chk("T3 exactly one frame flagged", n_par_bad, 1);

        // ---------------- T4: every byte arrives bit-reversed -------------
        // Note the parity bit stays CONSISTENT: XOR is order-independent, so
        // parity cannot see a reversal at all.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, bitrev(8'h55), ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T4 reversed (0x55)  : verdict=%0s rev=%0d/%0d par_bad=%0d",
                 vname(verdict), n_rev, n_frames, n_par_bad);
        chk("T4 verdict REVERSED", verdict, V_REVERSED);
        chk("T4 every frame reversed", n_rev, n_frames);
        chk("T4 parity did NOT notice", n_par_bad, 0);

        // ---------------- T5: the same fault, probed with 0xA5 ------------
        // 0xA5 is a bit-palindrome. Reversed, it is still 0xA5. The logic is
        // just as broken as in T4 and the capture is spotless.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'hA5, bitrev(8'hA5), ^(8'hA5), 1'b0, 1'b1);
        #1;
        $display("T5 reversed (0xA5)  : verdict=%0s rev=%0d ambiguous=%0d/%0d",
                 vname(verdict), n_rev, n_ambig, n_frames);
        chk("T5 0xA5 is its own mirror", bitrev(8'hA5), 8'hA5);
        chk("T5 the broken logic looks clean", verdict, V_OK);
        chk("T5 no reversal was detectable", n_rev, 0);
        chk("T5 every frame flagged ambiguous", n_ambig, n_frames);

        // ---------------- T6: one framing error outranks everything -------
        restart;
        for (i = 0; i < 8; i = i + 1) begin
            if (i == 5) present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b0);   // stop = SPACE
            else        present(8'h55, 8'h55, ^(8'h55), 1'b0, 1'b1);
        end
        #1;
        $display("T6 one framing err  : verdict=%0s stop_bad=%0d",
                 vname(verdict), n_stop_bad);
        chk("T6 verdict FRAMING", verdict, V_FRAMING);
        chk("T6 one bad stop", n_stop_bad, 1);

        // ---------------- T7: refuse to judge on thin evidence ------------
        restart;
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        chk("T7 one frame is not evidence", verdict, V_NEED);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        chk("T7 three frames still not enough", verdict, V_NEED);
        present(8'h55, 8'h55, ~(^(8'h55)), 1'b0, 1'b1);
        #1;
        $display("T7 fourth frame     : verdict=%0s", vname(verdict));
        chk("T7 the fourth frame decides it", verdict, V_PAR_CFG);

        // ---------------- T8: corruption that is not a clean mirror -------
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, 8'h5D, ^(8'h55), 1'b0, 1'b1);
        #1;
        $display("T8 plain corruption : verdict=%0s data_bad=%0d rev=%0d",
                 vname(verdict), n_data_bad, n_rev);
        chk("T8 verdict DATA", verdict, V_DATA);
        chk("T8 not mistaken for a reversal", n_rev, 0);

        // ---------------- T10: both faults at once ------------------------
        // Every frame is reversed AND every stop bit is SPACE. The evidence for
        // the reversal is still recorded, but the VERDICT must be FRAMING: if
        // the frame boundary is wrong, the bits inside it were sampled at an
        // unknown offset, so "it looks reversed" is not yet a sound conclusion.
        restart;
        for (i = 0; i < 8; i = i + 1)
            present(8'h55, bitrev(8'h55), ^(8'h55), 1'b0, 1'b0);
        #1;
        $display("T10 reversed + framing: verdict=%0s rev=%0d/%0d stop_bad=%0d",
                 vname(verdict), n_rev, n_frames, n_stop_bad);
        chk("T10 framing outranks reversal", verdict, V_FRAMING);
        chk("T10 the reversal is still recorded", n_rev, n_frames);

        // ---------------- T9: clear really clears -------------------------
        restart;
        #1;
        chk("T9 frames cleared",   n_frames, 0);
        chk("T9 verdict reset",    verdict,  V_NEED);
        chk("T9 counters cleared", n_data_bad + n_par_bad + n_rev + n_stop_bad + n_ambig, 0);

        $display("");
        $display("== %0d checks, %0d failures ==", checks, fails);
        if (fails == 0) $display("   RESULT: ALL SYSTEMVERILOG FAIL-CLASSIFIER TESTS PASSED");
        else            $display("   RESULT: %0d FAILURE(S)", fails);
        $finish;
    end

endmodule

VHDL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- ---------------------------------------------------------------------------
-- Testbench for uart_fail_classifier.
--
-- Each test presents a STREAM of frames describing one physical fault, and
-- asserts the verdict the evidence supports. The interesting cases are the two
-- where the honest answer is "this stimulus cannot tell you":
--
--   T5  bit-order reversal probed with 0xA5 -- a bit-palindrome. The reversed
--       byte equals the original, so the capture is clean and proves nothing.
--   T3  parity wrong on SOME frames, which is noise, versus T2 where it is
--       wrong on EVERY frame, which is a configuration fault. One frame cannot
--       separate these; a rate can.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb_uart_fail_classifier is
end entity tb_uart_fail_classifier;

architecture sim of tb_uart_fail_classifier is

    constant MINF : natural := 4;
    constant TCLK : time    := 10 ns;

    constant V_NEED      : std_logic_vector(2 downto 0) := "000";
    constant V_OK        : std_logic_vector(2 downto 0) := "001";
    constant V_FRAMING   : std_logic_vector(2 downto 0) := "010";
    constant V_REVERSED  : std_logic_vector(2 downto 0) := "011";
    constant V_DATA      : std_logic_vector(2 downto 0) := "100";
    constant V_PAR_CFG   : std_logic_vector(2 downto 0) := "101";
    constant V_PAR_NOISE : std_logic_vector(2 downto 0) := "110";

    signal clk     : std_logic := '0';
    signal rst_n   : std_logic := '0';
    signal clr     : std_logic := '0';
    signal go      : std_logic := '0';
    signal exp_b   : std_logic_vector(7 downto 0) := (others => '0');
    signal rx_b    : std_logic_vector(7 downto 0) := (others => '0');
    signal par_rx  : std_logic := '0';
    signal par_odd : std_logic := '0';
    signal stop_b  : std_logic := '1';
    signal done    : boolean   := false;

    signal n_frames, n_stop_bad, n_rev, n_data_bad, n_par_bad, n_ambig
        : unsigned(7 downto 0);
    signal verdict : std_logic_vector(2 downto 0);

    function bitrev (b : std_logic_vector(7 downto 0))
        return std_logic_vector is
        variable r : std_logic_vector(7 downto 0);
    begin
        for i in 0 to 7 loop
            r(i) := b(7-i);
        end loop;
        return r;
    end function;

    function xor_all (b : std_logic_vector(7 downto 0)) return std_logic is
        variable r : std_logic := '0';
    begin
        for i in 0 to 7 loop
            r := r xor b(i);
        end loop;
        return r;
    end function;

    function vname (v : std_logic_vector(2 downto 0)) return string is
    begin
        case v is
            when V_NEED      => return "NEED_MORE";
            when V_OK        => return "OK";
            when V_FRAMING   => return "FRAMING";
            when V_REVERSED  => return "REVERSED";
            when V_DATA      => return "DATA";
            when V_PAR_CFG   => return "PARITY_CFG";
            when V_PAR_NOISE => return "PARITY_NOISE";
            when others      => return "?";
        end case;
    end function;

begin

    clk <= '0' when done else not clk after TCLK/2;

    dut : entity work.uart_fail_classifier
        generic map (MINFRAMES => MINF)
        port map (clk => clk, rst_n => rst_n, clr_i => clr, go_i => go,
                  exp_i => exp_b, rx_i => rx_b, par_rx_i => par_rx,
                  par_odd_i => par_odd, stop_i => stop_b,
                  n_frames_o => n_frames, n_stop_bad_o => n_stop_bad,
                  n_rev_o => n_rev, n_data_bad_o => n_data_bad,
                  n_par_bad_o => n_par_bad, n_ambig_o => n_ambig,
                  verdict_o => verdict);

    stim : process
        variable checks, fails : integer := 0;

        procedure chk (name : string; got : integer; exp : integer) is
        begin
            checks := checks + 1;
            if got /= exp then
                fails := fails + 1;
                report "  FAIL " & name & ": got " & integer'image(got) &
                       " expected " & integer'image(exp) severity error;
            end if;
        end procedure;

        procedure present (e : std_logic_vector(7 downto 0);
                           r : std_logic_vector(7 downto 0);
                           p : std_logic; po : std_logic; s : std_logic) is
        begin
            wait until falling_edge(clk);
            exp_b <= e; rx_b <= r; par_rx <= p; par_odd <= po; stop_b <= s;
            go <= '1';
            wait until rising_edge(clk);
            wait until falling_edge(clk);
            go <= '0';
        end procedure;

        procedure restart is
        begin
            wait until falling_edge(clk); clr <= '1';
            wait until rising_edge(clk);
            wait until falling_edge(clk); clr <= '0';
            wait until rising_edge(clk);
        end procedure;

    begin
        rst_n <= '0';
        for i in 0 to 2 loop wait until rising_edge(clk); end loop;
        wait until falling_edge(clk); rst_n <= '1';
        for i in 0 to 1 loop wait until rising_edge(clk); end loop;

        -- ---------------- T1: healthy traffic -----------------------------
        restart;
        for i in 0 to 7 loop
            present(x"55", x"55", xor_all(x"55"), '0', '1');
        end loop;
        wait for 1 ns;
        report "T1 healthy          : verdict=" & vname(verdict) &
               " frames=" & integer'image(to_integer(n_frames));
        chk("T1 verdict OK", to_integer(unsigned(verdict)), to_integer(unsigned(V_OK)));
        chk("T1 no parity complaints", to_integer(n_par_bad), 0);

        -- ---------------- T2: parity wrong on EVERY frame -----------------
        -- The transmitter is using odd parity; the receiver is configured even.
        restart;
        for i in 0 to 7 loop
            present(x"55", x"55", not xor_all(x"55"), '0', '1');
        end loop;
        wait for 1 ns;
        report "T2 parity every fr. : verdict=" & vname(verdict) &
               " par_bad=" & integer'image(to_integer(n_par_bad)) &
               "/" & integer'image(to_integer(n_frames));
        chk("T2 verdict PARITY_CFG", to_integer(unsigned(verdict)), to_integer(unsigned(V_PAR_CFG)));
        chk("T2 every frame flagged", to_integer(n_par_bad), to_integer(n_frames));

        -- ---------------- T3: parity wrong on ONE frame in eight ----------
        restart;
        for i in 0 to 7 loop
            if i = 3 then present(x"55", x"55", not xor_all(x"55"), '0', '1');
            else          present(x"55", x"55",     xor_all(x"55"), '0', '1');
            end if;
        end loop;
        wait for 1 ns;
        report "T3 parity one frame : verdict=" & vname(verdict) &
               " par_bad=" & integer'image(to_integer(n_par_bad)) &
               "/" & integer'image(to_integer(n_frames));
        chk("T3 verdict PARITY_NOISE", to_integer(unsigned(verdict)), to_integer(unsigned(V_PAR_NOISE)));
        chk("T3 exactly one frame flagged", to_integer(n_par_bad), 1);

        -- ---------------- T4: every byte arrives bit-reversed -------------
        -- Note the parity bit stays CONSISTENT: XOR is order-independent, so
        -- parity cannot see a reversal at all.
        restart;
        for i in 0 to 7 loop
            present(x"55", bitrev(x"55"), xor_all(x"55"), '0', '1');
        end loop;
        wait for 1 ns;
        report "T4 reversed (0x55)  : verdict=" & vname(verdict) &
               " rev=" & integer'image(to_integer(n_rev)) &
               "/" & integer'image(to_integer(n_frames)) &
               " par_bad=" & integer'image(to_integer(n_par_bad));
        chk("T4 verdict REVERSED", to_integer(unsigned(verdict)), to_integer(unsigned(V_REVERSED)));
        chk("T4 every frame reversed", to_integer(n_rev), to_integer(n_frames));
        chk("T4 parity did NOT notice", to_integer(n_par_bad), 0);

        -- ---------------- T5: the same fault, probed with 0xA5 ------------
        -- 0xA5 is a bit-palindrome. Reversed, it is still 0xA5. The wire is
        -- just as broken as in T4 and the capture is spotless.
        restart;
        for i in 0 to 7 loop
            present(x"A5", bitrev(x"A5"), xor_all(x"A5"), '0', '1');
        end loop;
        wait for 1 ns;
        report "T5 reversed (0xA5)  : verdict=" & vname(verdict) &
               " rev=" & integer'image(to_integer(n_rev)) &
               " ambiguous=" & integer'image(to_integer(n_ambig)) &
               "/" & integer'image(to_integer(n_frames));
        if bitrev(x"A5") = x"A5" then chk("T5 0xA5 is its own mirror", 1, 1);
        else                          chk("T5 0xA5 is its own mirror", 0, 1); end if;
        chk("T5 the broken wire looks clean", to_integer(unsigned(verdict)), to_integer(unsigned(V_OK)));
        chk("T5 no reversal was detectable", to_integer(n_rev), 0);
        chk("T5 every frame flagged ambiguous", to_integer(n_ambig), to_integer(n_frames));

        -- ---------------- T6: one framing error outranks everything -------
        restart;
        for i in 0 to 7 loop
            if i = 5 then present(x"55", x"55", xor_all(x"55"), '0', '0');
            else          present(x"55", x"55", xor_all(x"55"), '0', '1');
            end if;
        end loop;
        wait for 1 ns;
        report "T6 one framing err  : verdict=" & vname(verdict) &
               " stop_bad=" & integer'image(to_integer(n_stop_bad));
        chk("T6 verdict FRAMING", to_integer(unsigned(verdict)), to_integer(unsigned(V_FRAMING)));
        chk("T6 one bad stop", to_integer(n_stop_bad), 1);

        -- ---------------- T7: refuse to judge on thin evidence ------------
        restart;
        present(x"55", x"55", not xor_all(x"55"), '0', '1');
        wait for 1 ns;
        chk("T7 one frame is not evidence", to_integer(unsigned(verdict)), to_integer(unsigned(V_NEED)));
        present(x"55", x"55", not xor_all(x"55"), '0', '1');
        present(x"55", x"55", not xor_all(x"55"), '0', '1');
        wait for 1 ns;
        chk("T7 three frames still not enough", to_integer(unsigned(verdict)), to_integer(unsigned(V_NEED)));
        present(x"55", x"55", not xor_all(x"55"), '0', '1');
        wait for 1 ns;
        report "T7 fourth frame     : verdict=" & vname(verdict);
        chk("T7 the fourth frame decides it", to_integer(unsigned(verdict)), to_integer(unsigned(V_PAR_CFG)));

        -- ---------------- T8: corruption that is not a clean mirror -------
        restart;
        for i in 0 to 7 loop
            present(x"55", x"5D", xor_all(x"55"), '0', '1');
        end loop;
        wait for 1 ns;
        report "T8 plain corruption : verdict=" & vname(verdict) &
               " data_bad=" & integer'image(to_integer(n_data_bad)) &
               " rev=" & integer'image(to_integer(n_rev));
        chk("T8 verdict DATA", to_integer(unsigned(verdict)), to_integer(unsigned(V_DATA)));
        chk("T8 not mistaken for a reversal", to_integer(n_rev), 0);

        -- ---------------- T10: both faults at once ------------------------
        -- Every frame is reversed AND every stop bit is SPACE. The evidence for
        -- the reversal is still recorded, but the VERDICT must be FRAMING: if
        -- the frame boundary is wrong, the bits inside it were sampled at an
        -- unknown offset, so "it looks reversed" is not yet a sound conclusion.
        restart;
        for i in 0 to 7 loop
            present(x"55", bitrev(x"55"), xor_all(x"55"), '0', '0');
        end loop;
        wait for 1 ns;
        report "T10 reversed + framing: verdict=" & vname(verdict) &
               " rev=" & integer'image(to_integer(n_rev)) &
               "/" & integer'image(to_integer(n_frames)) &
               " stop_bad=" & integer'image(to_integer(n_stop_bad));
        chk("T10 framing outranks reversal",
            to_integer(unsigned(verdict)), to_integer(unsigned(V_FRAMING)));
        chk("T10 the reversal is still recorded",
            to_integer(n_rev), to_integer(n_frames));

        -- ---------------- T9: clear really clears -------------------------
        restart;
        wait for 1 ns;
        chk("T9 frames cleared", to_integer(n_frames), 0);
        chk("T9 verdict reset",  to_integer(unsigned(verdict)), to_integer(unsigned(V_NEED)));
        chk("T9 counters cleared",
            to_integer(n_data_bad) + to_integer(n_par_bad) + to_integer(n_rev) +
            to_integer(n_stop_bad) + to_integer(n_ambig), 0);

        report "";
        report "== " & integer'image(checks) & " checks, " &
               integer'image(fails) & " failures ==";
        if fails = 0 then
            report "   RESULT: ALL VHDL FAIL-CLASSIFIER TESTS PASSED";
        else
            report "   RESULT: " & integer'image(fails) & " FAILURE(S)" severity error;
        end if;
        done <= true;
        wait;
    end process;

end architecture sim;

7. Proving the Tests Can Fail — and One That Could Not

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  mutation                                             checks failed   verdict
  --------------------------------------------------   -------------   --------
  M6  drop the "and differs" qualifier from is_rev                  2   killed
  M7  test parity noise before parity configuration                 2   killed
  M8  weigh reversal before framing                                 0   SURVIVED

M8 survived the original suite, and that survival was informative rather than embarrassing.

Swapping the first two branches of the verdict only changes the answer when a capture is both fully reversed and framing-broken at the same time. Every existing test had one fault or the other, never both, so the precedence rule — which the block's comments explicitly claimed — was asserted nowhere. The suite could not tell whether the order mattered.

The fix was not to delete the mutation or weaken the claim. It was to add T10, which drives exactly that combination, and re-run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  M8  weigh reversal before framing (after adding T10)              1   killed

8. Choosing Test Vectors That Can Actually Fail

Everything in §2 comes down to a single practical rule: a test vector is only as good as the faults it is capable of revealing.

To detectUseBecause
Bit-order reversal0x55 / 0xAAmirror images of each other; maximally sensitive
Sampling drift (17.2)0x55 / 0xAAevery adjacent bit differs, so any drift lands on a different value
Stuck-at-one on any data line0x00any 1 that arrives is a fault
Stuck-at-zero on any data line0xFFany 0 that arrives is a fault
Parity configurationany payload, many framesit is the rate that carries the answer
Framingany payloadthe stop bit does not depend on the data

0xA5 is a fine byte for confirming that a link carries data at all. It is a poor byte for proving anything about bit order, and 0x00 and 0xFF — the other two reflexive choices — are equally blind to it.

A loopback suite that sends 0x00, 0xFF and 0xA5 and passes has demonstrated that the wire conducts. It has not demonstrated that the bit order is right, because none of the three could have shown otherwise.

Continue learning

Where this fits

Part of the UART curriculum.