VHDL arbiters part IV – advanced simulation

Chapter 3 of this tutorial series on VHDL arbiters ended with the simulation of a round-robin arbiter. Here is the picture again, for reference:

Let’s take a look again at the marker placed at 670 ns. Two masters (1 and 2) are requesting the bus, but as we know, only one of them can receive it at a given moment, and in this case, it is master 2. But here happens something strange: Master 1 drops its request, even given the fact that it never received a grant signal! Although such behavior is possible, it is not common. The usual behavior of a master is to keep the request signal asserted until it receives a grant.

Why did this happen in our simulation? Because it was based on a very simple testbench, that asserts and de-asserts request signals without any relationship with what happens in the system.

To avoid bad test benches, many times we will need to introduce additional blocks that emulate the behavior of other blocks on the simulated system. The testbench, and the additional emulation blocks, will be connected to the DUT (device under test). In this case, the DUT is the arbiter.

So we will introduce “agents” that emulate the real behavior of masters connected to an arbiter as mentioned above, i.e., modules that keep requesting the bus until they receive it. We will also want that the masters stop requesting the bus once they gained access to it.

What we want from our agents is to request the bus until they receive it. Once granted, they will stop requesting the bus and they will “use” it for some time. Once they completed the transaction, they will pause for some time, and request the bus again. This intermittent behavior of masters on a bus is typical of many computing systems. For example, each time you press a key a message is sent from the keyboard controller to the CPU. At the same time, the CPU may be downloading a file, so it is receiving packets from, for example, an Ethernet controller. The downloaded file is stored in memory, and again, the memory controller has to be given access to the bus to retrieve the data for the downloaded file.

The CPU bus, in this very simple access, is being requested by three agents: the memory controller, the communication controller, and the keyboard controller. Each agent has very different characteristics. The keyboard controller sends very short bursts of data with very long inactive times between bursts. The memory controller will issue very frequent, deep bursts of data. The communications controller is somewhere in the middle of the other two controllers, with respect to the frequency of data delivered. On the other hand, it is not uncommon for a communications controller to send data bursts much wider than those of the memory controller.

In summary, different agents access the bus on different frequencies, and once they access the bus, they keep it busy for different times, as shown in the following table:

AgentAccess frequencyData burst width
Keyboard controllerVery slowVery narrow
Memory controllerVery highMedium
Communication controllerHighVery wide

The arbiter in charge of the CPU bus must be able to cope with these, and many others agents.

Let’s see how we will change our simulation to introduce several realistic agents requesting the bus from our arbiter. Once this is done, we will simulate both the fixed-priority arbiter and the round-robin arbiter and compare how they cope with a specific arbitration scenario.

This is the code for the bus agent:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity bus_agent is
  port (
    clk                : in  std_logic;
    rstn               : in  std_logic;

    -- inputs
    gnt                : in  std_logic;
    busy_in            : in  std_logic;

    -- outputs
    req                : out std_logic;
    busy               : out std_logic;

    -- statistic outputs
    no_of_transactions : out integer;
    max_latency        : out integer;
    timeouts           : out integer
  );
end bus_agent;

architecture rtl of bus_agent is

  constant DURATION : natural := 6;
  constant PAUSE : natural := 30;
  constant TIMEOUT : natural := 127;

  type st_T is (idle, pause_st, req_st, timeout_st, send);
  signal st : st_T;
  signal packet_duration : integer;
  signal packet_pause : integer;
  signal latency : integer;
begin

  -- generate requests
  gen_req_pr : process (clk)
  begin
    if (rising_edge(clk)) then
      if (rstn = '0') then
        req <= '0';
        no_of_transactions <= 0;
        max_latency <= 0;
        timeouts <= 0;
        busy <= '0';
        st <= idle;
      else
        case st is
          when idle =>
            latency <= 0;
            st <= req_st;
          when req_st =>
            req <= '1';
            if (latency = TIMEOUT) then
              req <= '0';
              st <= timeout_st;
            elsif (gnt = '1' and busy_in = '0') then
              -- Grant received, update max_latency
              if (latency > max_latency) then
                max_latency <= latency;
              end if;
              no_of_transactions <= no_of_transactions + 1;
              packet_duration <= DURATION - 1;
              busy <= '1';
              st <= send;
            else
              latency <= latency + 1;
            end if;
          when timeout_st =>
            timeouts <= timeouts + 1;
            packet_pause <= pause;
            st <= pause_st;
          when send =>
            req <= '0';
            if (packet_duration = 0) then
              packet_pause <= PAUSE - 1;
              busy <= '0';
              st <= pause_st;
            else
              packet_duration <= packet_duration - 1;
            end if;
          when pause_st =>
            if (packet_pause = 0) then
              st <= idle;
            else
              packet_pause <= packet_pause - 1;
            end if;
        end case;
      end if;
    end if;
  end process gen_req_pr;

end rtl;

The code emulates an agent on the bus and also collects statistics on the behavior of the agent<->arbiter pair.

A state machine makes for the basic behavior of the agent. The agent requests the bus. Once it receives a grant signal, and the bus is not busy, it goes to the send state for packet_duration. Once it has sent its packet, it pauses until requesting it again.

The agent also measures the latency from the moment it requested the bus until it starts to send. It also implements a timeout in case it takes too long to receive the bus. It also keeps track of how many transactions it has made on the bus.

With the help of this agent emulator, we will test the behavior of a fixed priority arbiter vs. a round-robin one. The block diagram of this advanced testbench is as follows:

The testbench can be configured for different numbers of arbiters. It can be seen in the simulation that the fixed priority arbiter, as expected, adds a lot more latency to those agents with the lowest priority. Given a defined timeout value, the fixed priority arbiter is much more prone to hit the timeout wall and, for arbiters that serve more agents, it is more probable that some of them will never be able to get access to the bus because of the fixed priority.

You are invited to explore the simulation by yourself and compare how the TIMEOUT value and the number of arbiters in the system influence its behavior.

The following simulation was done with 14 agents, 7 connected to a round-robin arbiter and 7 to a fixed-priority arbiter. The TIMEOUT value was set to 127. It can be seen that the two last agents connected to the fixed-priority arbiter (12 and 13), NEVER get access to the bus. Can be also seen the latencies for the round-robin and the fixed-priority arbiters:

  • 11, 11, 16, 23, 30, 37, 51 (round-robin)
  • 7, 11, 20. 29. 38, <none>, <none>

The fixed-priority arbiter gives faster service to the highest priority agent, but the price for this speed is that the two last agents never receive the bus.

On our next and last entry on VHDL arbiters, we will do this simulation on HW.

The sources and testbench files for this simulation are available on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *