본문 바로가기
Digital Design/SoC

[SoC] DMAC_arbiter_top.v (최종)

by 스테고사우르스 2023. 4. 30.

현재 ARM의 DMAC Reference Manual을 보고 DMAC Arbiter를 설계하는 프로젝트를 진행하고 있습니다.

이게 ARM에서 추천한 DMAC 동작 메뉴얼을 보고 만든거라 DMAC Arbiter라고 하는건데,

사실 조금씩 수정해서 아무데나 범용성 있게 사용해도 될 것 같습니다.

이제 마지막 관문입니다!

 

앞서 Fixed Priority와 LRG 기반 Round-Robin Arbiter를 설계했었습니다.

오늘은 이 두 아비터를 merge한 top block을 업로드하려고 합니다.

 

참고로 지난 시간에 설계한 LRG Round-Robin Arbiter는 시뮬레이션을 좀 더 편하게 보려고

한 Master에게 1clk씩만 사용권을 부여했었습니다.

하지만 실제로는 여러 클락동안 사용해야겠죠?

따라서 REQ가 내려가면 Master의 bus 사용이 끝났다고 받아들이고 GNT를 중단하도록 수정했습니다.

 

※ 티스토리 코드블럭을 이용하면 띄어쓰기가 좀 바뀌어서 가독성이 떨어질 수 있다는 점 양해 부탁드려요!

 


Block Diagram

먼저 ARM DMAC Technical Reference Manual에 나와있는 상태도를 보겠습니다.

  • State Diagram

위의 상태도를 기반으로 Fixed Priority Arbiter와 Round-Robin Arbiter를 순차적으로 배치하겠습니다.

 

  • Block Diagram

Block Diagram으로는 이렇게 나타낼 수 있습니다.

우선 4개의 Master가 있다고 가정하였습니다.

이 Master에서 REQ가 들어오면 먼저 Fixed Priority Arbiter로 들어갑니다.

여기서 우선순위를 참고하여 GNT할 Master를 선택해줍니다.

 

만약 우선순위가 같다면 두 번째인 Round-Robin Arbiter로 들어갑니다.

여기서는 History QUEUE를 참고하여 GNT할 Master를 결정합니다.

최종적으로 GNT하게 되면 shift_case와 shift_trigger 신호를 이용해 History QUEUE를 업데이트합니다.

 

여기까지가 arbiter_top의 개요이고 이제 코드를 보겠습니다.

 

2023.05.16 : Block Diagram 수정

좀 더 예쁘게 바꿔보았습니다.

 

 


Verilog Code

DUT

  • dmac_arbiter_top.v
`timescale 1ns / 1ps

module dmac_arbiter_top(
input i_clk,
input i_reset,
input i_req0,
input i_req1,
input i_req2,
input i_req3,

output o_gnt0,
output o_gnt1,
output o_gnt2,
output o_gnt3
);

wire w_m0;
wire w_m1;
wire w_m2;
wire w_m3;

arbiter_fixed_priority arbiter_fp (.i_clk    (i_clk),
                                   .i_reset  (i_reset),
                                   .i_req0   (i_req0),
                                   .i_req1   (i_req1),
                                   .i_req2   (i_req2),
                                   .i_req3   (i_req3),
                                   .o_gnt0   (w_m0),
                                   .o_gnt1   (w_m1),
                                   .o_gnt2   (w_m2),
                                   .o_gnt3   (w_m3)  );
                                   
rrarbiter_lrg arbiter_lrg         (.i_clk    (i_clk),
                                   .i_reset  (i_reset),
                                   .i_req0   (w_m0),
                                   .i_req1   (w_m1),
                                   .i_req2   (w_m2),
                                   .i_req3   (w_m3),
                                   .o_gnt0   (o_gnt0),
                                   .o_gnt1   (o_gnt1),
                                   .o_gnt2   (o_gnt2),
                                   .o_gnt3   (o_gnt3)  );
                                   
endmodule

 

  • arbiter_fixed_priority.v
`timescale 1ns / 1ps

module arbiter_fixed_priority(
input i_clk,
input i_reset,
input i_req0,
input i_req1,
input i_req2,
input i_req3,

output reg o_gnt0,
output reg o_gnt1,
output reg o_gnt2,
output reg o_gnt3
);

//PRIORITY: M2 > M1 = M2 = M3
always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0000;
    end
    else if (i_req2) begin
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0100;
    end
    else if (i_req0 || i_req1 || i_req3) begin
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {i_req3, i_req2, i_req1, i_req0};
    end
    else begin  //IF NO REQUEST
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0000;
    end
end
    
endmodule

Master의 우선순위는 M2 > M0 = M1 = M3로 하였습니다.

 

  • rrarbiter_lrg.v
`timescale 1ns / 1ps

module rrarbiter_lrg (
input i_clk,
input i_reset,
input i_req0,
input i_req1,
input i_req2,
input i_req3,

output reg o_gnt0,
output reg o_gnt1,
output reg o_gnt2,
output reg o_gnt3
);

parameter [3:0] m_0 = 4'b0001;
parameter [3:0] m_1 = 4'b0010;
parameter [3:0] m_2 = 4'b0100;
parameter [3:0] m_3 = 4'b1000;

reg [3:0] queue [0:3];
reg [1:0] shift_case;
reg shift_trigger;
reg req_delay0;
reg req_delay1;
reg req_delay2;
reg req_delay3;
wire [3:0] req = {i_req3, i_req2, i_req1, i_req0};
wire [3:0] req_delay = {req_delay3, req_delay2, req_delay1, req_delay0};
assign req0_fall = ~i_req0 & req_delay0;
assign req1_fall = ~i_req1 & req_delay1;
assign req2_fall = ~i_req2 & req_delay2;
assign req3_fall = ~i_req3 & req_delay3;


always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        req_delay0 <= 1'b0;
    end
    else begin
        req_delay0 <= i_req0;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        req_delay1 <= 1'b0;
    end
    else begin
        req_delay1 <= i_req1;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        req_delay2 <= 1'b0;
    end
    else begin
        req_delay2 <= i_req2;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        req_delay3 <= 1'b0;
    end
    else begin
        req_delay3 <= i_req3;
    end
end
/*
wire [3:0] queue0 = queue[0];
wire [3:0] queue1 = queue[1];
wire [3:0] queue2 = queue[2];
wire [3:0] queue3 = queue[3];
*/

/* history queue
*  _____3________2_______1_______0________
  | recently | ... ..| .. .. | granted   |
  | granted  | ..->..| ..->. | long time |
  |__________|_______|_______|___ago_____|
1. REQn input
2. Is REQn in queue[0]? (!(queue[0] & req == 0))
	Yes: GNT, update(shift) history queue
	No: go to queue[1]
3. Is REQn in queue[1]?
	Yes: GNT, update(shift) history queue
	No: go to queue[2]
	.
	.
**Initial Value: queue <= {M3, M2, M1, M0} 
*/

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0000;
	    shift_case <= 2'b00;
	    shift_trigger <= 1'b0;
    end
    else if (!((queue[0] & req_delay) == 4'b0000)) begin
        case (queue[0])
	        m_0: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0001;
		             if (req0_fall) begin
	                     shift_case <= 2'b11;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_1: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0010;
	                 if (req1_fall) begin
	                     shift_case <= 2'b11;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_2: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0100;
	                 if (req2_fall) begin
	                     shift_case <= 2'b11;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_3: begin 
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b1000;
	                 if (req3_fall) begin
	                     shift_case <= 2'b11;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        default: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {o_gnt3, o_gnt2, o_gnt1, o_gnt0};
			         shift_case <= shift_case;
			         shift_trigger <= shift_trigger;
		         end
	    endcase
    end
    else if (!((queue[1] & req_delay) == 4'b0000)) begin
        case (queue[1])
            m_0: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0001;
		             if (req0_fall) begin
	                     shift_case <= 2'b10;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_1: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0010;
	                 if (req1_fall) begin
	                     shift_case <= 2'b10;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_2: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0100;
	                 if (req2_fall) begin
	                     shift_case <= 2'b10;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_3: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b1000;
	                 if (req3_fall) begin
	                     shift_case <= 2'b10;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        default: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {o_gnt3, o_gnt2, o_gnt1, o_gnt0};
			         shift_case <= shift_case;
			         shift_trigger <= shift_trigger;
		         end
	    endcase
    end
    else if (!((queue[2] & req_delay) == 4'b0000)) begin
        case (queue[2])
	        m_0: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0001;
	                 if (req0_fall) begin
	                     shift_case <= 2'b01;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_1: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0010;
	                 if (req1_fall) begin
	                     shift_case <= 2'b01;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_2: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0100;
	                 if (req2_fall) begin
	                     shift_case <= 2'b01;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_3: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b1000;
	                 if (req3_fall) begin
	                     shift_case <= 2'b01;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        default: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {o_gnt3, o_gnt2, o_gnt1, o_gnt0};
			         shift_case <= shift_case;
			         shift_trigger <= shift_trigger;
		         end
	    endcase
    end
    else if (!((queue[3] & req_delay) == 4'b0000)) begin
        case (queue[3])
	        m_0: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0001;
	                 if (req0_fall) begin
	                     shift_case <= 2'b00;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_1: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0010;
	                 if (req1_fall) begin
	                     shift_case <= 2'b00;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_2: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b0100;
	                 if (req2_fall) begin
	                     shift_case <= 2'b00;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        m_3: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= 4'b1000;
	                 if (req3_fall) begin
	                     shift_case <= 2'b00;
		                 shift_trigger <= ~shift_trigger;
		             end
		             else begin
		                 shift_case <= shift_case;
		                 shift_trigger <= shift_trigger;
		             end
	             end
	        default: begin
		             {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {o_gnt3, o_gnt2, o_gnt1, o_gnt0};
			         shift_case <= shift_case;
			         shift_trigger <= shift_trigger;
		         end
	    endcase	
    end
    else begin
        {o_gnt3, o_gnt2, o_gnt1, o_gnt0} <= {o_gnt3, o_gnt2, o_gnt1, o_gnt0};
	    shift_case <= shift_case;
	    shift_trigger <= shift_trigger;
    end
end

//shift_case
always @ (shift_trigger) begin
    case (shift_case)
        2'b00: begin //when queue[3] POP
	           //No-op
	       end
        2'b01: begin //when queue[2] POP
	           queue[3] <= queue[2];
		       queue[2] <= queue[3];
		       //queue[1] <= queue[1];
		       //queue[0] <= queue[0];
	       end
        2'b10: begin //when queue[1] POP
	           queue[3] <= queue[1];
		       queue[2] <= queue[3];
		       queue[1] <= queue[2];
		        //queue[0] <= queue[0];
	       end
        2'b11: begin //when queue[0] POP
	           queue[3] <= queue[0];
		       queue[2] <= queue[3];
		       queue[1] <= queue[2];
		       queue[0] <= queue[1];
	       end
        default: begin
	           //No-op
	       end
    endcase
end

endmodule

FF처리 (req_delay <= i_req)하여 req_delay를 만들어줍니다.

이후 ~i_req & req_delay를 이용해서 REQ의 falling edge를 캡쳐하는 req_fall을 만들어줍니다.

이 req_fall이 뜨면 버스 사용이 끝난 것(REQ이 내려갔다)이므로 history queue를 shift 해줍니다.

queue가 바뀌었다면 case문에서 걸려서 bus 사용권이 바뀌게 됩니다.

 

이런 방식으로 LRG 알고리즘을 짜보았습니다.

 

 

Testbench

`timescale 1ns / 1ps

