Round-Robin Arbiter Design in Verilog and SystemVerilog
Are you working on a project that requires multiple requests to access a shared resource? Then you may need an arbiter to decide which request to grant access to the resource. One of the most common types of arbiters used in digital designs is the round-robin arbiter. In this blog post, we will discuss how to design a round-robin arbiter in Verilog and SystemVerilog.
Why Round-Robin Arbiter is Important
Before we dive into the design of the round-robin arbiter, let's first discuss why it is important. The round-robin arbiter is used to prevent starvation and provide statistical fairness in a system. Starvation occurs when a request is repeatedly denied access to the shared resource, even though other requests are being granted access. The round-robin arbiter solves this problem by granting access to each request in a circular fashion, ensuring that no request is consistently denied access to the resource.
Statistical fairness is also important in systems with multiple requests for a shared resource. Without statistical fairness, a small number of requests may be granted access to the resource more often than other requests. This can lead to performance degradation or even system failure. The round-robin arbiter ensures that each request has an equal chance of being granted access to the resource, which promotes statistical fairness.
Designing the Round-Robin Arbiter in Verilog and SystemVerilog
Now let's move on to the design of the round-robin arbiter in Verilog and SystemVerilog. We will use the following design:
module RoundRobinArbiter #(
parameter NumRequests = 8
) (
input logic clk,
input logic rstN,
input logic [NumRequests-1:0] req,
output logic [NumRequests-1:0] grant
);
logic [NumRequests-1:0] mask, maskNext;
logic [NumRequests-1:0] maskedReq;
logic [NumRequests-1:0] unmaskedGrant;
logic [NumRequests-1:0] maskedGrant;
assign maskedReq = req & mask;
Arbiter #(
.NumRequests(NumRequests)
) arbiter (
.request(req),
.grant (unmaskedGrant)
);
Arbiter #(
.NumRequests(NumRequests)
) maskedArbiter (
.request(maskedReq),
.grant (maskedGrant)
);
assign grant = (maskedReq == '0) ? unmaskedGrant : maskedGrant;
always_comb begin
if (grant == '0) begin
maskNext = mask;
end
else begin
maskNext = '1;
for (int i = 0; i < NumRequests; i++) begin
maskNext[i] = 1'b0;
if (grant[i]) break;
end
end
end
always_ff @(posedge clk or negedge rstN) begin
if (!rstN) mask <= '1;
else mask <= maskNext;
end
endmodule
In this Verilog and SystemVerilog design of the round-robin arbiter, two fixed priority arbiters are utilized to manage requests for a shared resource. The first arbiter, the unmasked arbiter, processes the original request. The second arbiter, the masked arbiter, processes the request ANDed with a mask. The masked arbiter takes priority over the unmasked one. If there is no masked request, the unmasked arbiter's result is used. However, if there is a masked request, the masked arbiter's result takes precedence. The mask determines which request has priority.
The challenge in the round-robin arbiter design lies in the mask update logic. When the N-th bit is granted, the subsequent bits above N must have priority in the next cycle. To accomplish this, the MSB:N+1 bits are set to 1, while the remaining bits are set to 0. For example, if the grant vector is 00001000, the mask will be 11110000 in the next cycle. Requests in the MSB 4 bits are granted access, but if there are no requests in these bits, the unmasked arbiter's result is used. If no grant occurs during the cycle, the mask remains unchanged.
The maskedReq
expression is a bitwise AND between the original request req
and the mask. This variable ensures that only requests with priority (i.e., those with a set mask bit) are processed by the masked arbiter. The grant
output is determined by the ternary operator, which checks whether the maskedReq
expression is 0. If it is, the unmasked arbiter's result is used. If it is not, the masked arbiter's result is used.
The maskNext
variable is updated in the combinational block. When a request is granted, the bits above the granted bit are given priority in the next cycle. The for
loop in the combinational block iterates through the granted bits to determine which bit has the highest priority. The mask
variable is updated in the synchronous block using a flip-flop, which is triggered by a positive edge clock or negative edge reset signal.
Conclusion
In conclusion, the round-robin arbiter is an important component in digital designs that manage multiple requests for a shared resource. The design we discussed in this blog post utilizes two fixed priority arbiters, with the masked arbiter taking priority over the unmasked one. The mask update logic is a crucial element of the design, which ensures that requests are processed in a circular fashion, promoting statistical fairness and preventing starvation. By implementing this design in Verilog and SystemVerilog, engineers can ensure that their designs provide equal access to shared resources for all requests, leading to optimal system performance.