본문 바로가기
Digital Design/SoC

[AMBA] APB Protocol 설계 (Design with Verilog)

by 스테고사우르스 2023. 2. 9.

오늘은 AMBA2.0의 APB Protocol을 Verilog로 설계해보려고 합니다.

 

APB Protocol 이론은 다음 게시글을 참고해주세요.

APB: https://metastable.tistory.com/39

 

APB는 Bus입니다.

APB를 기반으로 Master와 Slave를 효율적으로 연결할 수 있습니다.

 

APB Protocol을 확인하려면 Master와 Slave를 만들어서 잘 연결이 되는지 확인하면 되는데요,

처음부터 Master와 Slave까지 설계하는 것은 어렵습니다.

 

따라서 오늘은 간단한 Slave만 만들고

input signal은 testbench에서 넣어주는 식으로 진행하겠습니다.

 

Slave안에 SFR을 만들어서 거기에 데이터를 읽고 쓰는 작업을 수행해보겠습니다.

 


Verilog Code

DUT

`timescale 1ns / 1ps

module apb_slave(

input            i_pclk,
input            i_presetn,
input      [7:0] i_paddr,
input            i_pwrite,
input      [7:0] i_pwdata,
input            i_psel,
input            i_penable,
input            i_busy,
input            i_err,

output reg       o_pready,
output reg [7:0] o_prdata,
output reg       o_slverr
    );
    
    parameter sfr_word_width = 8;
    parameter sfr_addr_width = 8;
    parameter sfr_depth = 256;
    
    reg [sfr_word_width-1:0] r_sfr [0:sfr_depth-1];
    wire access_state = i_psel & i_penable;
    
    always @ (posedge i_pclk or negedge i_presetn) begin        // o_pready
        if (!i_presetn) begin
            o_pready <= 1'd0;
        end
        else if (i_busy == 1'd1) begin
            o_pready <= 1'd0;
        end
        else if (i_busy == 1'd0) begin
            o_pready <= 1'd1;
        end
        else begin
            o_pready <= o_pready;
        end
    end
    
    always @ (posedge i_pclk or negedge i_presetn) begin           //write
        if (i_pwrite == 1'd1 && access_state) begin
            if (i_busy == 1'd0) begin
                r_sfr[i_paddr] <= i_pwdata;
            end
        end
    end
    
    always @ (posedge i_pclk or negedge i_presetn) begin         //read
        if (!i_presetn) begin
            o_prdata <= 8'd0;
        end
        else if (i_pwrite == 1'd0 && access_state) begin
            if (i_busy <= 1'd0) begin
                o_prdata <= r_sfr[i_paddr];
            end
        end
        else begin
            o_prdata <= o_prdata;
        end
    end
    
endmodule

APB signal들을 선언해주었습니다.

 

i_busy와 i_err는 apb와 관련 없는 신호입니다.

 

Slave에서 PREADY 신호를 Master에 전송해줘야 하는데

제가 만든 Slave는 아무 기능이 없어서 딱히 delay가 발생할 요소가 없습니다.

따라서 고의로 PREADY를  LOW로 만들기 위해 i_busy를 넣어주었습니다.

i_err로 마찬가지로 고의로 에러를 주기 위해 만들었습니다.

 

Access state에 PREADY가 HIGH이면 R/W를 진행하도록 설계하였습니다.

 

 

Testbench

`timescale 1ns / 1ps

module tb_apb_slave();

reg i_pclk;
reg i_presetn;
reg [7:0] i_paddr;
reg i_pwrite;
reg [7:0] i_pwdata;
reg i_psel;
reg i_penable;
reg i_busy;
reg i_err;

parameter sfr_word_width = 8;
parameter sfr_addr_width = 8;
parameter sfr_depth = 256;

wire o_pready;
wire [7:0] o_prdata;
wire o_slverr;

apb_slave apb_slave1 ( .i_pclk    (i_pclk),
                      .i_presetn  (i_presetn),
                      .i_paddr    (i_paddr),
                      .i_pwrite   (i_pwrite),
                      .i_pwdata   (i_pwdata),
                      .i_psel     (i_psel),
                      .i_penable  (i_penable),
                      .i_busy     (i_busy),
                      .i_err      (i_err),
                      
                      .o_pready   (o_pready),
                      .o_prdata   (o_prdata),
                      .o_slverr   (o_slverr)    );

always #5 i_pclk = ~i_pclk;
initial begin
    i_pclk = 1'd0;
    i_presetn = 1'd1;
    i_psel = 1'd0;
    i_penable = 1'd0;
    i_busy = 1'd0;
    i_err = 1'd0;
    #1
    i_presetn = 1'd0;
    #1
    i_presetn = 1'd1;
    #8
    i_paddr = 8'd13;
    i_pwrite = 1'd1;
    i_pwdata = 8'd66;
    i_psel = 1'd1;
    #10
    i_penable = 1'd1;
    #10
    i_busy = 1'd1;
    #10
    i_paddr = 8'd15;
    i_pwrite = 1'd1;
    i_pwdata = 8'd35;
    #10
    i_busy = 1'd0;
    #10
    i_pwrite = 1'd0;
    #20
    i_paddr = 8'd13;
    #10
    i_busy = 1'd1;
    #10
    i_paddr = 8'd15;
    #10
    i_busy = 1'd0;
    #30
    
    $finish;
end

endmodule

조금 지저분하긴 한데 datasheet에 나오는 타이밍들과 비슷하게 만들어보려고 했습니다.

 

 

Simulation

i_psel과 i_penable은 켜두어서 PREADY 조건만 맞으면 transfer를 진행합니다.

 

ACCESS_state에 전송준비가 되면 pclk rising edge에서

PREADY가 HIGH가 되고 R/W를 진행하는 것을 볼 수 있습니다.

 


 

댓글