Digital Design/SoC

[SoC] DMAC_arbiter_top.v (최종)

스테고사우르스 2023. 4. 30. 17:39

현재 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를 참고하여 우선권을 결정합니다.