VHDL coding tips and tricks: VHDL code for Cyclic Reduntancy Check(CRC)

Sunday, November 15, 2015

VHDL code for Cyclic Reduntancy Check(CRC)

A Cyclic Redundancy Check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data. There are many CRC polynomials available, used depending on the specific application.
The CRC polynomial I have implemented here is 8 bit in size and is known as CRC-8-CCITT. The polynomial is represented by the polynomial equation,

P(x) = x8+ x2+ x1+ x0

The https://www.ghsi.de/  website has a nice tool to generate the circuit diagram for any CRC polynomial. The service also generates a Verilog code. The advantage of the circuit generated is that, the input is assumed to be serially fed into the circuit. This means the input can be really long and still the fpga resource usage will remain the same.

Circuit Diagram generated by the online tool:



CRC - 8 bit:

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 
use IEEE.NUMERIC_STD.ALL;

entity CRC8 is 
port( Clk: in std_logic; 
        reset : in std_logic; --active high reset
        size_data : in unsigned(15 downto 0);  --the size of input stream in bits.
        Data_in : in std_logic; --serial input
        crc_out : out unsigned(7 downto 0); --8 bit crc checksum
        crc_ready : out std_logic --high when the calculation is done.
        ); 
end CRC8; 

architecture Behavioral of CRC8 is 

signal count : unsigned(15 downto 0) := (others => '0');
signal crc_temp : unsigned(7 downto 0) := (others => '0');

begin

process(Clk,reset)
begin
    if(reset = '1') then
        crc_temp <= (others => '0');
        count <= (others => '0');
        crc_ready <= '0';
    elsif(rising_edge(Clk)) then
    --crc calculation in the next four lines.
        crc_temp(0) <= Data_in xor crc_temp(7);
        crc_temp(1) <= crc_temp(0) xor crc_temp(7);
        crc_temp(2) <= crc_temp(1) xor crc_temp(7);
        crc_temp(7 downto 3) <= crc_temp(6 downto 2);
        
        count <= count + 1; --keeps track of the number of rounds
        if(count = size_data + 7) then --check when to finish the calculations
            count <= (others => '0');
            crc_ready <= '1';
        end if; 
    end if; 
end process;    

crc_out <= crc_temp;

end Behavioral;

Testbench code for CRC:

For testing the design I send two different input streams to the crc calculating module. The first stream is 8 bits in size and the second stream is 16 bits in size. 

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY tb IS
END tb;

ARCHITECTURE behavior OF tb IS 

    -- CRC Component Declaration for testing.
    COMPONENT CRC8
    port( Clk: in std_logic; 
        reset : in std_logic;
        size_data : in unsigned(15 downto 0);
        Data_in : in std_logic;
        crc_out : out unsigned(7 downto 0);
        crc_ready : out std_logic
        );
    END COMPONENT;    

   --Inputs
   signal Clk : std_logic := '0';
   signal reset : std_logic := '0';
   signal Data_in : std_logic := '0';
    signal size_data : unsigned(15 downto 0) := (others => '0');
    --Outputs
   signal crc_out : unsigned(7 downto 0);
   signal crc_ready : std_logic;
   -- Clock period definitions
   constant Clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: CRC8 PORT MAP (
          Clk => Clk,
          reset => reset,
          size_data => size_data,
          Data_in => Data_in,
          crc_out => crc_out,
          crc_ready => crc_ready
        );

   -- Clock process definitions
   Clk_process :process
   begin
        Clk <= '0';
        wait for Clk_period/2;
        Clk <= '1';
        wait for Clk_period/2;
   end process;

   -- Stimulus process
   stim_proc: process
   begin        
      reset <= '1'; 
        wait for 100 ns;
        --Data stream(in hex) : 78
        size_data <= to_unsigned(8,16);  --data stream is 8 bits in size
        wait until falling_edge(Clk);
        reset <= '0';
        Data_in <= '0'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '0'; wait for Clk_period;
        wait until crc_ready = '1'; wait for Clk_period;
        reset <= '1';
        
        wait for 100 ns;
        --Data stream(in hex) : B800
        size_data <= to_unsigned(16,16); --data stream is 16 bits in size
        wait until falling_edge(Clk);
        reset <= '0';
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '0'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '1'; wait for Clk_period;
        Data_in <= '0'; wait for Clk_period;
        wait until crc_ready = '1'; wait for Clk_period;
        reset <= '1';
      wait;
   end process;

END;

Simulation waveform:

The codes were compiled and simulated using Xilinx ISE 13.1. The waveform results were verified as per the results obtained in here.




Synthesis Results:

The code was successfully synthesised in Xilinx XST for Virtex 4 fpga. The tool showed a maximum frequency of 300 MHz for the design.

3 comments:

  1. I'd expect the data input xor'ed with all bits:

    crc_temp(0) <= Data_in xor crc_temp(7);
    crc_temp(1) <= crc_temp(0) xor crc_temp(7) xor Data_in;
    crc_temp(2) <= crc_temp(1) xor crc_temp(7) xor Data_in;
    crc_temp(7 downto 3) <= crc_temp(6 downto 2);

    Do I miss something?

    ReplyDelete
  2. Hi, how to analyse the CRC_out?

    ReplyDelete
  3. Yes you are right. crc_temp(1) <= crc_temp(0) xor crc_temp(7) xor Data_in;
    As per the diagram given your statement is correct.

    ReplyDelete