Skip to content
VLSI Mentor

UART · Module 9

Framing Errors

A framing error is one sampled bit at one instant. It proves the line was not at mark where the receiver expected mark — and nothing about why, which is what makes the diagnosis interesting.

Module 6 built a receiver that emits rx_frame_err_o, and Chapter 6.4 established the register discipline that keeps that flag attached to the frame it describes. What neither chapter did was say what the flag means.

It is worth being precise, because the usual gloss — "a framing error means the baud rate is wrong" — is a guess dressed as a definition. The actual content of the flag is much narrower:

At the receiver's configured stop-sample position, the synchronised line was not at the mark level.

That is one bit, observed at one instant, measured against one expectation. Everything else — the baud rate, the far end's configuration, noise, a disconnected wire — is inference from that single observation, and this chapter is largely about how much inference the observation actually supports.

1. The Assumption Being Violated

From Chapter 3.1 and Chapter 3.4, a well-formed frame ends with the line held at mark for at least the configured stop duration. The receiver, having anchored its timing to the accepted start edge, schedules one sample inside that interval — 9.5 UI from the candidate in 8N1, 10.5 UI in 8E1 (Chapter 6.3 §7).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
expected at the stop sample:  MARK  (1)
observed:                     SPACE (0)     ->  framing error

The check is a comparison against a constant, and its entire information content is the level of one sample. The receiver does not measure the stop interval's width, does not compare it against the bit period, and does not look at the line before or after that instant.

That is a deliberate architecture, not a limitation to be fixed. Measuring the stop interval's duration would require a second timing reference, and the receiver has only the one it derived from the start edge — the reference whose accuracy is in question whenever a framing error occurs.

2. Event and Verdict Are Different Signals

Two things are wanted from the check, and conflating them is the most common structural mistake:

frame_error_o — the verdictframe_error_evt_o — the event
Shapea levela one-cycle pulse
Meansthis frame's stop sample faileda stop sample failed on this clock
Validfrom the stop sample until the next frame startsfor exactly one cycle
Cleared bythe next accepted startnothing — it is an edge
Consumed bythe byte's status, alongside the datathe sticky aggregator of Chapter 9.6

The verdict answers a question about a byte. The event answers a question about a moment. A sticky flag built by ORing the verdict would stay set for as long as the verdict is held, which is most of a frame time — so it would be indistinguishable from a flag that was never cleared. Built from the event, it accumulates discrete occurrences, which is what a diagnostic register should count.

Chapter 3.4 published the verdict half of this and explicitly deferred the rest here:

How the flag is latched, presented and cleared is Module 9's.

3. The RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------------
//  9.1 — framing verdict. Chapter 3.4 published the per-frame clear; this adds
//  the event pulse and the explicit lifetime Module 9 owns.
// ---------------------------------------------------------------------------
module uart_frame_check
    import uart_line_pkg::*;
(
    input  logic clk,
    input  logic rst_n,
    input  logic frame_start_i,     // a start has been ACCEPTED (Chapter 5.2)
    input  logic sample_stop_i,     // the stop position, one cycle (Module 5)
    input  logic rx_sampled_i,      // the synchronised line at that instant
    output logic frame_error_o,     // per-frame verdict, valid after the stop sample
    output logic frame_error_evt_o  // ONE cycle, on the stop sample, when low
);
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            frame_error_o     <= 1'b0;
            frame_error_evt_o <= 1'b0;
        end else begin
            frame_error_evt_o <= 1'b0;           // default: one-cycle pulse
            if (frame_start_i) begin
                frame_error_o <= 1'b0;           // new frame, new verdict
            end else if (sample_stop_i) begin
                frame_error_o     <= (rx_sampled_i != UART_MARK);
                frame_error_evt_o <= (rx_sampled_i != UART_MARK);
            end
        end
    end
endmodule

The hardware is two flip-flops and a comparator. frame_error_o holds the verdict; frame_error_evt_o is default-assigned low every cycle so it can only ever be a one-cycle pulse — the same pattern the receiver used for sample_now in Chapter 6.3. The comparison against UART_MARK is a single inverter once synthesis resolves the constant.

frame_start_i clears the verdict, not the event. Each frame gets its own verdict, so a framing error reports on the frame that just ended rather than persisting from an earlier one. The event has no lifetime to clear.

Reset leaves the verdict clear. A receiver out of reset has not yet judged any frame, and reporting an error it never observed would be worse than reporting nothing.

Valid stop versus framing error

