1. APB(Advanced Peripheral Bus)
- 저전력, 저가, 간소화된 버스
- 속도가 느린 peripheral을 연결하는데에 사용(TIM, UART 등)
위와 같이 비교적 복잡하지 않은 구조를 가지고 있다.(Master 1개, Slave 여러개)
- 7개 Input, 1개 Output
- Select 신호를 통해 Slave를 특정하고 해당 Slave 메모리 안의 데이터를 Address를 이용해 접근한다.
APB의 FSM은 위와 같은데
ACCESS state에서
PREADY == 1이고 더이상 전송이 없으면 IDLE state로 가고
PREADY == 0일때는 계속 IDLE state를 유지한다
2. AHB(Advanced High-performance Bus)
APB와는 다르게 Arbiter가 존재한다.
- Sel 신호를 통해 Slave를 특정함
- 하나의 Slave에 여러 Master가 접근하려고 하는 경우 Arbiter가 순서를 할당함.
3. AXI
[Bus의 장점]
- 인터페이스 간소화
- 각 블록을 개별적으로 연결하는 대신 하나의 버스를 통해 연결함으로써 배선의 복잡성을 줄일 수 있다.
- 확장성
- 새로운 블록(IP 등)을 추가하기 용이하다.
- 표준화
- 다양한 제조업체의 장치들이 호환되도록 보장하며 이는 하드웨어 개발과 유지보수를 간소화한다.
AXI Channel
- 서로 다른 Master가 서로 다른 Slave를 동시에 사용할 수 있다. (한 Master가 한 Slave 사용을 끝날때까지 대기하지 않아도 됨)
- Inter-Connector에 의해 Master가 접근하고자 하는 Slave에만 데이터를 보낸다.
- AHB-Lite에서는 모든 Slave에 데이터를 보내고 chip-enable 신호로 해당 Slave만 활성화하는 방식
- Write Address(AW), Write Data(W), Write Response(B) Channel : Write Transaction
- Write Responese(B-Channel) : OKAY(00), EXOKAY (01), SLVERR (10), DECERR (11)
- Read Address(AR), Read Data(R) Channel : Read Transaction
이 때문에 AXI protocol은 read와 write가 독립적으로 처리가 된다.
→ 이론상으로는 동시에 처리 가능
한 채널에 여러개의 데이터 라인이 있기 때문에 아래 그림과 같이 채널을 네모 칸으로 나타낸다.
Channel Handshake ★★
- AXI Protocol의 가장 중요한 특징
- Handshake : INFORMATION(data, address, response)를 전송할 떄 VALID, READY 신호를 주고 받는다.
- INFORMATION을 보낼 때 VALID Signal을 동시에 전송 → Destination에서 데이터 처리를 완료하면 READY Signal을 전송한다.
- VALID : Source → Destination
- READY : Destination → Source
Handshake process
VALID High == Information 데이터가 유효한 데이터라는 의미 & Triggering Signal
→ IDLE 상태에서 데이터를 처리하는 상태로 넘어간다.
# 1) VALID before READY Handshake
- Source 입장에서 데이터 처리 2클럭 소모
- Source → Destination ) Information data와 Valid Signal을 동시에 전송
- Destination에서 Valid Signal High를 인지하면 Information을 받아들이고 처리한다.
- 처리 완료 후 Destination → Source ) Ready Signal 전송
# 2) READY before VALID Handshake
- 아직 데이터 처리를 하지 않았지만 미리 Destination에서 READY High
- 데이터를 곧 전송할 것이 확실할 때
- 데이터를 조금 더 빠르게 처리하고 싶을 때 Ready Signal을 먼저 High로 유지시키고 대기
- Source 입장에서 데이터 처리 1클럭 소모
# 3) Valid with READY Handshake
- INFO 정보를 1클럭 내에 처리한다.
Handshake pairs
각 채널과 Handshake의 명칭을 위와 같이 사용한다.
Read Transaction
- “화살표 한개 = 필요하다”, “화살표 두개 = 꼭 필요하다” 의미
- RDATA를 전송하기 위해서는 ARVALID와 ARREADY가 asserted 되어야 한다.
Write Transaction
- Response는 WVALID와 WREADY가 asserted 된 후 전송된다.
4. AXI4-Lite
- Single Master
- No Burst
4-1. Write Transaction 구현
- response data 중 OKAY(00)만 구현
AWADDR과 AWVALID를 언제 트리거링할지는 개발자 마음이다.
→ 외부에서 들어오는 valid 신호 = 1이면 triggering 하도록 구현
(누군가는 Address > 0 이면 triggering으로 구현할 수 있다.)
구현
module AXI_Master (
// Global Signal
input logic ACLK,
input logic ARESETn, // Active Low
// AW Channel
output logic [31:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// W Channel
output logic [31:0] WDATA,
output logic WVALID,
input logic WREADY,
// B Channel
input logic [1:0] BRESP,
input logic BVALID,
output logic BREADY,
// external signal
input logic [31:0] aw_addr,
input logic [31:0] w_data,
input logic valid,
output logic ready
);
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg;
// AW Channel
enum bit {
AW_IDLE_S,
AW_VALID_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) aw_state <= AW_IDLE_S;
else aw_state <= aw_state_next;
end
// next, output logic
always_comb begin
aw_state_next = aw_state;
// aw_addr_reg = 0;
AWVALID = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (valid) begin // 외부 valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
aw_state_next = AW_VALID_S;
aw_addr_reg = aw_addr; // address latch
// Data temporary storage
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
AWADDR = aw_addr_reg;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg;
// W Channel
enum bit {
W_IDLE_S,
W_VALID_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) w_state <= W_IDLE_S;
else w_state <= w_state_next;
end
// next, output logic
always_comb begin
w_state_next = w_state;
// w_data_reg = 0;
WVALID = 1'b0;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (valid) begin // 외부 valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
w_state_next = W_VALID_S;
w_data_reg = w_data; // data latch
end
end
W_VALID_S: begin
WVALID = 1'b1;
WDATA = w_data_reg;
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg;
// B Channel
enum bit {
B_IDLE_S,
B_READY_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) b_state <= B_IDLE_S;
else b_state <= b_state_next;
end
// next, output logic
always_comb begin
b_state_next = b_state;
// b_resp_reg = 0;
BREADY = 1'b0;
ready = 1'b0;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
if (WVALID) begin // Write Data가 나가면 state 바꿈
b_state_next = B_READY_S;
end
end
B_READY_S: begin
BREADY = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
b_resp_reg = BRESP;
ready = 1'b1; // write transaction 끝나면 ready High 외부 출력
end
end
endcase
end
endmodule
module AXI_Slave_Memory (
// Global Signal
input logic ACLK,
input logic ARESETn, //
// AW Channel
input logic [31:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// W Channel
input logic [31:0] WDATA,
input logic WVALID,
output logic WREADY,
// B Channel
output logic [1:0] BRESP,
output logic BVALID,
input logic BREADY
);
logic [7:0] slave_mem[0:7];
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg;
// AW Channel
enum bit {
AW_IDLE_S,
AW_READY_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) aw_state <= AW_IDLE_S;
else aw_state <= aw_state_next;
end
// next, output logic
always_comb begin
aw_state_next = aw_state;
// aw_addr_reg = 0;
AWREADY = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWREADY = 1'b0;
if (AWVALID) begin // Master로부터 VALID 신호 들어 왔을 때 state 바꿈
aw_state_next = AW_READY_S;
// aw_addr_reg = AWADDR; // address latch
// Data temporary storage
end
end
AW_READY_S: begin // READY state에서 READY High signal
AWREADY = 1'b1;
aw_addr_reg = AWADDR;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg;
// W Channel
enum bit {
W_IDLE_S,
W_READY_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) w_state <= W_IDLE_S;
else w_state <= w_state_next;
end
// next, output logic
always_comb begin
w_state_next = w_state;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWREADY && WVALID) begin // address를 입력받아 처리하였고 && Master로부터 WVALID signal 받았을 때
w_state_next = W_READY_S;
w_data_reg = WDATA; // data latch
end
end
W_READY_S: begin // READY state에서 READY High signal
WREADY = 1'b1;
slave_mem[aw_addr_reg] = w_data_reg;
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg;
// B Channel
enum bit {
B_IDLE_S,
B_VALID_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) b_state <= B_IDLE_S;
else b_state <= b_state_next;
end
// next, output logic
always_comb begin
b_state_next = b_state;
b_resp_reg = 0;
BVALID = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) begin // Write Data가 처리 완료되었으면 response data 출력
b_state_next = B_VALID_S;
b_resp_reg = 2'b00; // OKAY response
end
end
B_VALID_S: begin
BVALID = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
BRESP = b_resp_reg;
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
end
end
endcase
end
endmodule
구현할 때 각 채널의 VALID, READY 신호를 언제 발생시킬지 신경써야 한다.
위 코드에서는 VALID, READY 신호의 타이밍을 아래와 같이 구현하였다.
[AW Channel]
- AWVALID : 외부의 트리거 신호가 High이면 High 출력
- Master → Slave
- AWREADY : AWVALID 신호가 High로 입력 되었을 때 High 출력
- Slave → Master
⇒ 외부 신호가 들어오면 Master에서는 VALID 신호와 ADDR info를 Slave로 출력한다.
Slave에서는 Master에서 출력한 VALID신호가 입력되면 ADDR을 저장하고 READY 신호를 출력한다.
[W Channel]
- AWVALID : 외부의 트리거 신호가 High이면 High 출력
- Master → Slave
- AW Channel과 W Channel이 동시에 진행된다.(외부로부터 주소와 데이터가 동시에 들어옴)
- AWREADY : AWVALID && WVALID
- Slave → Master
- write data가 메모리에 저장되기 위해서는 주소(AW)와 데이터(W)가 모두 입력된 상태여야 한다.
⇒ 외부 신호가 들어오면 Master에서는 VALID 신호와 DATA info를 Slave로 출력한다.
Slave에서는 ADDR과 DATA가 모두 있을 때 DATA를 메모리의 ADDR 위치에 저장하고 READY 신호를 출력한다.
[B Channel]
- BVALID : WVALID && WREADY
- Slave → Master
- W Channel에서 데이터 저장과정이 모두 끝난 후
- BREADY : WVALID
- Master → Slave
- Master에서 write data를 출력하는 동시에 BREADY 신호도 미리 High로 유지 (금방 RESPONSE info 데이터가 올 것이기에…)
⇒ BREADY는 미리 High로 유지시키고 BVALID는 Slave에서 모든 write 과정이 끝난 후에 신호를 출력한다.
시뮬레이션
[testbench code]
`timescale 1ns / 1ps
module tb_AXI_Memory ();
// Global Signal
logic ACLK;
logic ARESETn;
// AW Channel
logic [31:0] AWADDR;
logic AWVALID;
logic AWREADY;
// W Channel
logic [31:0] WDATA;
logic WVALID;
logic WREADY;
// B Channel
logic [ 1:0] BRESP;
logic BVALID;
logic BREADY;
// external signal
logic [31:0] aw_addr;
logic [31:0] w_data;
logic valid;
logic ready;
AXI_Master dut_master (.*);
AXI_Slave_Memory dut_slave (.*);
always #5 ACLK = ~ACLK;
initial begin
ACLK = 1'b0;
ARESETn = 1'b0;
#20 ARESETn = 1'b1;
@(posedge ACLK);
aw_addr = 32'd0;
w_data = 32'd100;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
@(posedge ACLK);
aw_addr = 32'd1;
w_data = 32'd111;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
@(posedge ACLK);
aw_addr = 32'd2;
w_data = 32'd222;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
end
endmodule
4-2. Write data strobes
- 데이터 중 유효한 바이트가 어디인지 알려주는 신호
- 64bit 데이터일 경우 WSTRB == 8bit, 32bit 데이터일 경우 WSTRB == 4bit,
구현
`timescale 1ns / 1ps
module AXI_Memory ();
endmodule
module AXI_Master (
// Global Signal
input logic ACLK,
input logic ARESETn, // Active Low
// AW Channel
output logic [31:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// W Channel
output logic [31:0] WDATA,
output logic WVALID,
output logic [ 3:0] WSTRB,
input logic WREADY,
// B Channel
input logic [1:0] BRESP,
input logic BVALID,
output logic BREADY,
// external signal
input logic [31:0] aw_addr,
input logic [31:0] w_data,
input logic valid,
input logic [ 3:0] w_strb,
output logic ready
);
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg, aw_addr_next;
// AW Channel
enum bit {
AW_IDLE_S,
AW_VALID_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWVALID = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (valid) begin // 외부 valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
aw_state_next = AW_VALID_S;
aw_addr_next = aw_addr; // address latch
// Data temporary storage
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
AWADDR = aw_addr_reg;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_next;
// W Channel
enum bit {
W_IDLE_S,
W_VALID_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
w_data_reg <= 0;
w_strb_reg <= 0;
end else begin
w_state <= w_state_next;
w_data_reg <= w_data_next;
w_strb_reg <= w_strb_next;
end
end
// next, output logic
always_comb begin
w_state_next = w_state;
w_data_next = w_data_reg;
w_strb_next = w_strb_reg;
WVALID = 1'b0;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (valid) begin // 외부 valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
w_state_next = W_VALID_S;
w_data_next = w_data; // data latch
w_strb_next = w_strb;
end
end
W_VALID_S: begin
WVALID = 1'b1;
WDATA = w_data_reg;
WSTRB = w_strb_reg;
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg, b_resp_next;
// B Channel
enum bit {
B_IDLE_S,
B_READY_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
b_resp_reg <= 0;
end else begin
b_state <= b_state_next;
b_resp_reg <= b_resp_next;
end
end
// next, output logic
always_comb begin
b_state_next = b_state;
b_resp_next = b_resp_reg;
BREADY = 1'b0;
ready = 1'b0;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
if (WVALID) begin // Write Data가 나가면 state 바꿈
b_state_next = B_READY_S;
end
end
B_READY_S: begin
BREADY = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
b_resp_next = BRESP;
ready = 1'b1; // write transaction 끝나면 ready High 외부 출력
end
end
endcase
end
endmodule
module AXI_Slave_Memory (
// Global Signal
input logic ACLK,
input logic ARESETn, //
// AW Channel
input logic [31:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// W Channel
input logic [31:0] WDATA,
input logic WVALID,
input logic [ 3:0] WSTRB,
output logic WREADY,
// B Channel
output logic [1:0] BRESP,
output logic BVALID,
input logic BREADY
);
logic [7:0] slave_mem[0:15];
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg, aw_addr_next;
// AW Channel
enum bit {
AW_IDLE_S,
AW_READY_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
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 // Master로부터 VALID 신호 들어 왔을 때 state 바꿈
aw_state_next = AW_READY_S;
// aw_addr_reg = AWADDR; // address latch
// Data temporary storage
end
end
AW_READY_S: begin // READY state에서 READY High signal
AWREADY = 1'b1;
aw_addr_next = AWADDR;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_next;
// W Channel
enum bit {
W_IDLE_S,
W_READY_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
w_data_reg <= 0;
w_strb_reg <= 0;
end else begin
w_state <= w_state_next;
w_data_reg <= w_data_next;
w_strb_reg <= w_strb_next;
end
end
// next, output logic
always_comb begin
w_state_next = w_state;
w_data_next = w_data_reg;
w_strb_next = w_strb_reg;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWREADY && WVALID) begin // address를 입력받아 처리하였고 && Master로부터 WVALID signal 받았을 때
// if (AWREADY) begin // address를 입력받아 처리하였고 && Master로부터 WVALID signal 받았을 때
w_state_next = W_READY_S;
w_data_next = WDATA; // data latch
w_strb_next = WSTRB;
end
end
W_READY_S: begin // READY state에서 READY High signal
WREADY = 1'b1;
if (w_strb_reg[0]) begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
end
if (w_strb_reg[1]) begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
end
if (w_strb_reg[2]) begin
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
if (w_strb_reg[3]) begin
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg, b_resp_next;
// B Channel
enum bit {
B_IDLE_S,
B_VALID_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
b_resp_reg <= 0;
end else begin
b_state <= b_state_next;
b_resp_reg <= b_resp_next;
end
end
// next, output logic
always_comb begin
b_state_next = b_state;
b_resp_next = b_resp_reg;
BVALID = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) begin // Write Data가 처리 완료되었으면 response data 출력
b_state_next = B_VALID_S;
b_resp_next = 2'b00; // OKAY response
end
end
B_VALID_S: begin
BVALID = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
BRESP = b_resp_reg;
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
end
end
endcase
end
endmodule
시뮬레이션
[testbench code]
`timescale 1ns / 1ps
module tb_AXI_Memory ();
// Global Signal
logic ACLK;
logic ARESETn;
// AW Channel
logic [31:0] AWADDR;
logic AWVALID;
logic AWREADY;
// W Channel
logic [31:0] WDATA;
logic WVALID;
logic [ 3:0] WSTRB;
logic WREADY;
// B Channel
logic [ 1:0] BRESP;
logic BVALID;
logic BREADY;
// external signal
logic [31:0] aw_addr;
logic [31:0] w_data;
logic valid;
logic [ 3:0] w_strb;
logic ready;
AXI_Master dut_master (.*);
AXI_Slave_Memory dut_slave (.*);
always #5 ACLK = ~ACLK;
initial begin
ACLK = 1'b0;
ARESETn = 1'b0;
#20 ARESETn = 1'b1;
@(posedge ACLK);
aw_addr = 32'd0;
w_data = 32'h12345678;
w_strb = 4'b0001;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
@(posedge ACLK);
aw_addr = 32'd1;
w_data = 32'h12345678;
w_strb = 4'b0011;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
@(posedge ACLK);
aw_addr = 32'd3;
w_data = 32'h12345678;
w_strb = 4'b1101;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
@(posedge ACLK);
aw_addr = 32'd7;
w_data = 32'h12345678;
w_strb = 4'b1111;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready); // wait for ready signal
end
endmodule
위와 같이 유효한 데이터를 특정하여 메모리에 저장한다.
4-3. Read Transaction 구현
RLAST Signal : BURST 기능에서 사용하는 신호
구현
`timescale 1ns / 1ps
module AXI_Master (
// Global Signal
input logic ACLK,
input logic ARESETn, // Active Low
////////////// READ Transaction ///////////
// AR Channel
output logic [31:0] ARADDR,
output logic ARVALID,
input logic ARREADY,
// R Channel
input logic [31:0] RDATA,
input logic RVALID,
output logic RREADY,
////////////// WRITE Transaction ///////////
// AW Channel
output logic [31:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// W Channel
output logic [31:0] WDATA,
output logic WVALID,
output logic [ 3:0] WSTRB,
input logic WREADY,
// B Channel
input logic [1:0] BRESP,
input logic BVALID,
output logic BREADY,
// external signal
// Read Transaction
input logic [31:0] ar_addr,
input logic read_valid,
output logic [31:0] r_data,
output logic read_Ready,
// write Transaction
input logic [31:0] aw_addr,
input logic [31:0] w_data,
input logic write_valid,
input logic [ 3:0] w_strb,
output logic write_Ready
);
/////////////////// AR Channel///////////////////
logic [31:0] ar_addr_reg, ar_addr_next;
enum bit {
AR_IDLE_S,
AR_VALID_S
}
ar_state, ar_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
always_comb begin
ar_state_next = ar_state;
ar_addr_next = ar_addr_reg;
ARVALID = 1'b0;
case (ar_state)
AR_IDLE_S: begin
ARVALID = 1'b0;
if (read_valid) begin // 외부 write_valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
ar_state_next = AR_VALID_S;
ar_addr_next = ar_addr; // address info 들어오자마자 바로 저장
// Data temporary storage
end
end
AR_VALID_S: begin
ARVALID = 1'b1; // VALID와 ADDR 동시에 전송
ARADDR = ar_addr_reg;
if (ARVALID && ARREADY) begin
ar_state_next = AR_IDLE_S;
end
end
endcase
end
/////////////////// R Channel///////////////////
// logic [31:0] r_data_reg, r_data_next;
enum bit {
R_IDLE_S,
R_READY_S
}
r_state, r_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
always_comb begin
r_state_next = r_state;
// r_data_next = r_data_reg;
RREADY = 1'b0;
read_Ready = 1'b0;
case (r_state)
R_IDLE_S: begin
RREADY = 1'b0;
if (ARVALID) begin // read address 보냈을 때 바로 받을 준비
r_state_next = R_READY_S;
// r_data_next = r_data; // data latch
end
end
R_READY_S: begin
RREADY = 1'b1;
r_data = RDATA;
if (RVALID && RREADY) begin
r_state_next = R_IDLE_S;
r_data = RDATA;
read_Ready = 1'b1;
end
end
endcase
end
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg, aw_addr_next;
// AW Channel
enum bit {
AW_IDLE_S,
AW_VALID_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
always_comb begin
aw_state_next = aw_state;
aw_addr_next = aw_addr_reg;
AWVALID = 1'b0;
case (aw_state)
AW_IDLE_S: begin
AWVALID = 1'b0;
if (write_valid) begin // 외부 write_valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
aw_state_next = AW_VALID_S;
aw_addr_next = aw_addr; // address latch
// Data temporary storage
end
end
AW_VALID_S: begin
AWVALID = 1'b1;
AWADDR = aw_addr_reg;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_next;
// W Channel
enum bit {
W_IDLE_S,
W_VALID_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
w_data_reg <= 0;
w_strb_reg <= 0;
end else begin
w_state <= w_state_next;
w_data_reg <= w_data_next;
w_strb_reg <= w_strb_next;
end
end
// next, output logic
always_comb begin
w_state_next = w_state;
w_data_next = w_data_reg;
w_strb_next = w_strb_reg;
WVALID = 1'b0;
case (w_state)
W_IDLE_S: begin
WVALID = 1'b0;
if (write_valid) begin // 외부 write_valid 신호 들어 왔을 때 state 바꾸고 외부에서 들어오는 info aw_addr 값을 래치에 저장
w_state_next = W_VALID_S;
w_data_next = w_data; // data latch
w_strb_next = w_strb;
end
end
W_VALID_S: begin
WVALID = 1'b1;
WDATA = w_data_reg;
WSTRB = w_strb_reg;
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg, b_resp_next;
// B Channel
enum bit {
B_IDLE_S,
B_READY_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
b_resp_reg <= 0;
end else begin
b_state <= b_state_next;
b_resp_reg <= b_resp_next;
end
end
// next, output logic
always_comb begin
b_state_next = b_state;
b_resp_next = b_resp_reg;
BREADY = 1'b0;
write_Ready = 1'b0;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
if (WVALID) begin // Write Data가 나가면 state 바꿈
b_state_next = B_READY_S;
end
end
B_READY_S: begin
BREADY = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
b_resp_next = BRESP;
write_Ready = 1'b1; // write transaction 끝나면 write_Ready High 외부 출력
end
end
endcase
end
endmodule
//////////////////////////////////////////////////////////////////////////////////////////////
module AXI_Slave_Memory (
// Global Signal
input logic ACLK,
input logic ARESETn,
////////////// READ Transaction ///////////
// AR Channel
input logic [31:0] ARADDR,
input logic ARVALID,
output logic ARREADY,
// R Channel
output logic [31:0] RDATA,
output logic RVALID,
input logic RREADY,
////////////// WRITE Transaction ///////////
// AW Channel
input logic [31:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// W Channel
input logic [31:0] WDATA,
input logic WVALID,
input logic [ 3:0] WSTRB,
output logic WREADY,
// B Channel
output logic [1:0] BRESP,
output logic BVALID,
input logic BREADY
);
logic [7:0] slave_mem[0:15];
/////////////////// AR Channel///////////////////
logic [31:0] ar_addr_reg, ar_addr_next;
enum bit {
AR_IDLE_S,
AR_READY_S
}
ar_state, ar_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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;
if (ARVALID && ARREADY) begin
ar_state_next = AR_IDLE_S;
end
end
endcase
end
/////////////////// R Channel///////////////////
logic [31:0] r_data_reg, r_data_next;
enum bit {
R_IDLE_S,
R_VALID_S
}
r_state, r_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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;
case (r_state)
R_IDLE_S: begin
RVALID = 1'b0;
if (ARREADY && ARVALID) begin // Address를 받아서 처리하고 난 뒤
r_state_next = R_VALID_S;
r_data_next[7:0] = slave_mem[ar_addr_reg+0];
r_data_next[15:8] = slave_mem[ar_addr_reg+1];
r_data_next[23:16] = slave_mem[ar_addr_reg+2];
r_data_next[31:24] = slave_mem[ar_addr_reg+3];
// 4byte 읽어옴
end
end
R_VALID_S: begin
RVALID = 1'b1;
RDATA = r_data_reg;
if (RVALID && RREADY) begin
r_state_next = R_IDLE_S;
end
end
endcase
end
/////////////////// AW Channel///////////////////
logic [31:0] aw_addr_reg, aw_addr_next;
// AW Channel
enum bit {
AW_IDLE_S,
AW_READY_S
}
aw_state, aw_state_next;
always_ff @(posedge ACLK, negedge ARESETn) 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
// next, output logic
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 // Master로부터 VALID 신호 들어 왔을 때 state 바꿈
aw_state_next = AW_READY_S;
// aw_addr_reg = AWADDR; // address latch
// Data temporary storage
end
end
AW_READY_S: begin // READY state에서 READY High signal
AWREADY = 1'b1;
aw_addr_next = AWADDR;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
/////////////////// W Channel///////////////////
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_next;
// W Channel
enum bit {
W_IDLE_S,
W_READY_S
}
w_state, w_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
w_state <= W_IDLE_S;
w_data_reg <= 0;
w_strb_reg <= 0;
end else begin
w_state <= w_state_next;
w_data_reg <= w_data_next;
w_strb_reg <= w_strb_next;
end
end
// next, output logic
always_comb begin
w_state_next = w_state;
w_data_next = w_data_reg;
w_strb_next = w_strb_reg;
WREADY = 1'b0;
case (w_state)
W_IDLE_S: begin
WREADY = 1'b0;
if (AWREADY && WVALID) begin // address를 입력받아 처리하였고 && Master로부터 WVALID signal 받았을 때
// if (AWREADY) begin
w_state_next = W_READY_S;
w_data_next = WDATA; // data latch
w_strb_next = WSTRB;
end
end
W_READY_S: begin // READY state에서 READY High signal
WREADY = 1'b1;
if (w_strb_reg[0]) begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
end
if (w_strb_reg[1]) begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
end
if (w_strb_reg[2]) begin
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
if (w_strb_reg[3]) begin
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
/////////////////// B Channel///////////////////
logic [31:0] b_resp_reg, b_resp_next;
// B Channel
enum bit {
B_IDLE_S,
B_VALID_S
}
b_state, b_state_next;
always_ff @(posedge ACLK, negedge ARESETn) begin
if (!ARESETn) begin
b_state <= B_IDLE_S;
b_resp_reg <= 0;
end else begin
b_state <= b_state_next;
b_resp_reg <= b_resp_next;
end
end
// next, output logic
always_comb begin
b_state_next = b_state;
b_resp_next = b_resp_reg;
BVALID = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) begin // Write Data가 처리 완료되었으면 response data 출력
b_state_next = B_VALID_S;
b_resp_next = 2'b00; // OKAY response
end
end
B_VALID_S: begin
BVALID = 1'b1; // READY 먼저 High 유지하다가 VALID 신호 들어오면 response data 처리
BRESP = b_resp_reg;
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
end
end
endcase
end
endmodule
[AR Channel]
- ARVALID : 외부의 트리거 신호가 High이면 High 출력
- Master → Slave
- ARREADY : ARVALID 신호가 High로 입력 되었을 때 High 출력
- Slave → Master
⇒ 외부 신호가 들어오면 Master에서는 VALID 신호와 ADDR info를 Slave로 출력한다.
Slave에서는 Master에서 출력한 VALID신호가 입력되면 ADDR을 저장하고 READY 신호를 출력한다.
[R Channel]
- RVALID : ARREADY && ARVALID
- Slave → Master
- AR Channel 과정이 모두 끝났을 때(Handshake 발생했을 때) 읽은 데이터를 출력
- RREADY : ARVALID
- Master → Slave
- Master가 ADDR를 전송하자마자 ARREADY를 미리 High로 유지하여 받을 준비
시뮬레이션
[testbench code]
`timescale 1ns / 1ps
module tb_AXI_Memory ();
// Global Signal
logic ACLK;
logic ARESETn;
// AR Channel
logic [31:0] ARADDR;
logic ARVALID;
logic ARREADY;
// R Channel
logic [31:0] RDATA;
logic RVALID;
logic RREADY;
// AW Channel
logic [31:0] AWADDR;
logic AWVALID;
logic AWREADY;
// W Channel
logic [31:0] WDATA;
logic WVALID;
logic [ 3:0] WSTRB;
logic WREADY;
// B Channel
logic [ 1:0] BRESP;
logic BVALID;
logic BREADY;
// external signal
logic [31:0] ar_addr;
logic read_valid;
logic [31:0] r_data;
logic read_Ready;
logic [31:0] aw_addr;
logic [31:0] w_data;
logic write_valid;
logic [ 3:0] w_strb;
logic write_Ready;
AXI_Master dut_master (.*);
AXI_Slave_Memory dut_slave (.*);
always #5 ACLK = ~ACLK;
initial begin
ACLK = 1'b0;
ARESETn = 1'b0;
#20 ARESETn = 1'b1;
@(posedge ACLK);
aw_addr = 32'd0;
w_data = 32'h12345678;
w_strb = 4'b0001;
write_valid = 1'b1;
@(posedge ACLK);
write_valid = 1'b0;
@(posedge write_Ready); // wait for write_Ready signal
@(posedge ACLK);
aw_addr = 32'd1;
w_data = 32'h12345678;
w_strb = 4'b0011;
write_valid = 1'b1;
@(posedge ACLK);
write_valid = 1'b0;
@(posedge write_Ready); // wait for write_Ready signal
@(posedge ACLK);
aw_addr = 32'd3;
w_data = 32'h12345678;
w_strb = 4'b1101;
write_valid = 1'b1;
@(posedge ACLK);
write_valid = 1'b0;
@(posedge write_Ready); // wait for write_Ready signal
@(posedge ACLK);
aw_addr = 32'd7;
w_data = 32'h12345678;
w_strb = 4'b1111;
write_valid = 1'b1;
@(posedge ACLK);
write_valid = 1'b0;
@(posedge write_Ready); // wait for write_Ready signal
@(posedge ACLK);
ar_addr = 32'd0;
read_valid = 1'b1;
@(posedge ACLK);
read_valid = 1'b0;
@(posedge read_Ready);
@(posedge ACLK);
ar_addr = 32'd7;
read_valid = 1'b1;
@(posedge ACLK);
read_valid = 1'b0;
@(posedge read_Ready);
end
endmodule
Made By Minseok KIM
'VerilogHDL > Study' 카테고리의 다른 글
[VerilogHDL] RISC-V 기본, RV32I R-Type, IL-Type (0) | 2024.06.18 |
---|---|
[VerilogHDL] CPU 기본 구조, 메모리, Counter 설계(Control Unit, Data Path) (1) | 2024.06.03 |
[VerilogHDL] FIFO, UART&FIFO (0) | 2024.05.30 |
[VerilogHDL] Verification(32bit register, BRAM) (0) | 2024.05.23 |
[VerilogHDL] System Verilog 기초 (0) | 2024.05.22 |
Let's Be Happy!
도움이 되었으면 좋겠어요 :)