interfaces component

Parallel CRC-32

Open in playground

CRC-32 is the checksum that guards Ethernet frames, ZIP archives, and PNG files. This block computes it one byte per clock. The register holds the running remainder, all-ones after reset; the port shows it inverted, which is the standard presentation. So after reset the port reads zero, the CRC-32 of an empty message. After each enabled byte it equals what software reports for the bytes so far.

A serial CRC retires one bit per cycle through a single polynomial tap, so a byte costs eight cycles. Here the eight reflected steps of polynomial 0xEDB88320 are unrolled into the logic between the register and its next value, and the whole byte lands in one edge. The unrolling is pure XOR shuffling: synthesis flattens the eight conditional shifts into one XOR expression per bit.

Load it in the playground and feed your own bytes; any CRC-32 calculator agrees with the port after every byte.

In the waveform: after the ninth byte of "123456789" the crc port lands on 0xCBF43926, the check value quoted in every CRC reference.

In the netlist: the XOR forest between the register and its next-value input is the eight steps flattened together; a serial CRC would show a single tap.

The course builds the serial CRC, and the division idea underneath it, from first principles in Shift Registers.

-- Byte-wide CRC-32: eight reflected steps of polynomial 0xEDB88320 land
-- in one clock. The register starts all-ones; the port shows it inverted,
-- so it always equals the standard CRC-32 of the bytes fed so far.
library ieee;
use ieee.std_logic_1164.all;

entity parallel_crc32 is
  port (
    clk  : in  std_logic;
    rst  : in  std_logic;
    en   : in  std_logic;
    data : in  std_logic_vector(7 downto 0);
    crc  : out std_logic_vector(31 downto 0)
  );
end entity parallel_crc32;

architecture rtl of parallel_crc32 is
  constant POLY : std_logic_vector(31 downto 0) := x"EDB88320";
  signal crc_r    : std_logic_vector(31 downto 0);
  signal crc_next : std_logic_vector(31 downto 0);
begin

  -- The byte XORs into the low bits once, then eight unrolled reflected
  -- steps shift it out, folding the polynomial in whenever the outgoing
  -- bit is 1.
  next_p : process(all)
    variable v : std_logic_vector(31 downto 0);
  begin
    v := crc_r xor (x"000000" & data);
    for i in 0 to 7 loop
      if v(0) = '1' then
        v := ("0" & v(31 downto 1)) xor POLY;
      else
        v := "0" & v(31 downto 1);
      end if;
    end loop;
    crc_next <= v;
  end process next_p;

  crc_p : process(clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        crc_r <= (others => '1');
      elsif en = '1' then
        crc_r <= crc_next;
      end if;
    end if;
  end process crc_p;

  -- The standard presentation inverts the register; after reset the port
  -- therefore reads zero, the CRC-32 of the empty message.
  crc <= not crc_r;

end architecture rtl;
Waveform loading...