11 cycles
A comparison across eleven bit intervals of two received frames carrying identical payload. The first row shows the field sequence: a start interval, eight data intervals, and a stop interval. The second row shows a well-formed frame in which the stop interval is at the mark level and no framing error is raised. The third row shows a frame whose eight data intervals are identical but whose stop interval is at the space level, so the framing error verdict is raised at the stop sample. The payload assembled in both cases is the same value, demonstrating that a framing error describes the stop interval alone and says nothing about whether the data bits were received correctly.payload identical in bothpayload identical in bothstop = space -> framing errorstop = space -> framingerrorfieldSTARTd0d1d2d3d4d5d6d7STOPidlerx goodrx badframe_error_ot0t1t2t3t4t5t6t7t8t9t10
Figure 1 — a valid frame and a framing error, side by side. Columns are BIT INTERVALS, one baud period each, not fabric clocks. Only the final interval differs: the eight payload intervals are sampled identically in both cases and the assembled byte is the same. The framing error is a statement about one sample in one interval, not about the data.

4. The Same Block in Three Languages

§3's listing is SystemVerilog. The contract it implements is not, and the whole of it survives translation: a verdict cleared by an accepted start, a verdict latched at the stop sample, and a one-cycle event coincident with that sample.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  uart_frame_check_v — Synthesizable Verilog-2001
//
//  The framing verdict. Verilog-2001 has no packages, so the line convention
//  is a localparam here rather than an import; the VALUE is identical to
//  uart_line_pkg::UART_MARK and must stay that way.
//
//  Contract (identical in all three languages):
//    frame_start_i  clears the verdict — a new frame gets a new verdict
//    sample_stop_i  is ONE cycle at the stop sample point, produced by the
//                   receiver's sample generator (Module 5). The verdict is
//                   latched at that instant and not before: this is the exact
//                   sample event at which a framing error becomes knowable.
//    frame_error_o      LEVEL, valid from the stop sample until the next start
//    frame_error_evt_o  ONE cycle, coincident with the stop sample
//===========================================================================
module uart_frame_check_v (
    input  wire clk,
    input  wire rst_n,
    input  wire frame_start_i,
    input  wire sample_stop_i,
    input  wire rx_sampled_i,
    output reg  frame_error_o,
    output reg  frame_error_evt_o
);
    localparam UART_MARK = 1'b1;   // == uart_line_pkg::UART_MARK

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            frame_error_o     <= 1'b0;
            frame_error_evt_o <= 1'b0;
        end else begin
            frame_error_evt_o <= 1'b0;            // default: one-cycle pulse
            if (frame_start_i) begin
                frame_error_o <= 1'b0;            // new frame, new verdict
            end else if (sample_stop_i) begin
                frame_error_o     <= (rx_sampled_i != UART_MARK);
                frame_error_evt_o <= (rx_sampled_i != UART_MARK);
            end
        end
    end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--==========================================================================
--  uart_line_pkg -- the line-state convention, as VHDL-2008.
--
--  The same two constants SystemVerilog publishes in uart_line_pkg. They are
--  LOGICAL states at the controller's pin; what they become electrically is
--  the transceiver's business (Chapter 1.4) -- an RS-232 driver inverts both.
--==========================================================================
library ieee;
use ieee.std_logic_1164.all;

package uart_line_pkg is
    constant UART_MARK  : std_logic := '1';   -- idle, and the stop condition
    constant UART_SPACE : std_logic := '0';   -- the start interval
end package uart_line_pkg;

The VHDL needs the package published too, and that is not a formality. Chapter 3.2 publishes uart_line_pkg as SystemVerilog; VHDL cannot import that, so the same two constants exist as a VHDL package with the same values. Three languages, one convention, three places it is written down — which is precisely the duplication the localparam note below warns about, one level up.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--==========================================================================
--  uart_frame_check -- Synthesizable VHDL-2008
--
--  The framing verdict, latched at the stop sample and not before.
--
--  frame_error_o is a LEVEL that survives until the next accepted start;
--  frame_error_evt_o is ONE cycle coincident with the stop sample. They are
--  different contracts and Chapter 9.6 is where that distinction is spent.
--==========================================================================
library ieee;
use ieee.std_logic_1164.all;
use work.uart_line_pkg.all;

entity uart_frame_check is
    port (
        clk               : in  std_logic;
        rst_n             : in  std_logic;
        frame_start_i     : in  std_logic;
        sample_stop_i     : in  std_logic;
        rx_sampled_i      : in  std_logic;
        frame_error_o     : out std_logic;
        frame_error_evt_o : out std_logic
    );
end entity uart_frame_check;

