Electrical – Issues with combining counters for traffic light

countervhdl

I am trying to implement something as simple as the controller for one single traffic light by using two counters (one for green/red and one for yellow), but I don't seem to be getting the timing right.

My first counter looked something like

entity counter is
    generic (
        max_count : integer := 50
    );
    port (
        clk : in std_ulogic;
        rst : in std_ulogic;
        count_value : out integer := '0';
        flag : out std_ulogic := '0'
    );
end entity;

architecture behav of counter is
    signal count : natural := 0;
begin
    count_value <= count

    process (rst, clk)
    begin
        if rst = '0' then
            flag <= '0';
            count <= 0;
        elsif rising_edge(clk) then
            count <= (count + 1) mod max_count;
            if count = 0 then
                -- set flag value for one cycle
                flag <= '1';
            else
                flag <= '0';
            end if;
        end if;
    end process;
end architecture behav;

which also sets the flag value when it starts to count.

When I tried to implement the traffic light (simply a state machine) by means of this counter, I got stuck. When I just start the red/green counter c_redgreen once, i.e. the counter is never reset. The problem with this approach is of course that the time where the traffic light is yellow is not taken into account (e.g. the light starts with red and stays in that state for 50 cycles, then it becomes yellow for 5 cycles, but then there are only 45 cycles green, because the counter was not paused during the yellow time etc…). I thought this could be simply worked around by resetting c_redgreen while the light is yellow, but immediately after the reset, the flag is immediately set and therefore I can't use something like if rising_edge(rg_flag) then ..., because the rising edge comes immediately (because the counter sets the flag as it starts).

After these troubles, I thought it would be better to reformulate my counter as follows

...
elsif rising_edge(clk) then
    temp := (count + 1) mod max_count;
    count <= temp
    if temp = 0 then
...

to avoid the flag being set when starting, but when I start the clock (in the testbench) with 0, the first red light does not last 50 cycles, but only 49. Also I am not sure whether it is a good idea for a counter to do nothing until it has reached max_count.

My question is thus how I can combine two counters (with or without start-flag) so that I can make my red/green lights shine for n cycles and my yellow light for m cycles. I hope I have been clear enough. If the code for my traffic light would be necessary, I can add it (I just didn't want to make this question even longer). I also don't necessarily need a traffic light, I just want to have these counters running in parallel in some way. Thanks in advance

Best Answer

You're way over-thinking this. There's no reason to make the counter a separate entity; you just end up writing a lot of code to do explicitly what the synthesis tools can do for you implicitly.

Also, for the example of traffic lights, you really only need one counter, because you only need one delay at a time. The same counter can handle different delays for different states.

Just make the counter part of the main state machine, something like this:

entity traffic_light is
  generic (
    green_time  : natural := 50;
    yellow_time : natural := 20;
  );
  port (
    clk         : in std_ulogic;
    rst         : in std_ulogic;
    -- lights are 3-bit fields that control red, yellow, green
    ns_lights   : out std_logic_vector (2 downto 0);
    ew_lights   : out std_logic_vector (2 downto 0);
  );
end entity;

architecture behav of traffic_light is
  signal state : natural;
  signal count : natural;
begin
  process (clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then         -- active-high synchronous reset
        state <= 0;
        ns_lights <= "100";
        ew_lights <= "100";
        count <= 100;
      else
        case state is

        when 0 =>               -- special start-up state; all lights red
          if count = 0 then
            ns_lights <= "001"; -- switch NS to green
            ew_lights <= "100";
            state <= 1;
            count <= green_time;
          else
            count <= count - 1;
          end if;

        when 1 =>               -- NS green, EW red
          if count = 0 then
            ns_lights <= "010"; -- switch NS to yellow
            ew_lights <= "100";
            state <= 2;
            count <= yellow_time;
          else
            count <= count - 1;
          end if;

        when 2 =>               -- NS yellow, EW red
          if count = 0 then
            ns_lights <= "100"; -- switch NS to red
            ew_lights <= "001"; -- switch EW to green
            state <= 3;
            count <= green_time;
          else
            count <= count - 1;
          end if;

        when 3 =>               -- NS red, EW green
          if count = 0 then
            ns_lights <= "100";
            ew_lights <= "010"; -- switch EW to yellow
            state <= 4;
            count <= yellow_time;
          else
            count <= count - 1;
          end if;

        when 4 =>               -- NS red, EW yellow
          if count = 0 then
            ns_lights <= "001"; -- switch NS to green
            ew_lights <= "100"; -- switch EW to red
            state <= 1;
            count <= green_time;
          else
            count <= count - 1;
          end if;

        when others =>
          state <= 0;
          ns_lights <= "100";   -- switch NS to red
          ew_lights <= "100";   -- switch EW to red
          count <= 100;
        end case;
      end if;
    end if;
  end process;
end architecture behav;