module tb_dmac_arbiter_top();

reg i_clk;
reg i_reset;
reg i_req0;
reg i_req1;
reg i_req2;
reg i_req3;

wire o_gnt0;
wire o_gnt1;
wire o_gnt2;
wire o_gnt3;

dmac_arbiter_top arbiter_top (.i_clk    (i_clk),
                              .i_reset  (i_reset),
                              .i_req0   (i_req0),
                              .i_req1   (i_req1),
                              .i_req2   (i_req2),
                              .i_req3   (i_req3),
                              .o_gnt0   (o_gnt0),
                              .o_gnt1   (o_gnt1),
                              .o_gnt2   (o_gnt2),
                              .o_gnt3   (o_gnt3)  );

always #5 i_clk = ~i_clk;

initial begin
    i_clk = 1'b0; i_reset = 1'b1;
    rrarbiter_lrg.queue[0] = 4'b0001;
    rrarbiter_lrg.queue[1] = 4'b0010;
    rrarbiter_lrg.queue[2] = 4'b0100;
    rrarbiter_lrg.queue[3] = 4'b1000;
    {i_req3, i_req2, i_req1, i_req0} = 4'b0000;   #1
    i_reset = 1'b0;                               #1
    i_reset = 1'b1;                               #8
    
    {i_req3, i_req2, i_req1, i_req0} = 4'b0100;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0110;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1001;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1101;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0101;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1111;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0101;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b0011;   #20
    {i_req3, i_req2, i_req1, i_req0} = 4'b1010;   #20
    $finish;
end

endmodule

 

 

Simulation

(Waveform은 컴퓨터에서 클릭해서 보면 더 잘보입니다.)

REQ2가 포함되어 있으면 무조건 M2 먼저 GNT해줍니다.

그렇지 않다면 history queue를 참고하여 우선권을 결정합니다.

 


 

댓글