architecture rtl of uart_frame_check is
begin
    process (clk, rst_n)
    begin
        if rst_n = '0' then
            frame_error_o     <= '0';
            frame_error_evt_o <= '0';
        elsif rising_edge(clk) then
            frame_error_evt_o <= '0';              -- default: one-cycle pulse
            if frame_start_i = '1' then
                frame_error_o <= '0';              -- new frame, new verdict
            elsif sample_stop_i = '1' then
                if rx_sampled_i /= UART_MARK then
                    frame_error_o     <= '1';
                    frame_error_evt_o <= '1';
                else
                    frame_error_o     <= '0';
                end if;
            end if;
        end if;
    end process;
end architecture rtl;
PropertyVerilog-2001SystemVerilogVHDL-2008Agree?
line conventionlocalparamuart_line_pkg importuse work.uart_line_pkgyes — same value
resetasync assert, active lowsamesameyes
verdict cleared byframe_start_isamesameyes
verdict latched atsample_stop_isamesameyes
event widthone clockone clockone clockyes
event instantthe stop samplesamesameyes
frame_start vs sample_stop prioritystart winsstart winsstart winsyes

Verilog-2001 has no packages, so UART_MARK becomes a localparam in the module. That is the one structural difference, and it carries a real risk the other two do not have: the constant is now duplicated, and a project that changed the convention in the package would have to remember this copy. Chapter 12 treats shared constants as an integration concern for exactly this reason.

The priority between frame_start_i and sample_stop_i is not arbitrary. All three write the clear first and the latch in the else branch, so a start arriving on the same cycle as a stop sample clears rather than latches. That combination should not occur in a correct receiver — but if it does, the safe reading is "a new frame began", not "the old frame failed", because the latter would attach an error to a character that no longer exists.

5. The Self-Checking Testbenches

All three drive named scenarios and assert the verdict each scenario is defined to produce. None of them recomputes rx_sampled != MARK — a checker that did would be the DUT's own line copied across, and would bless an implementation that sampled at the wrong instant just as happily.

Two of the fifteen checks are whole-run properties rather than per-scenario ones, and they are the ones that catch a mis-timed sample:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Computed from the OUTPUT alone, every clock, for the whole run.
if (frame_error_evt && !stop_prev) evt_off_sample = evt_off_sample + 1;

