From ffbf5c4984e7ee289da812a8687a72371357d217 Mon Sep 17 00:00:00 2001 From: MaxP Date: Wed, 16 Apr 2025 17:30:22 +0000 Subject: [PATCH] Add Gray counter implementation and testbench Introduces a synchronous Gray counter with configurable width, reset, enable, and look-ahead functionality. Implements binary-to-Gray and Gray-to-binary conversion functions. Includes a testbench for simulation and validation of the counter's behavior. --- libs/GrayCounter.vhd | 119 +++++++++++++++++++++++++++++++++++++++ tests/GrayCounter_tb.vhd | 46 +++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 libs/GrayCounter.vhd create mode 100644 tests/GrayCounter_tb.vhd diff --git a/libs/GrayCounter.vhd b/libs/GrayCounter.vhd new file mode 100644 index 0000000..5e81837 --- /dev/null +++ b/libs/GrayCounter.vhd @@ -0,0 +1,119 @@ +---------------------------------------------------------------------------------- +--@ - Name: **Gray Counter**
+--@ - Version: 0.0.2
+--@ - Author: __Maximilian Passarello ([Blog](mpassarello.de))__
+--@ - License: [MIT](LICENSE)
+--@ A synchronous Gray counter with reset and enable +--@ History: +-- - 0.0.2 (2024-03-27) Refactored code to use conventions +-- - 0.0.1 (2009-04-02) Initial version +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.all; +use IEEE.NUMERIC_STD.all; + +entity GrayCounter is + generic ( + --@ Width of the counter + G_Width : integer := 4; + --@ Initial value of the counter + G_InitialValue : integer := 0; + --@ Reset value of the counter + G_ResetValue : integer := 0; + --@ Counting direction: "UP" or "DOWN" + G_CountingDirection : string := "UP"; + --@ Look ahead value + G_LookAhead : integer := 0 + ); + port ( + --@ Clock input; rising edge + I_CLK : in std_logic := '0'; + --@ Clock enable; active high + I_CE : in std_logic := '1'; + --@ Reset input; active high; synchronous + I_RST : in std_logic := '0'; + --@ Count enable; active high + I_CountEnable : in std_logic := '0'; + --@ Gray counter value + O_Value : out std_logic_vector(G_Width - 1 downto 0) := (others => '-'); + --@ Look ahead value + O_LAValue : out std_logic_vector(G_Width - 1 downto 0) := (others => '-') + ); +end GrayCounter; + +architecture Behavioral of GrayCounter is + -- Functions + --@ Convert Binary to Gray code + function BinaryToGray(I_BinaryValue : std_logic_vector) + return std_logic_vector is + constant K_Width : integer := I_BinaryValue'Length; + variable V_GrayValue : std_logic_vector(K_Width - 1 downto 0); + begin + V_GrayValue(K_Width - 1) := I_BinaryValue(K_Width - 1); + for i in 1 to K_Width - 1 loop + V_GrayValue((K_Width - 1) - i) := I_BinaryValue((K_Width - 1) - i) xor I_BinaryValue(((K_Width - 1) - i) + 1); + end loop; + return V_GrayValue(K_Width - 1 downto 0); + end function BinaryToGray; + + --@ Convert Gray code to binary + function GrayToBinary(I_GrayValue : std_logic_vector) + return std_logic_vector is + constant K_Width : integer := I_GrayValue'Length; + variable V_BinaryValue : std_logic_vector(K_Width - 1 downto 0); + begin + V_BinaryValue(K_Width - 1) := I_GrayValue(K_Width - 1); + for i in 1 to K_Width - 1 loop + V_BinaryValue((K_Width - 1) - i) := V_BinaryValue(K_Width - i) xor I_GrayValue((K_Width - 1) - i); + end loop; + return V_BinaryValue(K_Width - 1 downto 0); + end function GrayToBinary; + + function CountingStep(I_BinaryValue : std_logic_vector; I_Step : integer := 1) + return std_logic_vector is + begin + if G_CountingDirection = "DOWN" then + return std_logic_vector(unsigned(I_BinaryValue) - I_Step); + else + return std_logic_vector(unsigned(I_BinaryValue) + I_Step); + end if; + end function CountingStep; + + -- Constants + constant K_InitialValue : std_logic_vector(G_Width - 1 downto 0) := std_logic_vector(to_unsigned(G_InitialValue, G_Width)); + constant K_InitialLookAhead : std_logic_vector(G_Width - 1 downto 0) := std_logic_vector(to_unsigned(G_InitialValue + G_LookAhead, G_Width)); + constant K_ResetValue : std_logic_vector(G_Width - 1 downto 0) := std_logic_vector(to_unsigned(G_ResetValue, G_Width)); + constant K_ResetLookAhead : std_logic_vector(G_Width - 1 downto 0) := std_logic_vector(to_unsigned(G_ResetValue + G_LookAhead, G_Width)); + + signal R_CounterValue : std_logic_vector(G_Width - 1 downto 0) := K_InitialValue; + signal R_GrayValue : std_logic_vector(G_Width - 1 downto 0) := BinaryToGray(K_InitialValue); + signal R_LookAheadValue : std_logic_vector(G_Width - 1 downto 0) := BinaryToGray(K_InitialLookAhead); +begin + + O_Value <= R_GrayValue; + O_LAValue <= R_LookAheadValue; + + Counter : process (I_CLK) + variable V_Counter : std_logic_vector(G_Width - 1 downto 0); + variable V_LookAhead : std_logic_vector(G_Width - 1 downto 0); + begin + if rising_edge(I_CLK) then + if I_RST = '1' then + V_Counter := (others => '0'); + R_GrayValue <= BinaryToGray(K_ResetValue); + R_LookAheadValue <= BinaryToGray(K_ResetLookAhead); + R_CounterValue <= K_ResetValue; + elsif I_CE = '1' then + if I_CountEnable = '1' then + V_Counter := CountingStep(R_CounterValue); + + R_CounterValue <= V_Counter; + R_GrayValue <= BinaryToGray(V_Counter); + + V_LookAhead := CountingStep(V_Counter, G_LookAhead); + R_LookAheadValue <= BinaryToGray(V_LookAhead); + end if; + end if; + end if; + end process; +end Behavioral; diff --git a/tests/GrayCounter_tb.vhd b/tests/GrayCounter_tb.vhd new file mode 100644 index 0000000..90c5bc5 --- /dev/null +++ b/tests/GrayCounter_tb.vhd @@ -0,0 +1,46 @@ + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity GrayCounter_tb is +end; + +architecture bench of GrayCounter_tb is + -- Clock period + constant clk_period : time := 10 ns; + -- Generics + constant G_Width : integer := 6; + constant G_InitialValue : integer := 0; + constant G_ResetValue : integer := 0; + constant G_CountingDirection : string := "UP"; + constant G_LookAhead : integer := 1; + -- Ports + signal I_CLK : std_logic := '0'; + signal I_CE : std_logic := '1'; + signal I_RST : std_logic := '0'; + signal I_CountEnable : std_logic := '1'; + signal O_Value : std_logic_vector(G_Width - 1 downto 0); + signal O_LookAheadValue : std_logic_vector(G_Width - 1 downto 0); +begin + + GrayCounter_inst : entity work.GrayCounter + generic map ( + G_Width => G_Width, + G_InitialValue => G_InitialValue, + G_ResetValue => G_ResetValue, + G_CountingDirection => G_CountingDirection, + G_LookAhead => G_LookAhead + ) + port map ( + I_CLK => I_CLK, + I_CE => I_CE, + I_RST => I_RST, + I_CountEnable => I_CountEnable, + O_Value => O_Value, + O_LAValue => O_LookAheadValue + ); + + I_CLK <= not I_CLK after clk_period/2; + +end; \ No newline at end of file