0. 개요
AXI4-Lite는 ARM의 AMBA(Advanced Microcontroller Bus Architecture) 표준에 포함된 경량화된 버스 프로토콜로, 단순한 제어 인터페이스를 요구하는 시스템에 사용된다. AXI4-Lite는 AXI4의 전체 기능을 축소한 버전으로, 고속 데이터 전송보다는 간단한 레지스터 읽기/쓰기 작업에 많이 사용된다. 이 글에서는 Verilog로 작성된 AXI4_Lite_Master와 AXI4_Lite_Slave 모듈을 통해 AXI4-Lite 버스의 동작을 살펴보고, WRITE와 READ 트랜잭션의 각 채널(AW, W, B, AR, R)을 분석해보겠다.
AXI4-Lite는 다음과 같은 주요 특징이 있다.
- 독립적인 채널 구조: 주소(AR/AW), 데이터(W/R), 응답(B) 채널이 분리되어 병렬 처리가 가능.
- 단일 트랜잭션: Burst 전송을 지원하지 않음.
- 32비트 데이터 폭: 주소와 데이터는 기본적으로 32비트로 고정.
1. WRITE Transaction
위 다이어그램은 WRITE 트랜잭션에서 AW(Address Write), W(Write Data), B(Write Response) 채널 간의 핸드셰이크 의존성을 보여준다.
1) AW CHANNEL
- AWVALID → AWREADY:
AWVALID는 Master가 Slave에게 주소와 제어 정보를 보낼 준비가 되었음을 나타낸다.
Master에서 AWVALID가 HIGH일 때 Slave의 AWREADY가 응답하면 트랜잭션이 진행된다.
(1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
output logic [ 3:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
// WRITE Transaction, AW Channel transfer
typedef enum {
AW_IDLE_S,
AW_VALID_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
end else begin
aw_state <= aw_state_next;
end
end
always_comb begin
aw_state_next = aw_state;
AWVALID = 1'b0;
AWADDR = addr;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (req && write) begin
aw_state_next = AW_VALID_S;
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
if (AWREADY) aw_state_next = AW_IDLE_S;
end
endcase
end
endmodule
(2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
input logic [ 3:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
);
logic [31:0] slv_reg0, slv_reg1, slv_reg2, slv_reg3;
// WRITE Transaction, AW Channel transfer
typedef enum bit {
AW_IDLE_S,
AW_READY_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
logic [3:0] aw_addr_reg, aw_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
aw_addr_reg <= 0;
end else begin
aw_state <= aw_state_next;
aw_addr_reg <= aw_addr_next;
end
end
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWREADY = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWREADY = 1'b0;
if (AWVALID) begin
aw_state_next = AW_READY_S;
aw_addr_next = AWADDR;
end
end
AW_READY_S: begin
AWREADY = 1'b1;
if (WVALID) aw_state_next = AW_IDLE_S;
end
endcase
end
endmodule
위 코드를 보게 되면 Mater에 Write 신호가 들어오게 되면 Master에서 AWVALID를 HIGH로 만들어 Slave로 전달되고, Slave에서는 WADDR을 래칭하게된다. 이후 Slave의 AWREADY가 응답하는 모습을 볼 수 있다.
2) W CHANNEL
- WVALID → WREADY:
WVALID는 Master가 데이터를 보낼 준비가 되었음을 나타낸다. WREADY는 Slave가 데이터를 수신할 준비가 되었음을 나타낸다.
AWVALID와 AWREADY 핸드셰이크와 동시에 WVALID와 WREADY가 동작하며, 데이터 전송이 이루어지게 된다.
(1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
output logic [ 3:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// WRITE Transaction, W Channel
output logic [31:0] WDATA,
output logic WVALID,
input logic WREADY,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
// WRITE Transaction, AW Channel transfer
typedef enum {
AW_IDLE_S,
AW_VALID_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
end else begin
aw_state <= aw_state_next;
end
end
always_comb begin
aw_state_next = aw_state;
AWVALID = 1'b0;
AWADDR = addr;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (req && write) begin
aw_state_next = AW_VALID_S;
end
end
AW_VALID_S: begin
AWADDR = addr;
AWVALID = 1'b1;
if (AWREADY) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum {
W_IDLE_S,
W_VALID_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WVALID = 1'b0;
WDATA = wdata;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (req && write) begin
w_state_next = W_VALID_S;
end
end
W_VALID_S: begin
WVALID = 1'b1;
if (WREADY) w_state_next = W_IDLE_S;
end
endcase
end
endmodule
(2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
input logic [ 3:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// WRITE Transaction, W Channel
input logic [31:0] WDATA,
input logic WVALID,
output logic WREADY,
);
// WRITE Transaction, AW Channel transfer
typedef enum bit {
AW_IDLE_S,
AW_READY_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
logic [3:0] aw_addr_reg, aw_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
aw_addr_reg <= 0;
end else begin
aw_state <= aw_state_next;
aw_addr_reg <= aw_addr_next;
end
end
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWREADY = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWREADY = 1'b0;
if (AWVALID) begin
aw_state_next = AW_READY_S;
aw_addr_next = AWADDR;
end
end
AW_READY_S: begin
AWREADY = 1'b1;
if (WVALID) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum bit {
W_IDLE_S,
W_READY_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWVALID) w_state_next = W_READY_S;
end
W_READY_S: begin
w_state_next = W_IDLE_S;
WREADY = 1'b1;
case (aw_addr_reg[3:2])
2'd0: slv_reg0 = WDATA;
2'd1: slv_reg1 = WDATA;
2'd2: slv_reg2 = WDATA;
2'd3: slv_reg3 = WDATA;
endcase
end
endcase
end
endmodule
위 코드에서도 마찬가지로, VALID, READY 핸드쉐이킹이 일어나는 순간, 지정된 ADDR의 Slave Register에 값이 업데이트 되는 것을 확인할 수 있다.
3) B CHANNEL
- BVALID → BREADY:
BVALID는 Slave가 WRITE 응답(예: 성공/실패)을 Master에게 보낼 준비가 되었음을 나타낸다. BREADY는 Master가 응답을 수신할 준비가 되었음을 확인한다.
WVALID와 WREADY가 완료된 후 BVALID가 활성화되며, 이후 WRITE 트랜잭션이 마무리 된다.
(1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
output logic [ 3:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// WRITE Transaction, W Channel
output logic [31:0] WDATA,
output logic WVALID,
input logic WREADY,
// WRITE Transaction, B Channel
input logic [ 1:0] BRESP,
input logic BVALID,
output logic BREADY,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
logic [1:0] b_resp;
logic w_ack, r_ack;
assign ack = w_ack || r_ack;
typedef enum {
AW_IDLE_S,
AW_VALID_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
end else begin
aw_state <= aw_state_next;
end
end
always_comb begin
aw_state_next = aw_state;
AWVALID = 1'b0;
AWADDR = addr;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (req && write) begin
aw_state_next = AW_VALID_S;
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
if (AWREADY) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum {
W_IDLE_S,
W_VALID_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WVALID = 1'b0;
WDATA = wdata;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (req && write) begin
w_state_next = W_VALID_S;
end
end
W_VALID_S: begin
WVALID = 1'b1;
if (WREADY) w_state_next = W_IDLE_S;
end
endcase
end
// WRITE Transaction, B Channel transfer
typedef enum {
B_IDLE_S,
B_READY_S
} b_state_e;
b_state_e b_state, b_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
end else begin
b_state <= b_state_next;
end
end
always_comb begin
b_state_next = b_state;
w_ack = 1'b0;
BREADY = 1'b0;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
w_ack = 1'b0;
if (WVALID) begin
end
end
B_READY_S: begin
BREADY = 1'b1;
if (BVALID) begin
b_state_next = B_IDLE_S;
b_resp = BRESP;
w_ack = 1'b1;
end
end
endcase
end
endmodule
(2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
input logic [ 3:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// WRITE Transaction, W Channel
input logic [31:0] WDATA,
input logic WVALID,
output logic WREADY,
// WRITE Transaction, B Channel
output logic [ 1:0] BRESP,
output logic BVALID,
input logic BREADY,
);
logic [31:0] slv_reg0, slv_reg1, slv_reg2, slv_reg3;
// WRITE Transaction, AW Channel transfer
typedef enum bit {
AW_IDLE_S,
AW_READY_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
logic [3:0] aw_addr_reg, aw_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
aw_addr_reg <= 0;
end else begin
aw_state <= aw_state_next;
aw_addr_reg <= aw_addr_next;
end
end
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWREADY = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWREADY = 1'b0;
if (AWVALID) begin
aw_state_next = AW_READY_S;
aw_addr_next = AWADDR;
end
end
AW_READY_S: begin
AWREADY = 1'b1;
if (WVALID) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum bit {
W_IDLE_S,
W_READY_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWVALID) w_state_next = W_READY_S;
end
W_READY_S: begin
w_state_next = W_IDLE_S;
WREADY = 1'b1;
case (aw_addr_reg[3:2])
2'd0: slv_reg0 = WDATA;
2'd1: slv_reg1 = WDATA;
2'd2: slv_reg2 = WDATA;
2'd3: slv_reg3 = WDATA;
endcase
end
endcase
end
// WRITE Transaction, B Channel transfer
typedef enum bit {
B_IDLE_S,
B_VALID_S
} b_state_e;
b_state_e b_state, b_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
end else begin
b_state <= b_state_next;
end
end
always_comb begin
b_state_next = b_state;
BRESP = 2'b00; // OK
BVALID = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) b_state_next = B_VALID_S;
end
B_VALID_S: begin
BRESP = 2'b00; // OK
BVALID = 1'b1;
if (BREADY) b_state_next = B_IDLE_S;
end
endcase
end
endmodule
2. READ Transaction
위 다이어그램은 READ 트랜잭션에서 AR(Address Read)와 R(Read Data) 채널 간의 핸드셰이크 의존성을 나타내고 있다.
1) AR CHANNEL
- ARVALID → ARREADY:
ARVALID는 Master가 Slave에게 읽기 요청 주소를 보낼 준비가 되었음을 나타낸다. ARREADY는 Slave가 해당 주소를 수신할 준비가 되었음을 확인한다.
ARVALID가 HIGH일 때 ARREADY가 응답해야 읽기 요청이 시작된다.
(1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// READ Transaction, AR Channel
output logic [3:0] ARADDR,
output logic ARVALID,
input logic ARREADY,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
// READ Transaction, AR Channel transfer
typedef enum {
AR_IDLE_S,
AR_VALID_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
end else begin
ar_state <= ar_state_next;
end
end
always_comb begin
ar_state_next = ar_state;
ARVALID = 1'b0;
ARADDR = addr;
case (ar_state)
AR_IDLE_S: begin
ARVALID = 1'b0;
if (req && ~write) begin
ar_state_next = AR_VALID_S;
end
end
AR_VALID_S: begin
ARADDR = addr;
ARVALID = 1'b1;
if (ARREADY) ar_state_next = AR_IDLE_S;
end
endcase
end
endmodule
(2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// READ Transaction, AR Channel
input logic [ 3:0] ARADDR,
input logic ARVALID,
output logic ARREADY,
// READ Transaction, R Channel
output logic [31:0] RDATA,
output logic RVALID,
input logic RREADY,
output logic [ 1:0] RRESP
);
// READ Transaction, AR Channel transfer
typedef enum bit {
AR_IDLE_S,
AR_READY_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
logic [3:0] ar_addr_reg, ar_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
ar_addr_reg <= 0;
end else begin
ar_state <= ar_state_next;
ar_addr_reg <= ar_addr_next;
end
end
always_comb begin
ar_state_next = ar_state;
ar_addr_next = ar_addr_reg;
ARREADY = 1'b0;
case (ar_state)
AR_IDLE_S: begin
ARREADY = 1'b0;
if (ARVALID) begin
ar_state_next = AR_READY_S;
ar_addr_next = ARADDR;
end
end
AR_READY_S: begin
ARREADY = 1'b1;
ar_state_next = AR_IDLE_S;
end
endcase
end
endmodule
2) R CHANNEL
- RVALID → RREADY:
RVALID는 Slave가 읽은 데이터를 Master에게 보낼 준비가 되었음을 나타낸다. RREADY는 Master가 데이터를 수신할 준비가 되었음을 확인한다.
ARVALID와 ARREADY 핸드셰이크가 완료된 후 RVALID와 RREADY가 동작하며, 데이터 전송이 이루어진다.
(1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// READ Transaction, AR Channel
output logic [3:0] ARADDR,
output logic ARVALID,
input logic ARREADY,
// READ Transaction, R Channel
input logic [31:0] RDATA,
input logic RVALID,
output logic RREADY,
input logic [ 1:0] RRESP,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
logic [1:0] b_resp;
logic w_ack, r_ack;
assign ack = w_ack || r_ack;
// READ Transaction, AR Channel transfer
typedef enum {
AR_IDLE_S,
AR_VALID_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
end else begin
ar_state <= ar_state_next;
end
end
always_comb begin
ar_state_next = ar_state;
ARVALID = 1'b0;
ARADDR = addr;
case (ar_state)
AR_IDLE_S: begin
ARVALID = 1'b0;
if (req && ~write) begin
ar_state_next = AR_VALID_S;
end
end
AR_VALID_S: begin
ARADDR = addr;
ARVALID = 1'b1;
if (ARREADY) ar_state_next = AR_IDLE_S;
end
endcase
end
// READ Transaction, R Channel transfer
typedef enum {
R_IDLE_S,
R_READY_S
} r_state_e;
r_state_e r_state, r_state_next;
logic [31:0] r_data_reg, r_data_next;
assign rdata = r_data_reg;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
r_state <= R_IDLE_S;
r_data_reg <= 0;
end else begin
r_state <= r_state_next;
r_data_reg <= r_data_next;
end
end
always_comb begin
r_state_next = r_state;
r_data_next = r_data_reg;
RREADY = 1'b0;
r_ack = 1'b0;
case (r_state)
R_IDLE_S: begin
RREADY = 1'b0;
r_ack = 1'b0;
if (ARVALID) begin
r_state_next = R_READY_S;
end
end
R_READY_S: begin
RREADY = 1'b1;
if (RVALID) begin
r_data_next = RDATA;
r_state_next = R_IDLE_S;
r_ack = 1'b1;
end
end
endcase
end
endmodule
(2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// READ Transaction, AR Channel
input logic [ 3:0] ARADDR,
input logic ARVALID,
output logic ARREADY,
// READ Transaction, R Channel
output logic [31:0] RDATA,
output logic RVALID,
input logic RREADY,
output logic [ 1:0] RRESP
);
logic [31:0] slv_reg0, slv_reg1, slv_reg2, slv_reg3;
// READ Transaction, AR Channel transfer
typedef enum bit {
AR_IDLE_S,
AR_READY_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
logic [3:0] ar_addr_reg, ar_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
ar_addr_reg <= 0;
end else begin
ar_state <= ar_state_next;
ar_addr_reg <= ar_addr_next;
end
end
always_comb begin
ar_state_next = ar_state;
ar_addr_next = ar_addr_reg;
ARREADY = 1'b0;
case (ar_state)
AR_IDLE_S: begin
ARREADY = 1'b0;
if (ARVALID) begin
ar_state_next = AR_READY_S;
ar_addr_next = ARADDR;
end
end
AR_READY_S: begin
ARREADY = 1'b1;
ar_state_next = AR_IDLE_S;
end
endcase
end
// READ Transaction, R Channel transfer
typedef enum bit {
R_IDLE_S,
R_VALID_S
} r_state_e;
r_state_e r_state, r_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
r_state <= R_IDLE_S;
end else begin
r_state <= r_state_next;
end
end
always_comb begin
r_state_next = r_state;
RVALID = 1'b0;
RRESP = 2'b00;
case (r_state)
R_IDLE_S: begin
RVALID = 1'b0;
RRESP = 2'b00;
if (ARREADY && ARVALID) r_state_next = R_VALID_S;
end
R_VALID_S: begin
if(RREADY) r_state_next = R_IDLE_S;
RVALID = 1'b1;
RRESP = 2'b00;
case (ar_addr_reg[3:2])
2'd0: RDATA = slv_reg0;
2'd1: RDATA = slv_reg1;
2'd2: RDATA = slv_reg2;
2'd3: RDATA = slv_reg3;
endcase
end
endcase
end
endmodule
3. Full Code
1) AXI4 Lite Master
`timescale 1ns / 1ps
module AXI4_Lite_Master (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
output logic [ 3:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// WRITE Transaction, W Channel
output logic [31:0] WDATA,
output logic WVALID,
input logic WREADY,
// WRITE Transaction, B Channel
input logic [ 1:0] BRESP,
input logic BVALID,
output logic BREADY,
// READ Transaction, AR Channel
output logic [3:0] ARADDR,
output logic ARVALID,
input logic ARREADY,
// READ Transaction, R Channel
input logic [31:0] RDATA,
input logic RVALID,
output logic RREADY,
input logic [ 1:0] RRESP,
// internal signals
input logic [ 3:0] addr,
input logic [31:0] wdata,
input logic write,
output logic [31:0] rdata,
input logic req,
output logic ack
);
logic [1:0] b_resp;
logic w_ack, r_ack;
assign ack = w_ack || r_ack;
// WRITE Transaction, AW Channel transfer
typedef enum {
AW_IDLE_S,
AW_VALID_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
end else begin
aw_state <= aw_state_next;
end
end
always_comb begin
aw_state_next = aw_state;
AWVALID = 1'b0;
AWADDR = addr;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (req && write) begin
aw_state_next = AW_VALID_S;
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
if (AWREADY) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum {
W_IDLE_S,
W_VALID_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WVALID = 1'b0;
WDATA = wdata;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (req && write) begin
w_state_next = W_VALID_S;
end
end
W_VALID_S: begin
WVALID = 1'b1;
if (WREADY) w_state_next = W_IDLE_S;
end
endcase
end
// WRITE Transaction, B Channel transfer
typedef enum {
B_IDLE_S,
B_READY_S
} b_state_e;
b_state_e b_state, b_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
end else begin
b_state <= b_state_next;
end
end
always_comb begin
b_state_next = b_state;
w_ack = 1'b0;
BREADY = 1'b0;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
w_ack = 1'b0;
if (WVALID) begin
b_state_next = B_READY_S;
end
end
B_READY_S: begin
BREADY = 1'b1;
if (BVALID) begin
b_state_next = B_IDLE_S;
b_resp = BRESP;
w_ack = 1'b1;
end
end
endcase
end
// READ Transaction, AR Channel transfer
typedef enum {
AR_IDLE_S,
AR_VALID_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
end else begin
ar_state <= ar_state_next;
end
end
always_comb begin
ar_state_next = ar_state;
ARVALID = 1'b0;
ARADDR = addr;
case (ar_state)
AR_IDLE_S: begin
ARVALID = 1'b0;
if (req && ~write) begin
ar_state_next = AR_VALID_S;
end
end
AR_VALID_S: begin
ARADDR = addr;
ARVALID = 1'b1;
if (ARREADY) ar_state_next = AR_IDLE_S;
end
endcase
end
// READ Transaction, R Channel transfer
typedef enum {
R_IDLE_S,
R_READY_S
} r_state_e;
r_state_e r_state, r_state_next;
logic [31:0] r_data_reg, r_data_next;
assign rdata = r_data_reg;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
r_state <= R_IDLE_S;
r_data_reg <= 0;
end else begin
r_state <= r_state_next;
r_data_reg <= r_data_next;
end
end
always_comb begin
r_state_next = r_state;
r_data_next = r_data_reg;
RREADY = 1'b0;
r_ack = 1'b0;
case (r_state)
R_IDLE_S: begin
RREADY = 1'b0;
r_ack = 1'b0;
if (ARVALID) begin
r_state_next = R_READY_S;
end
end
R_READY_S: begin
RREADY = 1'b1;
if (RVALID) begin
r_data_next = RDATA;
r_state_next = R_IDLE_S;
r_ack = 1'b1;
end
end
endcase
end
endmodule
2) AXI4 Lite Slave
`timescale 1ns / 1ps
module AXI4_Lite_Slave (
// Global Signals
input logic ACLK,
input logic ARESETn,
// WRITE Transaction, AW Channel
input logic [ 3:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// WRITE Transaction, W Channel
input logic [31:0] WDATA,
input logic WVALID,
output logic WREADY,
// WRITE Transaction, B Channel
output logic [ 1:0] BRESP,
output logic BVALID,
input logic BREADY,
// READ Transaction, AR Channel
input logic [ 3:0] ARADDR,
input logic ARVALID,
output logic ARREADY,
// READ Transaction, R Channel
output logic [31:0] RDATA,
output logic RVALID,
input logic RREADY,
output logic [ 1:0] RRESP
);
logic [31:0] slv_reg0, slv_reg1, slv_reg2, slv_reg3;
// WRITE Transaction, AW Channel transfer
typedef enum bit {
AW_IDLE_S,
AW_READY_S
} aw_state_e;
aw_state_e aw_state, aw_state_next;
logic [3:0] aw_addr_reg, aw_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
aw_state <= AW_IDLE_S;
aw_addr_reg <= 0;
end else begin
aw_state <= aw_state_next;
aw_addr_reg <= aw_addr_next;
end
end
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWREADY = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWREADY = 1'b0;
if (AWVALID) begin
aw_state_next = AW_READY_S;
aw_addr_next = AWADDR;
end
end
AW_READY_S: begin
AWREADY = 1'b1;
if (WVALID) aw_state_next = AW_IDLE_S;
end
endcase
end
// WRITE Transaction, W Channel transfer
typedef enum bit {
W_IDLE_S,
W_READY_S
} w_state_e;
w_state_e w_state, w_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
end else begin
w_state <= w_state_next;
end
end
always_comb begin
w_state_next = w_state;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWVALID) w_state_next = W_READY_S;
end
W_READY_S: begin
w_state_next = W_IDLE_S;
WREADY = 1'b1;
case (aw_addr_reg[3:2])
2'd0: slv_reg0 = WDATA;
2'd1: slv_reg1 = WDATA;
2'd2: slv_reg2 = WDATA;
2'd3: slv_reg3 = WDATA;
endcase
end
endcase
end
// WRITE Transaction, B Channel transfer
typedef enum bit {
B_IDLE_S,
B_VALID_S
} b_state_e;
b_state_e b_state, b_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
end else begin
b_state <= b_state_next;
end
end
always_comb begin
b_state_next = b_state;
BRESP = 2'b00; // OK
BVALID = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) b_state_next = B_VALID_S;
end
B_VALID_S: begin
BRESP = 2'b00; // OK
BVALID = 1'b1;
if (BREADY) b_state_next = B_IDLE_S;
end
endcase
end
// READ Transaction, AR Channel transfer
typedef enum bit {
AR_IDLE_S,
AR_READY_S
} ar_state_e;
ar_state_e ar_state, ar_state_next;
logic [3:0] ar_addr_reg, ar_addr_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
ar_state <= AR_IDLE_S;
ar_addr_reg <= 0;
end else begin
ar_state <= ar_state_next;
ar_addr_reg <= ar_addr_next;
end
end
always_comb begin
ar_state_next = ar_state;
ar_addr_next = ar_addr_reg;
ARREADY = 1'b0;
case (ar_state)
AR_IDLE_S: begin
ARREADY = 1'b0;
if (ARVALID) begin
ar_state_next = AR_READY_S;
ar_addr_next = ARADDR;
end
end
AR_READY_S: begin
ARREADY = 1'b1;
ar_state_next = AR_IDLE_S;
end
endcase
end
// READ Transaction, R Channel transfer
typedef enum bit {
R_IDLE_S,
R_VALID_S
} r_state_e;
r_state_e r_state, r_state_next;
always_ff @(posedge ACLK) begin
if (!ARESETn) begin
r_state <= R_IDLE_S;
end else begin
r_state <= r_state_next;
end
end
always_comb begin
r_state_next = r_state;
RVALID = 1'b0;
RRESP = 2'b00;
case (r_state)
R_IDLE_S: begin
RVALID = 1'b0;
RRESP = 2'b00;
if (ARREADY && ARVALID) r_state_next = R_VALID_S;
end
R_VALID_S: begin
if(RREADY) r_state_next = R_IDLE_S;
RVALID = 1'b1;
RRESP = 2'b00;
case (ar_addr_reg[3:2])
2'd0: RDATA = slv_reg0;
2'd1: RDATA = slv_reg1;
2'd2: RDATA = slv_reg2;
2'd3: RDATA = slv_reg3;
endcase
end
endcase
end
endmodule
'AMBA BUS > AXI BUS' 카테고리의 다른 글
AMBA AXI BUS - 04. VIVADO AXI4-Lite Template 분석 (0) | 2025.03.07 |
---|---|
AMBA AXI BUS - 02. Example of Ready / Valid Handshake (0) | 2025.02.22 |
AMBA AXI BUS - 01. Ready / Valid Handshake (1) | 2025.02.22 |