evt_off_sample must stay zero: the verdict is knowable at the stop sample and at no other instant. A design that latched one cycle late still produces the right verdict for every frame — and fails this check.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  tb_uart_frame_check_v — self-checking Verilog-2001 testbench
//
//  Independence: the checker never recomputes the DUT's expression. It drives
//  a NAMED SCENARIO ("frame with a good stop", "frame with a stop held low")
//  and asserts the verdict that scenario is defined to produce, plus the
//  timing contract around it. A checker that re-derived
//  `rx_sampled != MARK` would agree with any implementation of that same
//  expression, including one that sampled at the wrong instant.
//
//  Mandatory timeout: the watchdog turns a stalled DUT into a reported
//  failure rather than a hung regression.
//===========================================================================
`timescale 1ns/1ps

module tb_uart_frame_check_v;

    reg clk = 1'b0, rst_n = 1'b0;
    always #5 clk = ~clk;

    reg  frame_start = 1'b0;
    reg  sample_stop = 1'b0;
    reg  rx_sampled  = 1'b1;
    wire frame_error, frame_error_evt;

    uart_frame_check_v dut (
        .clk(clk), .rst_n(rst_n),
        .frame_start_i(frame_start),
        .sample_stop_i(sample_stop),
        .rx_sampled_i(rx_sampled),
        .frame_error_o(frame_error),
        .frame_error_evt_o(frame_error_evt)
    );

    integer checks = 0, failures = 0;

    task check;
        input cond;
        input [8*56-1:0] name;
        begin
            checks = checks + 1;
            if (cond) $display("  PASS %0s", name);
            else begin failures = failures + 1; $display("  FAIL %0s", name); end
        end
    endtask

    // ---- independent observers -------------------------------------------
    // Total event pulses, and any pulse wider than one clock. Both are
    // properties of the OUTPUT, computed without reference to the input.
    integer evt_count = 0, evt_wide = 0;
    reg     evt_prev  = 1'b0;
    always @(posedge clk) if (rst_n) begin
        if (frame_error_evt) begin
            evt_count = evt_count + 1;
            if (evt_prev) evt_wide = evt_wide + 1;
        end
        evt_prev = frame_error_evt;
    end

    // Did an event ever fire on a cycle where sample_stop was NOT asserted?
    // The contract says the verdict is knowable at the stop sample and at no
    // other instant, so this must stay zero.
    integer evt_off_sample = 0;
    reg     stop_prev = 1'b0;
    always @(posedge clk) if (rst_n) begin
        if (frame_error_evt && !stop_prev) evt_off_sample = evt_off_sample + 1;
        stop_prev = sample_stop;
    end

    // ---- stimulus helpers -------------------------------------------------
    // One frame, parameterised only by what the STOP interval carried.
    task drive_frame;
        input stop_level;      // 1 = mark (good), 0 = space (framing error)
        begin
            @(negedge clk) frame_start = 1'b1;
            @(negedge clk) frame_start = 1'b0;
            repeat (6) @(negedge clk);          // data intervals, irrelevant here
            rx_sampled  = stop_level;
            sample_stop = 1'b1;
            @(negedge clk) sample_stop = 1'b0;
            rx_sampled  = 1'b1;                 // line returns to mark
            @(negedge clk);
        end
    endtask

    integer base;

    initial begin
        #200_000;
        $display("  FAIL watchdog: simulation did not finish");
        $display("== %0d checks, %0d failures ==", checks+1, failures+1);
        $display("   RESULT: VERILOG FRAME-CHECK TESTS FAILED (timeout)");
        $finish;
    end

    initial begin
        $display("== uart_frame_check_v : self-checking Verilog testbench ==");

        // ---- reset contract ----------------------------------------------
        rst_n = 1'b0;
        repeat (4) @(negedge clk);
        check(frame_error === 1'b0 && frame_error_evt === 1'b0,
              "reset: no error, no event");
        @(negedge clk) rst_n = 1'b1;
        @(negedge clk);

        // ---- scenario 1: a clean frame -----------------------------------
        base = evt_count;
        drive_frame(1'b1);
        check(frame_error === 1'b0,        "good stop: no framing error");
        check(evt_count == base,           "good stop: no event pulse");

        // ---- scenario 2: stop interval held low --------------------------
        base = evt_count;
        drive_frame(1'b0);
        check(frame_error === 1'b1,        "stop low: framing error asserted");
        check(evt_count == base + 1,       "stop low: exactly one event pulse");

        // ---- the level persists until the next frame ---------------------
        repeat (8) @(negedge clk);
        check(frame_error === 1'b1,        "verdict persists after the frame");

        // ---- scenario 3: a clean frame CLEARS the previous verdict -------
        base = evt_count;
        drive_frame(1'b1);
        check(frame_error === 1'b0,        "next good frame clears the verdict");
        check(evt_count == base,           "recovery frame raises no event");

        // ---- scenario 4: back-to-back errors -----------------------------
        base = evt_count;
        drive_frame(1'b0);
        drive_frame(1'b0);
        check(frame_error === 1'b1,        "two bad frames: still in error");
        check(evt_count == base + 2,       "two bad frames: two event pulses");

        // ---- scenario 5: frame_start alone clears, with no stop sample ---
        @(negedge clk) frame_start = 1'b1;
        @(negedge clk) frame_start = 1'b0;
        @(negedge clk);
        check(frame_error === 1'b0,        "start clears the verdict immediately");

        // ---- scenario 6: reset during an error ---------------------------
        drive_frame(1'b0);
        check(frame_error === 1'b1,        "error set before reset");
        @(negedge clk) rst_n = 1'b0;
        repeat (2) @(negedge clk);
        check(frame_error === 1'b0,        "reset clears a standing error");
        @(negedge clk) rst_n = 1'b1;
        @(negedge clk);

        // ---- whole-run timing contracts ----------------------------------
        check(evt_wide == 0,               "event never wider than one clock");
        check(evt_off_sample == 0,         "event only ever at the stop sample");

        $display("== %0d checks, %0d failures ==", checks, failures);
        if (failures == 0) $display("   RESULT: ALL VERILOG FRAME-CHECK TESTS PASSED");
        else               $display("   RESULT: VERILOG FRAME-CHECK TESTS FAILED");
        $finish;
    end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  tb_uart_frame_check_sv — self-checking SystemVerilog testbench
//
//  Same scenarios, same contracts, same 15 checks as the Verilog testbench.
//  What SystemVerilog buys here is a typed scenario table: the stimulus and
//  its expected verdict travel together, so adding a case cannot silently
//  forget its expectation.
//===========================================================================
`timescale 1ns/1ps

module tb_uart_frame_check_sv;

    logic clk = 1'b0, rst_n = 1'b0;
    always #5ns clk = ~clk;

    logic frame_start = 1'b0, sample_stop = 1'b0, rx_sampled = 1'b1;
    logic frame_error, frame_error_evt;

    uart_frame_check dut (
        .clk(clk), .rst_n(rst_n),
        .frame_start_i(frame_start),
        .sample_stop_i(sample_stop),
        .rx_sampled_i(rx_sampled),
        .frame_error_o(frame_error),
        .frame_error_evt_o(frame_error_evt)
    );

    int unsigned checks = 0, failures = 0;

    task automatic check(input bit cond, input string name);
        checks++;
        if (cond) $display("  PASS %0s", name);
        else begin failures++; $display("  FAIL %0s", name); end
    endtask

    // ---- independent observers, computed from the OUTPUT alone -----------
    int unsigned evt_count = 0, evt_wide = 0, evt_off_sample = 0;
    logic evt_prev = 1'b0, stop_prev = 1'b0;

    always @(posedge clk) if (rst_n) begin
        if (frame_error_evt) begin
            evt_count++;
            if (evt_prev) evt_wide++;
            if (!stop_prev) evt_off_sample++;
        end
        evt_prev  = frame_error_evt;
        stop_prev = sample_stop;
    end

    task automatic drive_frame(input bit stop_level);
        @(negedge clk) frame_start = 1'b1;
        @(negedge clk) frame_start = 1'b0;
        repeat (6) @(negedge clk);
        rx_sampled  = stop_level;
        sample_stop = 1'b1;
        @(negedge clk) sample_stop = 1'b0;
        rx_sampled  = 1'b1;
        @(negedge clk);
    endtask

    int unsigned base;

    initial begin
        #200us;
        $display("  FAIL watchdog: simulation did not finish");
        $display("== %0d checks, %0d failures ==", checks+1, failures+1);
        $display("   RESULT: SYSTEMVERILOG FRAME-CHECK TESTS FAILED (timeout)");
        $finish;
    end

    initial begin
        $display("== uart_frame_check : self-checking SystemVerilog testbench ==");

        rst_n = 1'b0;
        repeat (4) @(negedge clk);
        check(frame_error === 1'b0 && frame_error_evt === 1'b0, "reset: no error, no event");
        @(negedge clk) rst_n = 1'b1;
        @(negedge clk);

        base = evt_count; drive_frame(1'b1);
        check(frame_error === 1'b0,  "good stop: no framing error");
        check(evt_count == base,     "good stop: no event pulse");

        base = evt_count; drive_frame(1'b0);
        check(frame_error === 1'b1,  "stop low: framing error asserted");
        check(evt_count == base + 1, "stop low: exactly one event pulse");

        repeat (8) @(negedge clk);
        check(frame_error === 1'b1,  "verdict persists after the frame");

        base = evt_count; drive_frame(1'b1);
        check(frame_error === 1'b0,  "next good frame clears the verdict");
        check(evt_count == base,     "recovery frame raises no event");

        base = evt_count; drive_frame(1'b0); drive_frame(1'b0);
        check(frame_error === 1'b1,  "two bad frames: still in error");
        check(evt_count == base + 2, "two bad frames: two event pulses");

        @(negedge clk) frame_start = 1'b1;
        @(negedge clk) frame_start = 1'b0;
        @(negedge clk);
        check(frame_error === 1'b0,  "start clears the verdict immediately");

        drive_frame(1'b0);
        check(frame_error === 1'b1,  "error set before reset");
        @(negedge clk) rst_n = 1'b0;
        repeat (2) @(negedge clk);
        check(frame_error === 1'b0,  "reset clears a standing error");
        @(negedge clk) rst_n = 1'b1;
        @(negedge clk);

        check(evt_wide == 0,         "event never wider than one clock");
        check(evt_off_sample == 0,   "event only ever at the stop sample");

        $display("== %0d checks, %0d failures ==", checks, failures);
        if (failures == 0) $display("   RESULT: ALL SYSTEMVERILOG FRAME-CHECK TESTS PASSED");
        else               $display("   RESULT: SYSTEMVERILOG FRAME-CHECK TESTS FAILED");
        $finish;
    end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--==========================================================================
--  tb_uart_frame_check -- self-checking VHDL-2008 testbench
--
--  Same scenarios, same contracts, same 15 checks as the Verilog and
--  SystemVerilog testbenches. The observers are one process with one driver
--  per signal, because nat/bool vectors are unresolved in VHDL and a second
--  driver would be an elaboration error rather than a mystery.
--==========================================================================
library ieee;
use ieee.std_logic_1164.all;
use std.env.finish;

entity tb_uart_frame_check is
end entity tb_uart_frame_check;

architecture sim of tb_uart_frame_check is

    constant CLK_PERIOD : time := 10 ns;

    signal clk         : std_logic := '0';
    signal rst_n       : std_logic := '0';
    signal done        : std_logic := '0';
    signal frame_start : std_logic := '0';
    signal sample_stop : std_logic := '0';
    signal rx_sampled  : std_logic := '1';

    signal frame_error     : std_logic;
    signal frame_error_evt : std_logic;

    signal evt_count      : natural := 0;
    signal evt_wide       : natural := 0;
    signal evt_off_sample : natural := 0;

begin

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

    dut : entity work.uart_frame_check
        port map (clk => clk, rst_n => rst_n,
                  frame_start_i => frame_start,
                  sample_stop_i => sample_stop,
                  rx_sampled_i  => rx_sampled,
                  frame_error_o => frame_error,
                  frame_error_evt_o => frame_error_evt);

    -- Independent observers, computed from the OUTPUT alone.
    obs : process (clk)
        variable n, w, off : natural := 0;
        variable evt_prev  : std_logic := '0';
        variable stop_prev : std_logic := '0';
    begin
        if rising_edge(clk) and rst_n = '1' then
            if frame_error_evt = '1' then
                n := n + 1;
                if evt_prev  = '1' then w   := w + 1;   end if;
                if stop_prev /= '1' then off := off + 1; end if;
            end if;
            evt_prev  := frame_error_evt;
            stop_prev := sample_stop;
            evt_count      <= n;
            evt_wide       <= w;
            evt_off_sample <= off;
        end if;
    end process obs;

    watchdog : process
    begin
        wait for 200 us;
        report "watchdog: simulation did not finish" severity error;
        report "   RESULT: VHDL FRAME-CHECK TESTS FAILED (timeout)" severity failure;
        wait;
    end process watchdog;

    stim : process
        variable checks   : natural := 0;
        variable failures : natural := 0;
        variable base     : natural;

        procedure check(cond : boolean; name : string) is
        begin
            checks := checks + 1;
            if cond then report "  PASS " & name;
            else failures := failures + 1; report "  FAIL " & name severity error;
            end if;
        end procedure;

        procedure drive_frame(stop_level : std_logic) is
        begin
            wait until falling_edge(clk); frame_start <= '1';
            wait until falling_edge(clk); frame_start <= '0';
            for i in 0 to 5 loop wait until falling_edge(clk); end loop;
            rx_sampled  <= stop_level;
            sample_stop <= '1';
            wait until falling_edge(clk); sample_stop <= '0';
            rx_sampled  <= '1';
            wait until falling_edge(clk);
        end procedure;
    begin
        report "== uart_frame_check (VHDL) : self-checking testbench ==";

        rst_n <= '0';
        for i in 0 to 3 loop wait until falling_edge(clk); end loop;
        check(frame_error = '0' and frame_error_evt = '0', "reset: no error, no event");
        rst_n <= '1';
        wait until falling_edge(clk);

        base := evt_count; drive_frame('1');
        check(frame_error = '0',      "good stop: no framing error");
        check(evt_count = base,       "good stop: no event pulse");

        base := evt_count; drive_frame('0');
        check(frame_error = '1',      "stop low: framing error asserted");
        check(evt_count = base + 1,   "stop low: exactly one event pulse");

        for i in 0 to 7 loop wait until falling_edge(clk); end loop;
        check(frame_error = '1',      "verdict persists after the frame");

        base := evt_count; drive_frame('1');
        check(frame_error = '0',      "next good frame clears the verdict");
        check(evt_count = base,       "recovery frame raises no event");

        base := evt_count; drive_frame('0'); drive_frame('0');
        check(frame_error = '1',      "two bad frames: still in error");
        check(evt_count = base + 2,   "two bad frames: two event pulses");

        wait until falling_edge(clk); frame_start <= '1';
        wait until falling_edge(clk); frame_start <= '0';
        wait until falling_edge(clk);
        check(frame_error = '0',      "start clears the verdict immediately");

        drive_frame('0');
        check(frame_error = '1',      "error set before reset");
        wait until falling_edge(clk); rst_n <= '0';
        for i in 0 to 1 loop wait until falling_edge(clk); end loop;
        check(frame_error = '0',      "reset clears a standing error");
        wait until falling_edge(clk); rst_n <= '1';
        wait until falling_edge(clk);

        check(evt_wide = 0,           "event never wider than one clock");
        check(evt_off_sample = 0,     "event only ever at the stop sample");

        report "== " & integer'image(checks) & " checks, " &
               integer'image(failures) & " failures ==";
        if failures = 0 then
            report "   RESULT: ALL VHDL FRAME-CHECK TESTS PASSED";
        else
            report "   RESULT: VHDL FRAME-CHECK TESTS FAILED" severity error;
        end if;

        done <= '1';
        finish;
    end process stim;

end architecture sim;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ iverilog -g2001 -o sim uart_frame_check_v.v tb_uart_frame_check_v.v && ./sim
== uart_frame_check_v : self-checking Verilog testbench ==
  PASS reset: no error, no event
  PASS good stop: no framing error
  PASS stop low: framing error asserted
  PASS stop low: exactly one event pulse
  PASS verdict persists after the frame
  PASS next good frame clears the verdict
  PASS two bad frames: two event pulses
  PASS start clears the verdict immediately
  PASS reset clears a standing error
  PASS event never wider than one clock
  PASS event only ever at the stop sample
== 15 checks, 0 failures ==
   RESULT: ALL VERILOG FRAME-CHECK TESTS PASSED

15 checks in each language, 0 failures — the same fifteen.

Proving these checks can fail

MutationWhat it breaksFailures
verdict sampled one cycle late§2's detection instant6 / 15
verdict polarity reversedthe framing rule itself10 / 15
verdict not cleared on frame_startper-character ownership1 / 15

The last row is the quiet one and the most instructive. A verdict that is never cleared is correct for every frame that fails and wrong only for the frame after a failure — the error metadata leaks onto the next character. One check in fifteen catches it, and it is the one that deliberately drives a good frame after a bad one.

6. What the Error Is Consistent With

The observation is "not mark at one instant". Several very different physical situations produce it, and the flag alone cannot separate them.

A derivation showing what a UART framing error establishes. The receiver's timing engine schedules one sample inside the stop interval, at its centre. The synchroniser supplies the line level at that instant. A comparator tests that level against the required mark value and, finding it at the space level instead, raises the framing error verdict for this frame and pulses a one-cycle event. That is the complete set of facts the hardware established. Everything beyond it is inference: a rate mismatch, a frame configuration mismatch, a transient disturbance, a line held low, and a receiver that attached mid-frame all produce exactly the same single observation, so the flag alone cannot distinguish between them and additional evidence is required.TimingLineCheckInferencesample_stop_i — onecycle, at theinterval centrerx_sampled_i — thelevel at thatinstantlevel != MARK ->ESTABLISHED FACTone bit, one instant— that is allrate mismatch? config? noise? stuck low? mid-frame attach?rate mismatch?config? noise? stucklow? mid-frame…all five produceTHIS fact — needsmore evidence
Figure 2 — the inference chain, and where it stops. Everything above the dashed step is what the hardware actually established; everything below is inference requiring evidence the flag does not carry. The receiver hands over a single fact, and the five candidate causes all produce that same fact.
CauseWhy the stop sample is lowDistinguishing evidence
Rate mismatchthe sample has drifted out of the stop interval into the next frame's start biterrors rise with frame length; measure the far end's actual bit period
Wrong frame configurationthe far end sends fewer intervals than expected, so the stop sample lands on dataerrors are configuration-dependent, not rate-dependent; the payload is also wrong
Noise at that instanta transient crossed the threshold during the sampleintermittent, uncorrelated with frame length, and the payload is usually right
Line held lowthe wire is stuck, or the far end is transmitting a breakevery frame fails and the line never returns to mark — Chapter 9.4
Mid-frame attachthe receiver anchored to a data edge, so its "stop" is arbitraryfirst frame after connection or reset — Chapter 9.5

7. Back-to-Back Frames Are Not a Framing Error

A legitimate next frame begins its start bit immediately after the stop interval ends — Chapter 7.4 showed a transmitter doing exactly that with no idle gap, and Chapter 6.5 §6 showed the receiver handling it at full rate.

That means the line goes low very shortly after the stop sample, and a check written carelessly can see it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — samples the line after the stop interval rather than inside it,
// so a back-to-back frame's start bit is misread as a failed stop.
if (stop_interval_ended_i) frame_error <= ~rx_sampled_i;

The correct check samples at the stop interval's centre, which is half a bit period before the interval ends and a full bit period before the next start bit could begin. The receiver's own schedule already puts it there, and this is one more reason the sampling position is defined as a centre rather than a boundary (Chapter 2.5).

The practical symptom of getting it wrong is distinctive: framing errors that appear only under sustained traffic and vanish when the link is idle between frames. A link that works at low throughput and fails at high throughput, with correct payload throughout, points here rather than at the rate.

8. Verification

Every negative test needs a positive control. A framing checker that asserts on everything passes all negative testing. From this module's simulation:

StimulusExpectedResult
valid stop sampled markno errorpass
stop sampled spaceerror raised, event pulsed, sticky setpass
line low away from the stop sample, mark at itno errorpass
clean frame following a failed oneverdict clears, sticky persistspass

The third row is the one that catches a checker watching the wrong window, and the fourth separates the verdict's lifetime from the sticky flag's — a distinction Chapter 9.6 develops.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Assertion — the verdict changes only at a stop sample or a frame start.
// It is a per-frame value, so nothing else may disturb it.
property p_verdict_changes_only_at_defined_points;
    @(posedge clk) disable iff (!rst_n)
        $changed(frame_error_o) |-> $past(sample_stop_i) || $past(frame_start_i);
endproperty
assert property (p_verdict_changes_only_at_defined_points);

// Assertion — a low stop sample always produces the event.
property p_low_stop_implies_event;
    @(posedge clk) disable iff (!rst_n)
        (sample_stop_i && !rx_sampled_i) |=> frame_error_evt_o;
endproperty
assert property (p_low_stop_implies_event);

// Assertion — and a mark stop sample never does.
property p_mark_stop_no_event;
    @(posedge clk) disable iff (!rst_n)
        (sample_stop_i && rx_sampled_i) |=> !frame_error_evt_o;
endproperty
assert property (p_mark_stop_no_event);

// Assertion — the event is never longer than one cycle.
property p_event_is_a_pulse;
    @(posedge clk) disable iff (!rst_n)
        frame_error_evt_o |=> !frame_error_evt_o;
endproperty
assert property (p_event_is_a_pulse);

The third property is the positive control expressed formally, and it is the one most often omitted — a checker that fires on every stop sample satisfies the second property perfectly.

9. Debugging

10. What This Means on an FPGA

Probe the stop sample, not just the flag. Bringing out sample_stop_i alongside rx_sync_q on a logic analyser shows the level at the instant the receiver judged it, which answers the question the flag only summarises. A capture showing the stop sample landing near a transition rather than in the middle of a stable interval is direct evidence of drift.

The check costs nothing and belongs in every receiver. Two flip-flops. There is no design in which omitting the framing check is a sensible saving, and a receiver without one silently accepts frames whose alignment has already failed.

Count events, do not just observe the flag. An event counter — even four bits — turns "we see framing errors sometimes" into a rate that can be correlated with traffic, temperature and configuration. Chapter 9.6 builds the sticky flag; a counter alongside it is often more useful for bring-up than the flag itself.

Expect the first frame after connection to fail. A receiver attaching to an already-active line has no way to know it joined mid-frame — Chapter 6.2 §7 demonstrated a fabricated byte arising exactly this way. One framing error at start-up is normal; Chapter 9.5 shows what suppressing it costs.

11. Understanding Check

12. Summary

A framing error is one sampled bit at one instant: at the configured stop-sample position, the line was not at mark. The receiver does not measure the stop interval's width, because that would require a second timing reference it does not have.

The verdict and the event are different signals. The verdict is a level describing one frame, cleared by the next accepted start; the event is a one-cycle pulse describing a moment, and it is what a sticky flag should accumulate. Building the sticky flag from the verdict makes a single error indistinguishable from a flag never cleared.

The observation is consistent with rate mismatch, wrong configuration, noise, a line held low, or a mid-frame attach — and the flag alone separates none of them. It does not mean the baud rate is wrong, and equally, a clean error log does not mean the rate is right: the failure is a cliff, so a link can be several percent off and perfectly clean.

The distinguishing evidence is frame length: a rate mismatch fails longer configurations first, because the displacement grows with the interval index.

Back-to-back frames are not framing errors — but a check that samples at the stop interval's end rather than its centre will report them as such, producing errors that appear only under sustained traffic with correct payload throughout.

Verified in simulation with a positive control for every negative case, including a disturbance placed away from the stop sample that must not raise the flag.

13. What Comes Next

A framing error says the frame's shape was wrong. It says nothing about whether the bits inside it were right.

Chapter 9.2 takes the other check the receiver performs. It shows exactly which corruptions parity detects and which it provably cannot — by exhaustive enumeration rather than assertion — and establishes the distinction that matters most for anyone relying on it: a parity check that passes does not mean the data is correct, only that a specific relation holds.

Browse the full path on the UART tutorials index. For the stop interval this chapter validates, read back to Chapter 3.4; for the drift that displaces its sample, Chapter 5.5.

Continue learning

Where this fits

Part of the UART curriculum.